﻿<?xml version="1.0" encoding="utf-8"?>
<Templates format="2" protocolVersion="74">
  <Template revision="1.0" id="6f9c3038-49db-4caf-8bc6-1650d7b5a21e">
    <SuggestedCCUParameters />
    <RevisionHistory>
      <Revision id="1.0">Initial version</Revision>
    </RevisionHistory>
	<ImportParameters>
		<Parameter>
			<Name>${ipAddress}</Name>	
			<Value>192.168.0.1</Value>
			<Id>IpAddress</Id>
		</Parameter>
		<Parameter>
			<Name>Transition time (ms)</Name>	
			<Value>300</Value>
			<Id>TransitionTime</Id>
		</Parameter>
	</ImportParameters>
    <Name>Shelly DUO RGBW</Name>
    <CcuModel>PacketParserCCU</CcuModel>
    <Producer></Producer>
    <Model></Model>
    <Description></Description>
    <Module>
      <Name>Shelly DUO RGBW Module</Name>
      <Model>PacketParserModuleHttp</Model>
      <DeviceProperties>
        <ReadScriptPacketParser>VAR now := NOW();

IF now.Ticks &lt; debounceTimestamp
    RETURN(0);
END

VAR response := SENDHTTPREQUEST("/status");

IF response.IsSuccess
    jsonStatus := response.Content;
ELSE
    jsonStatus := NULL;
END</ReadScriptPacketParser>
        <InternalPollInterval>2500</InternalPollInterval>
        <CustomVariables>[{"Name":"jsonStatus","ValueTypeId":2,"NumericValue":0,"StringValue":""},{"Name":"debounceTimestamp","ValueTypeId":1,"NumericValue":0},{"Name":"transitionTime","ValueTypeId":1,"NumericValue":$[TransitionTime]}]</CustomVariables>
        <ServiceAttributesScriptsPacketParser>[{"AttributeDefinition":{"Name":"IP Address"},"ReadFormula":"VAR response := SENDHTTPREQUEST(\u0022/status\u0022);\r\n\r\nIF response.IsSuccess\r\n    jsonStatus := response.Content;\r\nELSE\r\n    jsonStatus := NULL;\r\nEND\r\n\r\nVAR address := PARSEJSON(jsonStatus, \u0022$.wifi_sta.ip\u0022, 1);\r\nIF (ISNULL(address), \u0022-\u0022, address)"},{"AttributeDefinition":{"Name":"MAC Address"},"ReadFormula":"VAR value := PARSEJSON(jsonStatus, \u0022$.mac\u0022, 1);\r\nIF ISNULL(value)\r\n    RETURN(\u0022-\u0022);\r\nEND;\r\n\r\nSTRING mac;\r\nINT i := 0;\r\nINT len := LENGTH(value);\r\n\r\nWHILE i \u003C len\r\n    mac \u002B= GETAT(value, i);\r\n    i \u002B= 1;\r\n    \r\n    IF MOD(i, 2) = 0 AND i \u003C len\r\n        mac \u002B= \u0022:\u0022;\r\n    END\r\nLOOP\r\n\r\nmac"},{"AttributeDefinition":{"Name":"WIFI signal"},"ReadFormula":"VAR signal := PARSEJSON(jsonStatus, \u0022$.wifi_sta.rssi\u0022, 1);\r\nIF (ISNULL(signal), \u0022-\u0022, signal \u002B \u0022db\u0022);"},{"AttributeDefinition":{"Name":"Cloud enabled"},"ReadFormula":"VAR value := PARSEJSON(jsonStatus, \u0022$.cloud.enabled\u0022, 1);\r\nIF (ISNULL(value), \u0022-\u0022, value)"},{"AttributeDefinition":{"Name":"Cloud connected"},"ReadFormula":"VAR value := PARSEJSON(jsonStatus, \u0022$.cloud.connected\u0022, 1);\r\nIF (ISNULL(value), \u0022-\u0022, value)"},{"AttributeDefinition":{"Name":"MQTT connected"},"ReadFormula":"VAR value := PARSEJSON(jsonStatus, \u0022$.mqtt.connected\u0022, 1);\r\nIF (ISNULL(value), \u0022-\u0022, value)"},{"AttributeDefinition":{"Name":"Device time"},"ReadFormula":"VAR value := PARSEJSON(jsonStatus, \u0022$.time\u0022, 1);\r\nIF (ISNULL(value), \u0022-\u0022, value)"},{"AttributeDefinition":{"Name":"FW update available"},"ReadFormula":"VAR value := PARSEJSON(jsonStatus, \u0022$.has_update\u0022);\r\nIF (ISNULL(value), \u0022-\u0022, value)"},{"AttributeDefinition":{"Name":"Uptime"},"ReadFormula":"VAR value := PARSEJSON(jsonStatus, \u0022$.uptime\u0022, 1);\r\nIF ISNULL(value)\r\n    RETURN(\u0022-\u0022);\r\nEND\r\n\r\nVAR days := FLOOR(value/86400, 1);\r\nvalue := MOD(value, 86400);\r\nVAR hours := FLOOR(value/3600, 1);\r\nvalue := MOD(value, 3600);\r\nVAR minutes := FLOOR(value/60, 1);\r\n\r\ndays \u002B \u0022day(s) \u0022 \u002B hours \u002B \u0022h \u0022 \u002B minutes \u002B \u0022m\u0022"},{"AttributeDefinition":{"Name":"RAM"},"ReadFormula":"VAR ramFree := PARSEJSON(jsonStatus, \u0022$.ram_free\u0022, 1);\r\nVAR ramTotal := PARSEJSON(jsonStatus, \u0022$.ram_total\u0022, 1);\r\n\r\nIF ISNULL(ramFree) OR ISNULL(ramTotal)\r\n    RETURN(\u0022-\u0022);\r\nEND\r\n\r\nramFree \u002B \u0022 bytes free of \u0022 \u002B ramTotal"}]</ServiceAttributesScriptsPacketParser>
        <ServiceActionsScriptsPacketParser>[{"ActionDefinition":{"Name":"Enable cloud","NumericParameters":[],"BoolParameters":[{"FriendlyName":"Enable","Abbreviation":"enable","OffValueName":"Disable","OnValueName":"Enable"}],"EnumParameters":[]},"Script":"VAR response := SENDHTTPREQUEST(\"/settings/cloud?enabled=\" + enable);\r\nVAR contentJson := response.Content;\r\nVAR wasEnabled := PARSEJSON(contentJson, \"enabled\");\r\n\r\nIF(wasEnabled, \"Cloud enabled\", \"Cloud disabled\");"},{"ActionDefinition":{"Name":"Reboot","NumericParameters":[],"BoolParameters":[],"EnumParameters":[]},"Script":"VAR response := SENDHTTPREQUEST(\"/reboot\");\r\nVAR contentJson := response.Content;\r\nVAR wasRebooted := PARSEJSON(contentJson, \"ok\");\r\n\r\nIF(wasRebooted, \"Reboot successful\", \"Error\");"}]</ServiceActionsScriptsPacketParser>
		<IpAddress>$[IpAddress]</IpAddress>
        <Port>80</Port>
        <UseHttps>False</UseHttps>
      </DeviceProperties>
      <Devices>
        <Device>
          <Name>Shelly DUO RGBW ${deviceType_Light}</Name>
          <Model>PacketParserHSBLight</Model>
          <DeviceProperties>
            <ReadScriptPacketParser></ReadScriptPacketParser>
            <InternalPollInterval>2500</InternalPollInterval>
            <CustomVariables></CustomVariables>
            <ServiceAttributesScriptsPacketParser></ServiceAttributesScriptsPacketParser>
            <ServiceActionsScriptsPacketParser></ServiceActionsScriptsPacketParser>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
			<MinColorTemperature>3000</MinColorTemperature>
			<MaxColorTemperature>6500</MaxColorTemperature>
            <ReadHueScriptPacketParser>VAR now := NOW();

IF now.Ticks &lt; debounceTimestamp
    RETURN(Hd);
END

INT r := PARSEJSON(jsonStatus, "$.lights[0].red", 1);
INT g := PARSEJSON(jsonStatus, "$.lights[0].green", 1);
INT b := PARSEJSON(jsonStatus, "$.lights[0].blue", 1);
 
VAR HSVColor := RGBTOHSV(r, g, b);
ROUND(HSVColor.Hue);</ReadHueScriptPacketParser>
            <ReadSaturationScriptPacketParser>VAR now := NOW();

IF now.Ticks &lt; debounceTimestamp
    RETURN(Sa);
END

INT r := PARSEJSON(jsonStatus, "$.lights[0].red", 1);
INT g := PARSEJSON(jsonStatus, "$.lights[0].green", 1);
INT b := PARSEJSON(jsonStatus, "$.lights[0].blue", 1);
 
VAR HSVColor := RGBTOHSV(r, g, b);
ROUND(HSVColor.Saturation * 100.0) / 100.0;</ReadSaturationScriptPacketParser>
            <ReadBrightnessScriptPacketParser>VAR now := NOW();

IF now.Ticks &lt; debounceTimestamp
    RETURN(Hb);
END

INT value;
VAR mode := PARSEJSON(jsonStatus, "$.lights[0].mode", 1);

IF mode = "white"
    value := PARSEJSON(jsonStatus, "$.lights[0].brightness", 1);
ELSE
    value := PARSEJSON(jsonStatus, "$.lights[0].gain", 1);
END

IF ISNULL(value)
    RETURN(NaN);
END

BOOL isOn := PARSEJSON(jsonStatus, "$.lights[0].ison", 1);

IF ISNULL(isOn)
    RETURN(NaN);
END

IF(isOn, value/100.0, 0);</ReadBrightnessScriptPacketParser>
            <ReadCCTScriptPacketParser>VAR now := NOW();

IF now.Ticks &lt; debounceTimestamp
    RETURN(Ct);
END

INT value := PARSEJSON(jsonStatus, "$.lights[0].temp", 1);
STRING mode := PARSEJSON(jsonStatus, "$.lights[0].mode", 1);

IF ISNULL(value) OR mode != "white"
    RETURN(NaN);
END
 
value</ReadCCTScriptPacketParser>
            <WriteHueScriptPacketParser>IF !ISNAN(Ct)
    RETURN(0);
END

VAR color := HSVTORGB(Hd, Sa, 1);

SENDHTTPREQUEST("light/0?mode=color&amp;transition=" + transitionTime + "&amp;gain=" + ROUND(Hb*100) + "&amp;red=" + color.RED + "&amp;green=" + color.GREEN + "&amp;blue=" + color.BLUE);


VAR now := NOW();
debounceTimestamp := now.Ticks + transitionTime + 500;</WriteHueScriptPacketParser>
            <WriteSaturationScriptPacketParser>IF !ISNAN(Ct)
    RETURN(0);
END

VAR color := HSVTORGB(Hd, Sa, 1);

SENDHTTPREQUEST("light/0?mode=color&amp;transition=" + transitionTime + "&amp;gain=" + ROUND(Hb*100) + "&amp;red=" + color.RED + "&amp;green=" + color.GREEN + "&amp;blue=" + color.BLUE);


VAR now := NOW();
debounceTimestamp := now.Ticks + transitionTime + 500;</WriteSaturationScriptPacketParser>
            <WriteBrightnessScriptPacketParser>IF St &gt; 0.5
    VAR mode := PARSEJSON(jsonStatus, "$.lights[0].mode", 1);
    
    IF mode = "white"
        SENDHTTPREQUEST("light/0?turn=on&amp;transition=" + transitionTime + "&amp;brightness=" + ROUND(Hb*100.0));
    ELSE
        SENDHTTPREQUEST("light/0?turn=on&amp;transition=" + transitionTime + "&amp;gain=" + ROUND(Hb*100.0));
    END
ELSE
    SENDHTTPREQUEST("light/0?turn=off&amp;transition=" + transitionTime);
END

VAR now := NOW();
debounceTimestamp := now.Ticks + transitionTime + 500;</WriteBrightnessScriptPacketParser>
            <WriteCCTScriptPacketParser>IF Ct = NaN
    RETURN(0);
END

VAR temp := MIN(Ct, 6500);
temp := MAX(temp, 3000);

SENDHTTPREQUEST("light/0?mode=white&amp;transition=" + transitionTime + "&amp;brightness=" + ROUND(Hb*100) + "&amp;temp=" + temp);

VAR now := NOW();
debounceTimestamp := now.Ticks + transitionTime + 500;</WriteCCTScriptPacketParser>
            <IconId>0</IconId>
          </DeviceProperties>
        </Device>
		<Device>
		  <Name>Shelly DUO RGBW Electric meter</Name>
          <Model>PacketParserElectricityMeter</Model>
          <DeviceProperties>
            <ReadScriptPacketParser></ReadScriptPacketParser>
            <InternalPollInterval>2500</InternalPollInterval>
            <CustomVariables />
            <ServiceAttributesScriptsPacketParser></ServiceAttributesScriptsPacketParser>
            <ServiceActionsScriptsPacketParser></ServiceActionsScriptsPacketParser>
            <ReadTotalConsumptionScriptPacketParser>IF LENGTH(jsonStatus) = 0
    RETURN(NaN);
END

VAR power := PARSEJSON(jsonStatus, "$.meters[0].power");
VAR total := PARSEJSON(jsonStatus, "$.meters[0].total");
BOOL isOn := PARSEJSON(jsonStatus, "$.lights[0].ison");

IF ISNULL(power) OR ISNULL(total) OR (ison AND power=0)
	ADDWARNING("To enable power monitoring, open device settings in Shelly app or web browser and configure device model in Settings-&gt;Device Model");
	
    RETURN(NaN);
END

total / 60000.0</ReadTotalConsumptionScriptPacketParser>
            <ReadDemandScriptPacketParser>IF LENGTH(jsonStatus) = 0
    RETURN(NaN);
END

VAR power := PARSEJSON(jsonStatus, "$.meters[0].power", 1);

IF ISNULL(power)
    RETURN(NaN);
END

power / 1000.0</ReadDemandScriptPacketParser>
            <Calibration>0</Calibration>
          </DeviceProperties>
        </Device>
      </Devices>
    </Module>
  </Template>
</Templates>