Skip to content

Commit

Permalink
Matter support for split lights (SetOption68 1 and `SetOption37 128…
Browse files Browse the repository at this point in the history
  • Loading branch information
s-hadinger authored Jul 24, 2024
1 parent 8a7642f commit b62b2d0
Show file tree
Hide file tree
Showing 18 changed files with 2,302 additions and 2,077 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
### Added
- Support for Sonoff iFan04-H using template (#16402)
- Matter improve internal `inspect`for superclasses
- Matter support for split lights (`SetOption68 1` and `SetOption37 128`)

### Breaking Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,15 @@ class Matter_Plugin_Light0 : Matter_Plugin_Device
# var tick # tick value when it was last updated
# var node_label # name of the endpoint, used only in bridge mode, "" if none
var tasmota_relay_index # Relay number in Tasmota (1 based), may be nil for Lights 1/2/3 internal
var light_index # index number when using `light.get()` and `light.set()`
var shadow_onoff # (bool) status of the light power on/off

#############################################################
# Constructor
def init(device, endpoint, config)
super(self).init(device, endpoint, config)
self.shadow_onoff = false
self.light_index = 0 # default is 0 for light object
super(self).init(device, endpoint, config)
end

#############################################################
Expand Down Expand Up @@ -105,7 +107,7 @@ class Matter_Plugin_Light0 : Matter_Plugin_Device
self.update_shadow()
else
import light
light.set({'power':pow})
light.set({'power':pow}, self.light_index)
self.update_shadow()
end
end
Expand Down
27 changes: 22 additions & 5 deletions lib/libesp32/berry_matter/src/embedded/Matter_Plugin_3_Light1.be
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import matter

class Matter_Plugin_Light1 : Matter_Plugin_Light0
static var TYPE = "light1" # name of the plug-in in json
static var DISPLAY_NAME = "Light 1 Dimmer" # display name of the plug-in
static var DISPLAY_NAME = "Light 1 Dimmer" # display name of the plug-in
static var ARG = "light" # additional argument name (or empty if none)
static var ARG_HINT = "(opt) Light number"
# static var UPDATE_TIME = 250 # update every 250ms
static var CLUSTERS = matter.consolidate_clusters(_class, {
# 0x001D: inherited # Descriptor Cluster 9.5 p.453
Expand All @@ -46,13 +48,14 @@ class Matter_Plugin_Light1 : Matter_Plugin_Light0
# var node_label # name of the endpoint, used only in bridge mode, "" if none
# var tasmota_relay_index # Relay number in Tasmota (1 based), nil for internal light
# var shadow_onoff # (bool) status of the light power on/off
# var light_index # index number when using `light.get()` and `light.set()`
var shadow_bri # (int 0..254) brightness before Gamma correction - as per Matter 255 is not allowed

#############################################################
# Constructor
def init(device, endpoint, arguments)
super(self).init(device, endpoint, arguments)
self.shadow_bri = 0
super(self).init(device, endpoint, arguments)
end

#############################################################
Expand All @@ -64,6 +67,20 @@ class Matter_Plugin_Light1 : Matter_Plugin_Light0
if self.BRIDGE
self.tasmota_relay_index = int(config.find(self.ARG #-'relay'-#, nil))
if (self.tasmota_relay_index != nil && self.tasmota_relay_index <= 0) self.tasmota_relay_index = 1 end
else
if (self.tasmota_relay_index == nil) && (self.TYPE == "light1") # only if `light1` and not for subclasses
var light_index_arg = config.find(self.ARG #-'light'-#)
if (light_index_arg == nil)
if (tasmota.get_option(68) == 0) # if default mode, and `SO68 0`, check if we have split RGB/W
import light
if (light.get(1) != nil)
self.light_index = 1 # default value is `0` from superclass
end
end
else
self.light_index = int(light_index_arg) - 1 # internal is 0-based
end
end
end
end

Expand All @@ -73,7 +90,7 @@ class Matter_Plugin_Light1 : Matter_Plugin_Light0
def update_shadow()
if !self.VIRTUAL && !self.BRIDGE
import light
var light_status = light.get()
var light_status = light.get(self.light_index)
if light_status != nil
var pow = light_status.find('power', nil)
if pow != self.shadow_onoff
Expand Down Expand Up @@ -121,9 +138,9 @@ class Matter_Plugin_Light1 : Matter_Plugin_Light0
import light
var bri_255 = tasmota.scale_uint(bri_254, 0, 254, 0, 255)
if pow == nil
light.set({'bri': bri_255})
light.set({'bri': bri_255}, self.light_index)
else
light.set({'bri': bri_255, 'power': pow})
light.set({'bri': bri_255, 'power': pow}, self.light_index)
end
self.update_shadow()
end
Expand Down
14 changes: 11 additions & 3 deletions lib/libesp32/berry_matter/src/embedded/Matter_Plugin_4_Light2.be
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import matter

class Matter_Plugin_Light2 : Matter_Plugin_Light1
static var TYPE = "light2" # name of the plug-in in json
static var DISPLAY_NAME = "Light 2 CT" # display name of the plug-in
static var DISPLAY_NAME = "Light 2 CT" # display name of the plug-in
static var ARG = "" # no arg for native light
static var ARG_HINT = "_Not used_" # Hint for entering the Argument (inside 'placeholder')
static var CLUSTERS = matter.consolidate_clusters(_class, {
# 0x001D: inherited # Descriptor Cluster 9.5 p.453
# 0x0003: inherited # Identify 1.2 p.16
Expand All @@ -46,6 +48,7 @@ class Matter_Plugin_Light2 : Matter_Plugin_Light1
# var node_label # name of the endpoint, used only in bridge mode, "" if none
# var shadow_onoff # (bool) status of the light power on/off
# var shadow_bri # (int 0..254) brightness before Gamma correction - as per Matter 255 is not allowed
# var light_index # index number when using `light.get()` and `light.set()`
var shadow_ct # (int 153..500, default 325) Color Temperatur in mireds
var ct_min, ct_max # min and max value allowed for CT, Alexa emulation requires to have a narrower range 200..380

Expand All @@ -55,6 +58,10 @@ class Matter_Plugin_Light2 : Matter_Plugin_Light1
super(self).init(device, endpoint, arguments)
if !self.BRIDGE # in BRIDGE mode keep default to nil
self.shadow_ct = 325
import light
if (light.get(1) != nil)
self.light_index = 1 # default value is `0` from superclass
end
end
self.update_ct_minmax() # read SetOption to adjust ct min/max
end
Expand All @@ -67,7 +74,8 @@ class Matter_Plugin_Light2 : Matter_Plugin_Light1
import light
self.update_ct_minmax()
super(self).update_shadow()
var light_status = light.get()
# check if the light RGB/CT with split mode, i.e. CT is in `light.get(1)`
var light_status = light.get(self.light_index)
if light_status != nil
var ct = light_status.find('ct', nil)
if ct == nil ct = self.shadow_ct end
Expand Down Expand Up @@ -110,7 +118,7 @@ class Matter_Plugin_Light2 : Matter_Plugin_Light1
end
else
import light
light.set({'ct': ct})
light.set({'ct': ct}, self.light_index)
self.update_shadow()
end
end
Expand Down
13 changes: 7 additions & 6 deletions lib/libesp32/berry_matter/src/embedded/Matter_Plugin_4_Light3.be
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import matter

class Matter_Plugin_Light3 : Matter_Plugin_Light1
static var TYPE = "light3" # name of the plug-in in json
static var DISPLAY_NAME = "Light 3 RGB" # display name of the plug-in
static var DISPLAY_NAME = "Light 3 RGB" # display name of the plug-in
static var ARG = "" # no arg for native light
static var ARG_HINT = "_Not used_" # Hint for entering the Argument (inside 'placeholder')
static var CLUSTERS = matter.consolidate_clusters(_class, {
# 0x001D: inherited # Descriptor Cluster 9.5 p.453
# 0x0003: inherited # Identify 1.2 p.16
Expand Down Expand Up @@ -65,7 +67,7 @@ class Matter_Plugin_Light3 : Matter_Plugin_Light1
if !self.VIRTUAL && !self.BRIDGE
import light
super(self).update_shadow()
var light_status = light.get()
var light_status = light.get(self.light_index)
if light_status != nil
var hue = light_status.find('hue', nil)
var sat = light_status.find('sat', nil)
Expand Down Expand Up @@ -122,11 +124,11 @@ class Matter_Plugin_Light3 : Matter_Plugin_Light1
var sat_255 = (sat_254 != nil) ? tasmota.scale_uint(sat_254, 0, 254, 0, 255) : nil

if (hue_360 != nil) && (sat_255 != nil)
light.set({'hue': hue_360, 'sat': sat_255})
light.set({'hue': hue_360, 'sat': sat_255}, self.light_index)
elif (hue_360 != nil)
light.set({'hue': hue_360})
light.set({'hue': hue_360}, self.light_index)
else
light.set({'sat': sat_255})
light.set({'sat': sat_255}, self.light_index)
end
self.update_shadow()
end
Expand Down Expand Up @@ -176,7 +178,6 @@ class Matter_Plugin_Light3 : Matter_Plugin_Light1
# returns a TLV object if successful, contains the response
# or an `int` to indicate a status
def invoke_request(session, val, ctx)
import light
var TLV = matter.TLV
var cluster = ctx.cluster
var command = ctx.command
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class Matter_Plugin_Bridge_Light1 : Matter_Plugin_Light1
static var TYPE = "http_light1" # name of the plug-in in json
# static var DISPLAY_NAME = "Light 1 Dimmer" # display name of the plug-in
static var ARG = "relay" # additional argument name (or empty if none)
static var ARG_HINT = "Relay<x> number"
static var ARG_TYPE = / x -> int(x) # function to convert argument to the right type
static var UPDATE_TIME = 3000 # update every 3s
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class Matter_Plugin_Bridge_Light2 : Matter_Plugin_Light2
static var TYPE = "http_light2" # name of the plug-in in json
# static var DISPLAY_NAME = "Light 2 CT" # display name of the plug-in
static var ARG = "relay" # additional argument name (or empty if none)
static var ARG_HINT = "Relay<x> number"
static var ARG_TYPE = / x -> int(x) # function to convert argument to the right type
static var UPDATE_TIME = 3000 # update every 3s
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class Matter_Plugin_Bridge_Light3 : Matter_Plugin_Light3
static var TYPE = "http_light3" # name of the plug-in in json
# static var DISPLAY_NAME = "Light 3 RGB" # display name of the plug-in
static var ARG = "relay" # additional argument name (or empty if none)
static var ARG_HINT = "Relay<x> number"
static var ARG_TYPE = / x -> int(x) # function to convert argument to the right type
static var UPDATE_TIME = 3000 # update every 3s
end
Expand Down
42 changes: 36 additions & 6 deletions lib/libesp32/berry_matter/src/embedded/Matter_zz_Device.be
Original file line number Diff line number Diff line change
Expand Up @@ -1090,22 +1090,52 @@ class Matter_Device

# check if we have a light
var endpoint = matter.START_ENDPOINT
var light_present = false
var light_present = 0

import light
var light_status = light.get()
var light_status = light.get(0)
if light_status != nil
var channels_count = size(light_status.find('channels', ""))
light_present = 1
if channels_count > 0
if channels_count == 1
m[str(endpoint)] = {'type':'light1'}
endpoint += 1
# check if we have secondary Dimmer lights (SetOption68 1)
var idx = 1
var light_status_i
while (light_status_i := light.get(idx)) != nil
m[str(endpoint)] = {'type':'light1','light':idx + 1} # arg is 1-based so add 1
endpoint += 1
light_present += 1
idx += 1
end
elif channels_count == 2
m[str(endpoint)] = {'type':'light2'}
else
endpoint += 1
elif channels_count == 3
m[str(endpoint)] = {'type':'light3'}
endpoint += 1
# check if we have a split second light (SetOption37 128) with 4/5 channels
var light_status1 = light.get(1)
if (light_status1 != nil)
var channels_count1 = size(light_status1.find('channels', ""))

if (channels_count1 == 1)
m[str(endpoint)] = {'type':'light1'}
endpoint += 1
light_present += 1
elif (channels_count1 == 2)
m[str(endpoint)] = {'type':'light2'}
endpoint += 1
light_present += 1
end
end
elif channels_count == 4
# not supported yet
else # only option left is 5 channels
# not supported yet
end
light_present = true
endpoint += 1
end
end

Expand Down Expand Up @@ -1144,7 +1174,7 @@ class Matter_Device
# how many relays are present
var relay_count = size(tasmota.get_power())
var relay_index = 0 # start at index 0
if light_present relay_count -= 1 end # last power is taken for lights
relay_count -= light_present # last power(s) taken for lights

while relay_index < relay_count
if relays_reserved.find(relay_index) == nil # if relay is actual relay
Expand Down
Loading

0 comments on commit b62b2d0

Please sign in to comment.