﻿<?xml version="1.0" encoding="utf-8"?>
<Templates format="2" protocolVersion="74">
  <Template id="bc9f1632-89ea-4b11-8649-6f01fd41b7ed" revision="2.0">
    <SuggestedCCUParameters />
    <RevisionHistory>
      <Revision id="1.0">Initial version</Revision>
	  <Revision id="2.0">Add color temperature support</Revision>
    </RevisionHistory>
    <Name>Yeelight color</Name>
    <CcuModel>PacketParserCCU</CcuModel>
    <Producer></Producer>
    <Model></Model>
    <Description></Description>
	<ImportParameters>
		<Parameter>
			<Name>${ipAddress}</Name>
			<Value>192.168.0.1</Value>
			<Id>ipAddress</Id>
		</Parameter>
		<Parameter>
			<Name>Port</Name>
			<Value>55443</Value>
			<Id>port</Id>
		</Parameter>
		<Parameter>
			<Name>Internal poll interval</Name>
			<Value>10000</Value>
			<Id>internalPollInterval</Id>
		</Parameter>
	</ImportParameters>
    <Module>
      <Name>Yeelight color module</Name>
      <Model>PacketParserModuleTcp</Model>
      <DeviceProperties>
		<InternalPollInterval>$[internalPollInterval]</InternalPollInterval>
		<IpAddress>$[ipAddress]</IpAddress>
		<Port>$[port]</Port>
		<StayConnected>True</StayConnected>
        <ServiceAttributesScriptsPacketParser>[{"AttributeDefinition":{"Name":"Color mode"},"ReadFormula":"VAR json := \u0022{\\\u0022id\\\u0022:981, \\\u0022method\\\u0022:\\\u0022get_prop\\\u0022, \\\u0022params\\\u0022:[\\\u0022color_mode\\\u0022, \\\u0022ct\\\u0022]}\u0022;\r\nSENDDATA(json);\r\n\r\nRETURN(NULL);"},{"AttributeDefinition":{"Name":"Color temperature"}}]</ServiceAttributesScriptsPacketParser>
        <ListenerScriptPacketParser>VAR jsonResponse := TOSTRING(RECEIVEDBYTES);
VAR id := PARSEJSON(jsonResponse, "id", 1);

IF(id = 981)
   VAR error := PARSEJSON(jsonResponse, "error.message", 1);
   
   IF(!ISNULL(error))
      COMPLETESERVICEATTRIBUTE("Color mode", "", "error");
      COMPLETESERVICEATTRIBUTE("Color temperature", "", "error");
   ELSE
      VAR colorMode := PARSEJSON(jsonResponse, "result[0]", 1);
      VAR colorTemp := PARSEJSON(jsonResponse, "result[1]", 1);
      
      IF(!ISNULL(colorMode))
         VAR colorModeValue := SWITCH(TODOUBLE(colorMode), 1, "RGB", 2, "Temperature", 3, "HSV", "Unknown");
         COMPLETESERVICEATTRIBUTE("Color mode", colorModeValue, "");
      ELSE
         COMPLETESERVICEATTRIBUTE("Color mode", "", "error");
      END
      
      IF(!ISNULL(colorTemp))
         COMPLETESERVICEATTRIBUTE("Color temperature", colorTemp + "K", "");
      ELSE
         COMPLETESERVICEATTRIBUTE("Color temperature", "", "error");
      END
   END
END</ListenerScriptPacketParser>
        <MessageFramingDefinition>{"Framing":1,"Configuration":"{\"SendMessageBeginDelimiter\":null,\"SendMessageEndDelimiter\":\"DQo=\",\"ReceiveMessageBeginDelimiter\":null,\"ReceiveMessageEndDelimiter\":\"DQo=\"}"}</MessageFramingDefinition>
      </DeviceProperties>
      <Devices>
        <Device>
          <Name>Yeelight color</Name>
          <Model>PacketParserHSBLight</Model>
          <DeviceProperties>
            <ReadScriptPacketParser>IF(debounce &gt; 0)
    debounce := 0;
ELSE
    VAR json := "{\"id\":951, \"method\":\"get_prop\", \"params\":[\"hue\", \"sat\", \"bright\", \"power\", \"ct\", \"color_mode\"]}";
    SENDDATA(json);
END</ReadScriptPacketParser>
            <InternalPollInterval>10000</InternalPollInterval>
            <CustomVariables>[{"Name":"receiveError1","ValueTypeId":2,"NumericValue":0,"StringValue":""},{"Name":"receiveError2","ValueTypeId":2,"NumericValue":0,"StringValue":""},{"Name":"notificationError","ValueTypeId":2,"NumericValue":0,"StringValue":""},{"Name":"debounce","ValueTypeId":1,"NumericValue":0},{"Name":"writeErrorBrightness","ValueTypeId":2,"NumericValue":0,"StringValue":""},{"Name":"writeErrorHueSat","ValueTypeId":2,"NumericValue":0,"StringValue":""},{"Name":"writeErrorCt","ValueTypeId":2,"NumericValue":0,"StringValue":""}]</CustomVariables>
            <ServiceAttributesScriptsPacketParser></ServiceAttributesScriptsPacketParser>
            <ServiceActionsScriptsPacketParser></ServiceActionsScriptsPacketParser>
            <ListenerScriptPacketParser>VAR jsonResponse := TOSTRING(RECEIVEDBYTES);
VAR method := PARSEJSON(jsonResponse, "method", 1);
VAR id := PARSEJSON(jsonResponse, "id", 1);
VAR error := PARSEJSON(jsonResponse, "error.message", 1);

#response to ReadScript
IF(id = 951)
   receiveError1 := error;
   
   IF(LENGTH(receiveError1) = 0)
      VAR hueStr := PARSEJSON(jsonResponse, "result[0]", 1);
      VAR satStr := PARSEJSON(jsonResponse, "result[1]", 1);
      VAR brightnessStr := PARSEJSON(jsonResponse, "result[2]", 1);
      VAR onOffStr := PARSEJSON(jsonResponse, "result[3]", 1);
      VAR ctStr := PARSEJSON(jsonResponse, "result[4]", 1);
      VAR colorModeStr := PARSEJSON(jsonResponse, "result[5]", 1);
      
      IF(!ISNULL(hueStr))
         Hd := TODOUBLE(hueStr);
      END

      IF(!ISNULL(satStr))
        Sa := TODOUBLE(satStr) / 100.0;
      END
      
      IF(!ISNULL(brightnessStr))
        Hb := TODOUBLE(brightnessStr) / 100.0;
      END
   
      VAR cm := TODOUBLE(colorModeStr);
      IF(!ISNULL(ctStr) AND cm = 2)
        Ct := TODOUBLE(ctStr);
      ELSE
        Ct := NaN;
      END
      
      IF(onOffStr = "on")
         St := 1;
      ELSEIF(onOffStr = "off")
         Hb := 0;
         St := 0;
      END
   END
ELSEIF(id = 952)
   receiveError2 := error;
   
   IF(LENGTH(receiveError2) = 0)
      brightnessStr := PARSEJSON(jsonResponse, "result[0]", 1);
      
      IF(!ISNULL(brightnessStr))
        Hb := TODOUBLE(brightnessStr) / 100.0;
      END
   END
END

IF(id &gt;= 941 AND id &lt;= 942)
   writeErrorBrightness := error;
ELSEIF(id = 943)
   writeErrorHueSat := error;
ELSEIF(id = 944)
   writeErrorCt := error;
END

#general notification
IF(method = "props")
   notificationError := error;
   
   IF(LENGTH(notificationError) = 0)
      VAR brightJson := PARSEJSON(jsonResponse, "params.bright", 1);
      VAR onOffJson := PARSEJSON(jsonResponse, "params.power", 1);
      VAR hue := PARSEJSON(jsonResponse, "params.hue", 1);
      VAR sat := PARSEJSON(jsonResponse, "params.sat", 1);
      VAR colorMode := PARSEJSON(jsonResponse, "params.color_mode", 1);
      VAR ctJson := PARSEJSON(jsonResponse, "params.ct", 1);
      
      IF(!ISNULL(brightJson))
         Hb := brightJson / 100.0;
      END
      
      IF(!ISNULL(hue))
         Hd := hue;
         Ct := NaN;
      END      

      IF(!ISNULL(sat))
        Sa := sat / 100;
        Ct := NaN;
      END 
      
      IF(!ISNULL(ctJson))
        Ct := ctJson;
      END 
      
      IF(onOffJson = "on")
         St := 1;
         IF(ISNULL(brightJson))
            SENDDATA("{\"id\":952, \"method\":\"get_prop\", \"params\":[\"bright\"]}");
         END
      ELSEIF(onOffJson = "off")
         Hb := 0;
         St := 0;
      END
   END
END

IF(LENGTH(receiveError1) &gt; 0)
   ADDERROR(951, "Read error: " + receiveError1);
END

IF(LENGTH(receiveError2) &gt; 0)
   ADDERROR(952, "Read error: " + receiveError2);
END

IF(LENGTH(writeErrorBrightness) &gt; 0)
   ADDERROR(941, "Brightness write error: " + writeErrorBrightness);
END

IF(LENGTH(writeErrorHueSat) &gt; 0)
   ADDERROR(943, "HueSat write error: " + writeErrorHueSat);
END

IF(LENGTH(writeErrorCt) &gt; 0)
   ADDERROR(944, "Color temperature write error: " + writeErrorCt);
END

IF(LENGTH(notificationError) &gt; 0)
   ADDERROR(1, notificationError);
END</ListenerScriptPacketParser>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <ReadHueScriptPacketParser></ReadHueScriptPacketParser>
            <ReadSaturationScriptPacketParser></ReadSaturationScriptPacketParser>
            <ReadBrightnessScriptPacketParser></ReadBrightnessScriptPacketParser>
            <ReadCCTScriptPacketParser></ReadCCTScriptPacketParser>
            <WriteHueScriptPacketParser></WriteHueScriptPacketParser>
            <WriteSaturationScriptPacketParser>#do not send HUESAT when COLOR mode is active
IF !ISNAN(Ct)
    RETURN(0);
END

VAR hueSatJson := "{\"id\":941,\"method\":\"set_power\",\"params\":[\"on\", \"smooth\", 300]}\r\n{\"id\":943,\"method\":\"set_hsv\",\"params\":[" + ROUND(MOD(Hd, 360)) + ", " + ROUND(Sa*100) + ", \"smooth\", 300]}";

SENDDATA(hueSatJson);
debounce := 1;</WriteSaturationScriptPacketParser>
            <WriteBrightnessScriptPacketParser>IF(Hb &gt; 0)
    VAR jsonBrightness := "{\"id\":941,\"method\":\"set_power\",\"params\":[\"on\", \"smooth\", 300]}\r\n{\"id\":942,\"method\":\"set_bright\",\"params\":[" + ROUND(Hb*100) + ",\"smooth\", 300]}";
    SENDDATA(jsonBrightness);
ELSE
    VAR jsonPowerOff := "{\"id\":942,\"method\":\"set_power\",\"params\":[\"off\", \"smooth\", 300]}";
    SENDDATA(jsonPowerOff);
END

debounce := 1;</WriteBrightnessScriptPacketParser>
            <WriteCCTScriptPacketParser>#do not set CT in COLOR mode
IF ISNAN(Ct)
    RETURN(0);
END

VAR ctJson := "{\"id\":941,\"method\":\"set_power\",\"params\":[\"on\", \"smooth\", 300]}\r\n{\"id\":944,\"method\":\"set_ct_abx\",\"params\":[" + Ct + ", \"smooth\", 300]}";
SENDDATA(ctJson);

debounce := 1;</WriteCCTScriptPacketParser>
            <IconId>0</IconId>
            <MinColorTemperature>2700</MinColorTemperature>
            <MaxColorTemperature>6500</MaxColorTemperature>
          </DeviceProperties>
        </Device>
      </Devices>
    </Module>
  </Template>
</Templates>