Skip to content

Commit

Permalink
Remotes enhacement (#1290)
Browse files Browse the repository at this point in the history
* add specific code for RWL021 when  on zigpy mode
* Finish Legrand Sleep Wakeup device integration (#1289)
* Add LegrandSleepWakeupSelector
* Move LegrandSleepWakeupSelector to LvlControl
* fix decoding of fc00 frame when coming from zigpy. Add a RWL021_Dimmer_step parameter to define the progressing step of the Level
* fixing issue when calling get_max_cfg_rpt_attribute_value
* refactor how to handle philips remote hue, from zigate
* remove double space (codefactor)

Co-authored-by: Sylvain PERON <[email protected]>
  • Loading branch information
pipiche38 and SylvainPer authored Sep 27, 2022
1 parent 490c8fa commit 4b7a066
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 269 deletions.
52 changes: 0 additions & 52 deletions Conf/Certified/Legrand/Remote switch Wake up Sleep.json

This file was deleted.

55 changes: 29 additions & 26 deletions Conf/Certified/Philips/RWL021.json
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
{
"_comment": "Philips Hue Remote",
"_version": "1.0",
"Ep":{
"02":{
"0000":"",
"0001":"",
"0003":"",
"000f":"",
"fc00":"",
"Type":"LvlControl"
}
},
"Type":"",
"NickName":"Hue_Remote",
"ClusterToBind": [ "fc00", "0001", "000f" ],
"ConfigureReporting": {
"0001": {"Attributes": { "0021": {"DataType": "20", "MinInterval":"012C", "MaxInterval":"012C", "TimeOut":"0FFF","Change":"00"}}}
},
"ReadAttributes": {
"0000": [ "0000", "0001", "0002", "0003", "0004", "0005", "0006" ] ,
"0001": [ "0020" ] ,
"000f": [ ],
"fc00": [ "0000" ]
},
"BatteryDevice": 1,
"BatteryPercentageConverter": 2
"_comment": "Philips Hue Remote",
"_version": "1.0",
"Ep":{
"02":{
"0000":"",
"0001":"",
"0003":"",
"000f":"",
"fc00":"",
"Type":"LvlControl"
}
},
"Type":"",
"NickName":"Hue_Remote",
"ClusterToBind": [ "fc00", "0001", "000f" ],
"ConfigureReporting": {
"0001": {"Attributes": { "0021": {"DataType": "20", "MinInterval":"012C", "MaxInterval":"012C", "TimeOut":"0FFF","Change":"00"}}}
},
"ReadAttributes": {
"0000": [ "0000", "0001", "0002", "0003", "0004", "0005", "0006" ] ,
"0001": [ "0020" ] ,
"000f": [ ],
"fc00": [ "0000" ]
},
"BatteryDevice": 1,
"BatteryPercentageConverter": 2,
"Param": {
"RWL021_Dimmer_step": 1
}
}
2 changes: 1 addition & 1 deletion Modules/domoMaj.py
Original file line number Diff line number Diff line change
Expand Up @@ -1141,7 +1141,7 @@ def MajDomoDevice(self, Devices, NWKID, Ep, clusterID, value, Attribute_="", Col
nValue, sValue = getDimmerLevelOfColor(self, value)
UpdateDevice_v2(self, Devices, DeviceUnit, nValue, str(sValue), BatteryLevel, SignalLevel, Color_)

elif WidgetType == "LegrandSelector":
elif WidgetType in ("LegrandSelector", "LegrandSleepWakeupSelector"):
self.log.logging("Widget", "Debug", "------> LegrandSelector : Value -> %s" % value, NWKID)
if value == "00":
nValue = 0
Expand Down
4 changes: 2 additions & 2 deletions Modules/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -4418,9 +4418,9 @@ def Decode80A5(self, Devices, MsgData, MsgLQI):

if _ModelName == 'Remote switch Wake up Sleep':
if GroupID == 'fff4':
MajDomoDevice(self, Devices, MsgSrcAddr, "01", "0006", "00")
MajDomoDevice(self, Devices, MsgSrcAddr, "01", "0008", "00")
elif GroupID == 'fff5':
MajDomoDevice(self, Devices, MsgSrcAddr, "01", "0006", "01")
MajDomoDevice(self, Devices, MsgSrcAddr, "01", "0008", "01")



Expand Down
131 changes: 127 additions & 4 deletions Modules/philips.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@
#
# Author: zaraki673 & pipiche38
#
import struct

import Domoticz

import Modules.paramDevice
from Modules.basicOutputs import (raw_APS_request, set_poweron_afteroffon,
write_attribute)
from Modules.domoMaj import MajDomoDevice
from Modules.readAttributes import (ReadAttributeRequest_0006_0000,
ReadAttributeRequest_0006_400x,
ReadAttributeRequest_0008_0000,
ReadAttributeRequest_0406_philips_0030)
from Modules.tools import is_ack_tobe_disabled, retreive_cmd_payload_from_8002
from Modules.tools import (checkAndStoreAttributeValue, is_hex,
retreive_cmd_payload_from_8002)
from Modules.zigateConsts import ZIGATE_EP

PHILIPS_POWERON_MODE = {0x00: "Off", 0x01: "On", 0xFF: "Previous state"} # Off # On # Previous state
Expand Down Expand Up @@ -87,13 +92,14 @@ def philipsReadRawAPS(self, Devices, srcNWKID, srcEp, ClusterID, dstNWKID, dstEP

default_response, GlobalCommand, sqn, ManufacturerCode, cmd, data = retreive_cmd_payload_from_8002(MsgPayload)

if _ModelName == "RWL021" and cmd == "00" and ClusterID == "fc00":
if self.zigbee_communication == "native" and _ModelName == "RWL021" and cmd == "00" and ClusterID == "fc00":
# This is handle by the firmware
return

elif self.zigbee_communication == "zigpy" and _ModelName == "RWL021" and cmd == "00" and ClusterID == "fc00":
zigpy_philips_dimmer_switch(self, Devices, srcNWKID, srcEp, ClusterID, dstNWKID, dstEP, MsgPayload)
self.log.logging(
"Philips",
"Log",
"Debug",
"philipsReadRawAPS - Nwkid: %s/%s Cluster: %s, GlobalCommand: %s sqn: %s, Command: %s Payload: %s"
% (srcNWKID, srcEp, ClusterID, GlobalCommand, sqn, cmd, data),
)
Expand Down Expand Up @@ -148,3 +154,120 @@ def philips_set_poweron_after_offon_device(self, mode, nwkid):
)
set_poweron_afteroffon(self, nwkid, OnOffMode=mode)
ReadAttributeRequest_0006_400x(self, nwkid)

def zigpy_philips_dimmer_switch(self, Devices, MsgSrcAddr, MsgSrcEp, MsgClusterId, dstNWKID, dstEP, MsgPayload):
# 1d/0b10/2400/0300/00/30|01|21|1800 (long press)
# xxxx -> duration
# xx-> Action
# xxxx -> Attribute Id
# xxxx -> Manuf code

MsgAttrID = "%04x" %struct.unpack("H", struct.pack(">H", int( MsgPayload[10:14], 16)))[0]
MsgClusterData = MsgPayload[14+2:]
philips_dimmer_switch(self, Devices, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID, MsgClusterData)

def philips_dimmer_switch(self, Devices, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID, MsgClusterData):

self.log.logging( "Philips", "Debug", "philips_dimmer_switch %s - %s/%s - MsgAttrID = %s MsgClusterData: %s" % (
MsgSrcAddr, MsgSrcEp, MsgClusterId,MsgAttrID, MsgClusterData ))

self.log.logging( "Philips", "Debug", "philips_dimmer_switch %s - %s/%s - reading self.ListOfDevices[%s]['Ep'][%s][%s][%s] = %s" % (
MsgClusterId, MsgSrcAddr, MsgSrcEp, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID, self.ListOfDevices[MsgSrcAddr]["Ep"][MsgSrcEp][MsgClusterId], ), MsgSrcAddr, )

rwl021_dimmer_step = Modules.paramDevice.get_device_config_param(self, MsgSrcAddr, "RWL021_Dimmer_step")
if rwl021_dimmer_step is None:
rwl021_dimmer_step = 1

self.log.logging( "Philips", "Debug", "philips_dimmer_switch %s/%s - dimming step: %s" %( MsgSrcAddr, MsgSrcEp, rwl021_dimmer_step))

prev_Value = _retreive_current_position( self, MsgSrcAddr, MsgSrcEp, MsgClusterId)

prev_onoffvalue = onoffValue = int(prev_Value[0], 16)
prev_lvlValue = lvlValue = int(prev_Value[1], 16)
prev_duration = duration = int(prev_Value[2], 16)

self.log.logging( "Philips", "Debug", "philips_dimmer_switch - %s - %s/%s - past OnOff: %s, Lvl: %s" % (MsgClusterId, MsgSrcAddr, MsgSrcEp, onoffValue, lvlValue), MsgSrcAddr, )

if MsgAttrID == "0001": # On button
self.log.logging( "Philips", "Debug", "philips_dimmer_switch - %s - %s/%s - ON Button detected" % (MsgClusterId, MsgSrcAddr, MsgSrcEp), MsgSrcAddr, )
return _update_domoticz_widget( self, Devices, MsgSrcAddr, MsgSrcEp, MsgClusterId, prev_onoffvalue, 1, prev_lvlValue, lvlValue, duration )

elif MsgAttrID == "0004": # Off Button
self.log.logging( "Philips", "Debug", "philips_dimmer_switch - %s - %s/%s - OFF Button detected" % (
MsgClusterId, MsgSrcAddr, MsgSrcEp), MsgSrcAddr, )
onoffValue = 0
return _update_domoticz_widget( self, Devices, MsgSrcAddr, MsgSrcEp, MsgClusterId, prev_onoffvalue, 0, prev_lvlValue, lvlValue, duration )

elif MsgAttrID in ("0002", "0003"): # Dim+ / 0002 is +, 0003 is -
self.log.logging( "Philips", "Debug", "philips_dimmer_switch - %s - %s/%s - DIM Button detected" % (
MsgClusterId, MsgSrcAddr, MsgSrcEp), MsgSrcAddr, )
action = MsgClusterData[2:4]
duration = struct.unpack("H", struct.pack(">H", int( MsgClusterData[6:10], 16)))[0]

if action in ("00", ): # Short press
self.log.logging( "Philips", "Debug", "philips_dimmer_switch - %s - %s/%s - DIM Action: %s" % (
MsgClusterId, MsgSrcAddr, MsgSrcEp, action), MsgSrcAddr, )
onoffValue = 1
# Short press/Release - Make one step , we just report the press
if MsgAttrID == "0002":
lvlValue += rwl021_dimmer_step
elif MsgAttrID == "0003":
lvlValue -= rwl021_dimmer_step

elif action in ("01",): # Long press
delta = duration - prev_duration # Time press since last message
onoffValue = 1
if MsgAttrID == "0002":
lvlValue += round(delta * rwl021_dimmer_step)
elif MsgAttrID == "0003":
lvlValue -= round(delta * rwl021_dimmer_step)

elif action in ("03", "02"): # Release after Long/short Press
self.log.logging( "Philips", "Debug", "philips_dimmer_switch - %s - %s/%s - DIM Release after %s seconds" % (
MsgClusterId, MsgSrcAddr, MsgSrcEp, round(duration / 10)), MsgSrcAddr, )

else:
self.log.logging( "Philips", "Debug", "philips_dimmer_switch - %s - %s/%s - DIM Action: %s not processed" % (
MsgClusterId, MsgSrcAddr, MsgSrcEp, action), MsgSrcAddr, )
return # No need to update

# Check if we reach the limits Min and Max
lvlValue = min(lvlValue, 255)
lvlValue = max(lvlValue, 0)
self.log.logging( "Philips", "Debug", "philips_dimmer_switch - %s - %s/%s - Level: %s " % (
MsgClusterId, MsgSrcAddr, MsgSrcEp, lvlValue), MsgSrcAddr, )
return _update_domoticz_widget( self, Devices, MsgSrcAddr, MsgSrcEp, MsgClusterId, prev_onoffvalue, onoffValue, prev_lvlValue, lvlValue, duration )

else:
self.log.logging( "Philips", "Debug", "philips_dimmer_switch - %s - %s/%s unknown attribute: %s %s" % (
MsgClusterId, MsgSrcAddr, MsgSrcEp, MsgAttrID, MsgClusterData), MsgSrcAddr, )



def _update_domoticz_widget( self, Devices, MsgSrcAddr, MsgSrcEp, MsgClusterId, prev_onoffvalue, onoffValue, prev_lvlValue, lvlValue, duration ):
# Update Domo
sonoffValue = "%02x" % onoffValue
slvlValue = "%02x" % lvlValue
sduration = "%02x" % duration

checkAndStoreAttributeValue(self, MsgSrcAddr, MsgSrcEp, MsgClusterId, "0000", "%s;%s;%s" % (sonoffValue, slvlValue, sduration))
self.log.logging( "Philips", "Debug", "philips_dimmer_switch %s - %s/%s - updating self.ListOfDevices[%s]['Ep'][%s][%s] = %s" % (
MsgClusterId, MsgSrcAddr, MsgSrcEp, MsgSrcAddr, MsgSrcEp, MsgClusterId, self.ListOfDevices[MsgSrcAddr]["Ep"][MsgSrcEp][MsgClusterId], ),MsgSrcAddr,)

if prev_onoffvalue != onoffValue:
MajDomoDevice(self, Devices, MsgSrcAddr, MsgSrcEp, "0006", sonoffValue)
if prev_lvlValue != lvlValue:
MajDomoDevice(self, Devices, MsgSrcAddr, MsgSrcEp, MsgClusterId, slvlValue)


def _retreive_current_position( self, MsgSrcAddr, MsgSrcEp, MsgClusterId):
if "0000" not in self.ListOfDevices[MsgSrcAddr]["Ep"][MsgSrcEp][MsgClusterId]:
return "0;80;0".split(";")
prev_Value = str(self.ListOfDevices[MsgSrcAddr]["Ep"][MsgSrcEp][MsgClusterId]["0000"]).split(";")
if len(prev_Value) != 3:
return "0;80;0".split(";")
for val in prev_Value:
if not is_hex(val):
prev_Value = "0;80;0".split(";")
break
return prev_Value
Loading

0 comments on commit 4b7a066

Please sign in to comment.