﻿<?xml version="1.0" encoding="utf-8"?>
<Templates format="2" protocolVersion="80">
  <Template revision="1.0" id="d6b4053d-11b1-4374-a281-d84b7255a233">
    <SuggestedCCUParameters />
    <RevisionHistory>
      <Revision id="1.0">Initial version</Revision>
    </RevisionHistory>
    <Name>Shelly 2PM Gen3</Name>
    <CcuModel>PacketParserCCU</CcuModel>
    <Producer>Shelly</Producer>
    <Model>Shelly 2PM Gen3</Model>
    <Description></Description>
    <ImportParameters>
        <Parameter>
            <Name>${ipAddress}</Name>	
            <Value>192.168.0.1</Value>
            <Id>IpAddress</Id>
        </Parameter>
    </ImportParameters>       
    <Module>
      <Name>Shelly 2PM Gen3</Name>
      <Id>-1</Id>
      <Model>PacketParserModuleHttp</Model>
      <DeviceProperties>
        <ReadScriptPacketParser>VAR response := SENDHTTPREQUEST("/rpc/Sys.GetStatus");
IF response.IsSuccess
    Sys.GetStatus := response.Content;
END

response := SENDHTTPREQUEST("/rpc/Sys.GetConfig");
IF response.IsSuccess
    Sys.GetConfig := response.Content;
END

response := SENDHTTPREQUEST("/rpc/WiFi.GetStatus");
IF response.IsSuccess
    WiFi.GetStatus := response.Content;
END

if ! ISNULL(parsejson(Sys.GetStatus, "available_updates.stable", true))
    addwarning("New firmware update is available");
end

if parsejson(Sys.GetStatus, "restart_required", true)
    addwarning("Restart required");
end
</ReadScriptPacketParser>
        <InternalPollInterval>3000</InternalPollInterval>
        <CustomVariables>[{"Name":"Sys.GetStatus","ValueTypeId":2,"NumericValue":0,"StringValue":"0"},{"Name":"Sys.GetConfig","ValueTypeId":2,"NumericValue":0,"StringValue":"0"},{"Name":"WiFi.GetStatus","ValueTypeId":2,"NumericValue":0,"StringValue":"0"}]</CustomVariables>
        <ServiceAttributesScriptsPacketParser>[{"AttributeDefinition":{"Name":"Wifi"},"ReadFormula":"PARSEJSON(WiFi.GetStatus, \u0022ssid\u0022)"},{"AttributeDefinition":{"Name":"${ipAddress}"},"ReadFormula":"PARSEJSON(WiFi.GetStatus, \u0022sta_ip\u0022)"},{"AttributeDefinition":{"Name":"${xml_signal_strength} [dB]"},"ReadFormula":"PARSEJSON(WiFi.GetStatus, \u0022rssi\u0022)"},{"AttributeDefinition":{"Name":"MAC"},"ReadFormula":"PARSEJSON(Sys.GetStatus, \u0022mac\u0022)"},{"AttributeDefinition":{"Name":"Profile"},"ReadFormula":"PARSEJSON(Sys.GetConfig, \u0022$.device.profile\u0022);"}]</ServiceAttributesScriptsPacketParser>
        <ServiceActionsScriptsPacketParser>[{"ActionDefinition":{"Name":"${xml_update_fw}","NumericParameters":[],"BoolParameters":[],"EnumParameters":[]},"Script":"VAR response := SENDHTTPREQUEST(\u0022/rpc/Shelly.Update?stage=\\\u0022stable\\\u0022\u0022);\r\nIF response.IsSuccess\r\n    return(\u0022OK\u0022);\r\nelse\r\n    return(\u0022Error\u0022);\r\nEND\r\n\r\n"},{"ActionDefinition":{"Name":"${restart}","NumericParameters":[],"BoolParameters":[],"EnumParameters":[]},"Script":"VAR response := SENDHTTPREQUEST(\u0022/rpc/Shelly.Reboot\u0022);\r\nIF response.IsSuccess\r\n    return(\u0022OK\u0022);\r\nelse\r\n    return(\u0022Error\u0022);\r\nEND"},{"ActionDefinition":{"Name":"Set Profile","NumericParameters":[],"BoolParameters":[],"EnumParameters":[{"FriendlyName":"Profile","Abbreviation":"prof","Values":[{"Item1":0,"Item2":"cover"},{"Item1":1,"Item2":"switch"}]}]},"Script":"VAR response := SENDHTTPREQUEST(\u0022/rpc/Shelly.SetProfile?name=\\\u0022\u0022 \u002B \r\nswitch(prof,\r\n0, \u0022cover\u0022,\r\n1, \u0022switch\u0022,\r\n\u0022\u0022)\r\n\u002B \u0022\\\u0022\u0022);\r\nIF response.IsSuccess\r\n    return(\u0022OK\u0022);\r\nelse\r\n    return(\u0022Error\u0022);\r\nEND\r\n"}]</ServiceActionsScriptsPacketParser>
        <IpAddress>$[IpAddress]</IpAddress>
        <Port>80</Port>
        <UseHttps>False</UseHttps>
      </DeviceProperties>
      <Devices>
        <Device>
          <Name>${digital_output} (1)</Name>
          <Model>PacketParserSwitch</Model>
          <Id>-2</Id>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <ReadScriptPacketParser>if PARSEJSON(Sys.GetConfig, "$.device.profile") != "switch"
	adderror("Profile is not set to switch (" + PARSEJSON(Sys.GetConfig, "$.device.profile") + ")");
end

VAR response := SENDHTTPREQUEST("/rpc/Switch.GetStatus?id=" + ID);
IF response.IsSuccess
    Switch.GetStatus := response.Content;  
END

response := SENDHTTPREQUEST("/rpc/Switch.GetConfig?id=" + ID);
IF response.IsSuccess
    Switch.GetConfig := response.Content;
END

if ! ISNULL(parsejson(Switch.GetStatus, "errors", true))
    adderror("Error: " + parsejson(Switch.GetStatus, "errors"));
end

</ReadScriptPacketParser>
            <InternalPollInterval>2500</InternalPollInterval>
            <CustomVariables>[{"Name":"ID","ValueTypeId":1,"NumericValue":0},{"Name":"Switch.GetStatus","ValueTypeId":2,"NumericValue":0,"StringValue":""},{"Name":"Switch.GetConfig","ValueTypeId":2,"NumericValue":0,"StringValue":""}]</CustomVariables>
            <ServiceAttributesScriptsPacketParser>[{"AttributeDefinition":{"Name":"${device_variable_realtemperature} [\u00B0C]"},"ReadFormula":"parsejson(Switch.GetStatus, \u0022$.temperature.tC\u0022, true)"},{"AttributeDefinition":{"Name":"${device_variable_electricityconsumption} [Wh]"},"ReadFormula":"parsejson(Switch.GetStatus, \u0022$.aenergy.total\u0022, true)"},{"AttributeDefinition":{"Name":"${device_variable_electricitydemand} [W]"},"ReadFormula":"parsejson(Switch.GetStatus, \u0022$.apower\u0022, true)"},{"AttributeDefinition":{"Name":"${valueLogType_ElectricVoltage} [V]"},"ReadFormula":"parsejson(Switch.GetStatus, \u0022$.voltage\u0022, true)"},{"AttributeDefinition":{"Name":"${valueLogType_ElectricCurrent} [A]"},"ReadFormula":"parsejson(Switch.GetStatus, \u0022$.current\u0022, true)"},{"AttributeDefinition":{"Name":"${frequency} [Hz]"},"ReadFormula":"parsejson(Switch.GetStatus, \u0022$.freq\u0022, true)"},{"AttributeDefinition":{"Name":"Power Factor"},"ReadFormula":"parsejson(Switch.GetStatus, \u0022$.pf\u0022, true)"}]</ServiceAttributesScriptsPacketParser>
            <ServiceActionsScriptsPacketParser></ServiceActionsScriptsPacketParser>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <NegateState>False</NegateState>
            <OnStateIconId>60</OnStateIconId>
            <OffStateIconId>61</OffStateIconId>
            <OnStateName>${general_on}</OnStateName>
            <OffStateName>${general_off}</OffStateName>
            <ReadSwitchStateScriptPacketParser>VAR response := SENDHTTPREQUEST("/rpc/Switch.GetStatus?id=" + ID);
IF response.IsSuccess
    Switch.GetStatus := response.Content;  
END
return(PARSEJSON(Switch.GetStatus, "$.output", true));
</ReadSwitchStateScriptPacketParser>
            <WriteSwitchStateScriptPacketParser>VAR response := SENDHTTPREQUEST("rpc/Switch.Set?id=" + ID + "&amp;on=" + if(St,"true","false"));
IF response.IsSuccess = false
  ADDERROR(response.StatusCode);
END
</WriteSwitchStateScriptPacketParser>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>${digital_output} (2)</Name>
          <Model>PacketParserSwitch</Model>
          <Id>-3</Id>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <ReadScriptPacketParser>if PARSEJSON(Sys.GetConfig, "$.device.profile") != "switch"
	adderror("Profile is not set to switch (" + PARSEJSON(Sys.GetConfig, "$.device.profile") + ")");
end

VAR response := SENDHTTPREQUEST("/rpc/Switch.GetStatus?id=" + ID);
IF response.IsSuccess
    Switch.GetStatus := response.Content;  
END

response := SENDHTTPREQUEST("/rpc/Switch.GetConfig?id=" + ID);
IF response.IsSuccess
    Switch.GetConfig := response.Content;
END

if ! ISNULL(parsejson(Switch.GetStatus, "errors", true))
    adderror("Error: " + parsejson(Switch.GetStatus, "errors"));
end

</ReadScriptPacketParser>
            <InternalPollInterval>2500</InternalPollInterval>
            <CustomVariables>[{"Name":"ID","ValueTypeId":1,"NumericValue":1},{"Name":"Switch.GetStatus","ValueTypeId":2,"NumericValue":0,"StringValue":""},{"Name":"Switch.GetConfig","ValueTypeId":2,"NumericValue":0,"StringValue":""}]</CustomVariables>
            <ServiceAttributesScriptsPacketParser>[{"AttributeDefinition":{"Name":"${device_variable_realtemperature} [\u00B0C]"},"ReadFormula":"parsejson(Switch.GetStatus, \u0022$.temperature.tC\u0022, true)"},{"AttributeDefinition":{"Name":"${device_variable_electricityconsumption} [Wh]"},"ReadFormula":"parsejson(Switch.GetStatus, \u0022$.aenergy.total\u0022, true)"},{"AttributeDefinition":{"Name":"${device_variable_electricitydemand} [W]"},"ReadFormula":"parsejson(Switch.GetStatus, \u0022$.apower\u0022, true)"},{"AttributeDefinition":{"Name":"${valueLogType_ElectricVoltage} [V]"},"ReadFormula":"parsejson(Switch.GetStatus, \u0022$.voltage\u0022, true)"},{"AttributeDefinition":{"Name":"${valueLogType_ElectricCurrent} [A]"},"ReadFormula":"parsejson(Switch.GetStatus, \u0022$.current\u0022, true)"},{"AttributeDefinition":{"Name":"${frequency} [Hz]"},"ReadFormula":"parsejson(Switch.GetStatus, \u0022$.freq\u0022, true)"},{"AttributeDefinition":{"Name":"Power Factor"},"ReadFormula":"parsejson(Switch.GetStatus, \u0022$.pf\u0022, true)"}]</ServiceAttributesScriptsPacketParser>
            <ServiceActionsScriptsPacketParser></ServiceActionsScriptsPacketParser>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <NegateState>False</NegateState>
            <OnStateIconId>60</OnStateIconId>
            <OffStateIconId>61</OffStateIconId>
            <OnStateName>${general_on}</OnStateName>
            <OffStateName>${general_off}</OffStateName>
            <ReadSwitchStateScriptPacketParser>VAR response := SENDHTTPREQUEST("/rpc/Switch.GetStatus?id=" + ID);
IF response.IsSuccess
    Switch.GetStatus := response.Content;  
END
return(PARSEJSON(Switch.GetStatus, "$.output", true));</ReadSwitchStateScriptPacketParser>
            <WriteSwitchStateScriptPacketParser>VAR response := SENDHTTPREQUEST("rpc/Switch.Set?id=" + ID + "&amp;on=" + if(St,"true","false"));
IF response.IsSuccess = false
  ADDERROR(response.StatusCode);
END
</WriteSwitchStateScriptPacketParser>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>${slide}</Name>
          <Model>PacketParserSlide</Model>
          <Id>-4</Id>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <ReadScriptPacketParser>if PARSEJSON(Sys.GetConfig, "$.device.profile") != "cover"
	adderror("Profile is not set to cover (" + PARSEJSON(Sys.GetConfig, "$.device.profile") + ")");
end

VAR response := SENDHTTPREQUEST("/rpc/Cover.GetStatus?id=" + ID);
IF response.IsSuccess
    Cover.GetStatus := response.Content;  
END

response := SENDHTTPREQUEST("/rpc/Cover.GetConfig?id=" + ID);
IF response.IsSuccess
    Cover.GetConfig := response.Content;
END

if ! parsejson(Cover.GetStatus, "$.pos_control", true)
    #adderror("${presence_calibration_required}");
    adderror("Calibration is required");
end

if ! ISNULL(parsejson(Cover.GetStatus, "errors", true))
    adderror("Error: " + parsejson(Cover.GetStatus, "errors"));
end

</ReadScriptPacketParser>
            <InternalPollInterval>2500</InternalPollInterval>
            <CustomVariables>[{"Name":"ID","ValueTypeId":1,"NumericValue":0},{"Name":"Cover.GetConfig","ValueTypeId":2,"NumericValue":0,"StringValue":""},{"Name":"Cover.GetStatus","ValueTypeId":2,"NumericValue":0,"StringValue":""}]</CustomVariables>
            <ServiceAttributesScriptsPacketParser>[{"AttributeDefinition":{"Name":"${device_variable_realtemperature} [\u00B0C]"},"ReadFormula":"parsejson(Cover.GetStatus, \u0022$.temperature.tC\u0022, true)"},{"AttributeDefinition":{"Name":"${device_variable_electricityconsumption} [Wh]"},"ReadFormula":"parsejson(Cover.GetStatus, \u0022$.aenergy.total\u0022, true)"},{"AttributeDefinition":{"Name":"${device_variable_electricitydemand} [W]"},"ReadFormula":"parsejson(Cover.GetStatus, \u0022$.apower\u0022, true)"},{"AttributeDefinition":{"Name":"${valueLogType_ElectricVoltage} [V]"},"ReadFormula":"parsejson(Cover.GetStatus, \u0022$.voltage\u0022, true)"},{"AttributeDefinition":{"Name":"${valueLogType_ElectricCurrent} [A]"},"ReadFormula":"parsejson(Cover.GetStatus, \u0022$.current\u0022, true)"}]</ServiceAttributesScriptsPacketParser>
            <ServiceActionsScriptsPacketParser>[{"ActionDefinition":{"Name":"${start_automatic_calibration}","NumericParameters":[],"BoolParameters":[],"EnumParameters":[]},"Script":"VAR response := SENDHTTPREQUEST(\u0022/rpc/Cover.Calibrate?id=\u0022 \u002B ID);\r\nIF response.IsSuccess\r\n    return(\u0022OK\u0022);\r\nelse\r\n    return(\u0022Error\u0022);\r\nEND"}]</ServiceActionsScriptsPacketParser>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <ReadBlindsLevelScriptPacketParser>var pos := PARSEJSON(Cover.GetStatus, "$.current_pos", true);
if ! isnull(PARSEJSON(Cover.GetStatus, "$.target_pos", true))
	pos := PARSEJSON(Cover.GetStatus, "$.target_pos", true);
end

return(linear(pos,0,1,100,0));</ReadBlindsLevelScriptPacketParser>
            <WriteBlindsLevelScriptPacketParser>VAR response := SENDHTTPREQUEST("rpc/Cover.GoToPosition?id=" + ID + "&amp;pos=" + round(linear(Bl,0,100,1,0)));
IF response.IsSuccess = false
  ADDERROR(response.StatusCode);
END

response := SENDHTTPREQUEST("/rpc/Cover.GetStatus?id=" + ID);
IF response.IsSuccess
    Cover.GetStatus := response.Content;  
END</WriteBlindsLevelScriptPacketParser>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>${bus_module_blind}</Name>
          <Model>PacketParserBlind</Model>
          <Id>-5</Id>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <ReadScriptPacketParser>if PARSEJSON(Sys.GetConfig, "$.device.profile") != "cover"
	adderror("Profile is not set to cover (" + PARSEJSON(Sys.GetConfig, "$.device.profile") + ")");
end

VAR response := SENDHTTPREQUEST("/rpc/Cover.GetStatus?id=" + ID);
IF response.IsSuccess
    Cover.GetStatus := response.Content;  
END

response := SENDHTTPREQUEST("/rpc/Cover.GetConfig?id=" + ID);
IF response.IsSuccess
    Cover.GetConfig := response.Content;
END

if ! parsejson(Cover.GetStatus, "$.pos_control", true)
    #adderror("${presence_calibration_required}");
    adderror("Calibration is required");
end

if ! ISNULL(parsejson(Cover.GetStatus, "errors", true))
    adderror("Error: " + parsejson(Cover.GetStatus, "errors"));
end

</ReadScriptPacketParser>
            <InternalPollInterval>2500</InternalPollInterval>
            <CustomVariables>[{"Name":"ID","ValueTypeId":1,"NumericValue":0},{"Name":"Cover.GetConfig","ValueTypeId":2,"NumericValue":0,"StringValue":""},{"Name":"Cover.GetStatus","ValueTypeId":2,"NumericValue":0,"StringValue":""},{"Name":"BlSend","ValueTypeId":1,"NumericValue":0},{"Name":"BsSend","ValueTypeId":1,"NumericValue":0,"StringValue":"0"},{"Name":"DeltaPos","ValueTypeId":1,"NumericValue":0}]</CustomVariables>
            <ServiceAttributesScriptsPacketParser>[{"AttributeDefinition":{"Name":"${device_variable_realtemperature} [\u00B0C]"},"ReadFormula":"parsejson(Cover.GetStatus, \u0022$.temperature.tC\u0022, true)"},{"AttributeDefinition":{"Name":"${device_variable_electricityconsumption} [Wh]"},"ReadFormula":"parsejson(Cover.GetStatus, \u0022$.aenergy.total\u0022, true)"},{"AttributeDefinition":{"Name":"${device_variable_electricitydemand} [W]"},"ReadFormula":"parsejson(Cover.GetStatus, \u0022$.apower\u0022, true)"},{"AttributeDefinition":{"Name":"${valueLogType_ElectricVoltage} [V]"},"ReadFormula":"parsejson(Cover.GetStatus, \u0022$.voltage\u0022, true)"},{"AttributeDefinition":{"Name":"${valueLogType_ElectricCurrent} [A]"},"ReadFormula":"parsejson(Cover.GetStatus, \u0022$.current\u0022, true)"},{"AttributeDefinition":{"Name":"Slat"},"ReadFormula":"if(parsejson(Cover.GetConfig, \u0022$.slat.enable\u0022, true), \u0022Enabled\u0022, \u0022Disabled\u0022)"},{"AttributeDefinition":{"Name":"Slat open time [s]"},"ReadFormula":"parsejson(Cover.GetConfig, \u0022$.slat.open_time \u0022, true)"},{"AttributeDefinition":{"Name":"Slat close time [s]"},"ReadFormula":"parsejson(Cover.GetConfig, \u0022$.slat.open_time \u0022, true)"}]</ServiceAttributesScriptsPacketParser>
            <ServiceActionsScriptsPacketParser>[{"ActionDefinition":{"Name":"${start_automatic_calibration}","NumericParameters":[],"BoolParameters":[],"EnumParameters":[]},"Script":"VAR response := SENDHTTPREQUEST(\u0022/rpc/Cover.Calibrate?id=\u0022 \u002B ID);\r\nIF response.IsSuccess\r\n    return(\u0022OK\u0022);\r\nelse\r\n    return(\u0022Error\u0022);\r\nEND"},{"ActionDefinition":{"Name":"Set Slat","NumericParameters":[{"FriendlyName":"Open time [s]","Abbreviation":"ot","IsFloat":true,"MinValue":0.1,"MaxValue":5},{"FriendlyName":"Close time [s]","Abbreviation":"ct","IsFloat":true,"MinValue":0.1,"MaxValue":5}],"BoolParameters":[],"EnumParameters":[]},"Script":"VAR response := SENDHTTPREQUEST(\u0022/rpc/Cover.SetConfig?id=\u0022 \u002B ID \u002B \r\n\t\u0022\u0026config={\\\u0022slat\\\u0022:{\\\u0022enable\\\u0022:true, \\\u0022open_time\\\u0022: \u0022 \u002B ot \u002B\u0022, \\\u0022close_time\\\u0022: \u0022 \u002B ct \u002B\u0022}}\u0022);\r\nIF response.IsSuccess\r\n    return(\u0022OK\u0022);\r\nelse\r\n    return(\u0022Error\u0022);\r\nEND"}]</ServiceActionsScriptsPacketParser>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <ReadBlindLevelScriptPacketParser>var pos := PARSEJSON(Cover.GetStatus, "$.current_pos", true);
var target := PARSEJSON(Cover.GetStatus, "$.target_pos", true);
var slatPos := PARSEJSON(Cover.GetStatus, "$.slat_pos", true);

BlSend := 0;
if BsSend = 0 and (Bs = 0 or Bs = 1) 
	if (!isnull(target))
		DeltaPos := 0;
		return(linear(target,0,1,100,0));
	else
		return(linear(pos - DeltaPos,0,1,100,0));	
	end
end

return(Bl);
</ReadBlindLevelScriptPacketParser>
            <ReadBlindSlopeScriptPacketParser>var pos := PARSEJSON(Cover.GetStatus, "$.current_pos", true);
var slatPos := PARSEJSON(Cover.GetStatus, "$.slat_pos", true);
var state := PARSEJSON(Cover.GetStatus, "$.state", true);
var date := NOW();

if ISNULL(slatPos)
    adderror("Slat position must be enabled for blinds");
    return(NaN);
end

if state = "closing" or state = "opening"
	BsSend := 1;
	return(Bs);
end

if BsSend &gt; 1
	BsSend := 1;
	return(Bs);
else
	if BsSend = 1
		BsSend := 0;
		DeltaPos := pos - round(linear(Bl,0,100,1,0));	
	end
	return(linear(slatPos,0,1,100,0));
end</ReadBlindSlopeScriptPacketParser>
            <WriteBlindLevelScriptPacketParser>var pos := PARSEJSON(Cover.GetStatus, "$.current_pos", true);
var target := PARSEJSON(Cover.GetStatus, "$.target_pos", true);
var slatPos := PARSEJSON(Cover.GetStatus, "$.slat_pos", true);

VAR response := SENDHTTPREQUEST("rpc/Cover.GoToPosition?id=0&amp;pos=" + round(linear(Bl,0,100,1,0)));
BlSend := 1;
IF response.IsSuccess = false
  ADDERROR(response.StatusCode);
END

#response := SENDHTTPREQUEST("/rpc/Cover.GetStatus?id=0");
#IF response.IsSuccess
#    Cover.GetStatus := response.Content;  
#END
</WriteBlindLevelScriptPacketParser>
            <WriteBlindSlopeScriptPacketParser>var date := NOW();
var pos := PARSEJSON(Cover.GetStatus, "$.current_pos", true);

if BlSend = 0
	VAR response := SENDHTTPREQUEST("rpc/Cover.GoToPosition?id=0&amp;pos=" + pos + "&amp;slat_pos=" + round(linear(Bs,0,100,1,0)));
	BsSend := date.UNIXTIME;
	IF response.IsSuccess = false
	  ADDERROR(response.StatusCode);
	END
end

#response := SENDHTTPREQUEST("/rpc/Cover.GetStatus?id=0");
#IF response.IsSuccess
#    Cover.GetStatus := response.Content;  
#END
</WriteBlindSlopeScriptPacketParser>
            <OffsetTop>0</OffsetTop>
            <OffsetBottom>0</OffsetBottom>
            <OffsetLeft>0</OffsetLeft>
            <OffsetRight>0</OffsetRight>
            <WindowHorizontalAngle>0</WindowHorizontalAngle>
            <Type180>False</Type180>
            <CoolingAngleSunElevation0>80</CoolingAngleSunElevation0>
            <CoolingAngleSunElevation15>60</CoolingAngleSunElevation15>
            <CoolingAngleSunElevation30>40</CoolingAngleSunElevation30>
            <CoolingAngleSunElevation45>20</CoolingAngleSunElevation45>
            <CoolingAngleSunElevation60>10</CoolingAngleSunElevation60>
            <CoolingAngleSunElevation75>0</CoolingAngleSunElevation75>
            <CoolingAngleSunElevation90>0</CoolingAngleSunElevation90>
            <HeatingAngleSunElevation0>0</HeatingAngleSunElevation0>
            <HeatingAngleSunElevation15>0</HeatingAngleSunElevation15>
            <HeatingAngleSunElevation30>0</HeatingAngleSunElevation30>
            <HeatingAngleSunElevation45>0</HeatingAngleSunElevation45>
            <HeatingAngleSunElevation60>0</HeatingAngleSunElevation60>
            <HeatingAngleSunElevation75>0</HeatingAngleSunElevation75>
            <HeatingAngleSunElevation90>0</HeatingAngleSunElevation90>
            <WindowHeight>2</WindowHeight>
            <DistanceFromFloorToWindow>1</DistanceFromFloorToWindow>
          </DeviceProperties>
        </Device>
      </Devices>
    </Module>
  </Template>
</Templates>