From aae45d27a502c680908f36d3fe224ce9e2549951 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 4 Aug 2023 23:33:56 -0400 Subject: [PATCH 01/49] Adding Power consumption measurment to stelpro device Adding Power consumption measurment to stelpro device ( custom attribute 0x4008) Also added init function to trigger proper config of device (which was not always being properly triggered) Combined all stelar files into init.lua (as stelar_maestrostat.lua and stelar_sorb.lus a duplicates) --- .../zigbee-thermostat/src/stelpro/init.lua | 89 +++++++++++++++---- 1 file changed, 72 insertions(+), 17 deletions(-) diff --git a/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua b/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua index 1009b3dc55..e23cb4ed0c 100644 --- a/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua +++ b/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua @@ -14,11 +14,20 @@ local capabilities = require "st.capabilities" local clusters = require "st.zigbee.zcl.clusters" +local device_management = require "st.zigbee.device_management" +local cluster_base = require "st.zigbee.cluster_base" +local data_types = require "st.zigbee.data_types" +local RelativeHumidity = clusters.RelativeHumidity local Thermostat = clusters.Thermostat local ThermostatUserInterfaceConfiguration = clusters.ThermostatUserInterfaceConfiguration +local ThermostatMode = capabilities.thermostatMode local ThermostatOperatingState = capabilities.thermostatOperatingState +local ThermostatPower = capabilities.powerMeter + +local POWER_ATTTRIBUTE = 0x4008 +local MFG_CODE = 0x1185 local RX_FREEZE_VALUE = 0x7ffd local RX_HEAT_VALUE = 0x7fff @@ -28,18 +37,56 @@ local HEAT_ALRAM_TEMPERATURE = 50 local STELPRO_THERMOSTAT_FINGERPRINTS = { { mfr = "Stelpro", model = "MaestroStat" }, { mfr = "Stelpro", model = "SORB" }, - { mfr = "Stelpro", model = "SonomaStyle" } + { mfr = "Stelpro", model = "SonomaStyle" }, + { mfr = "Stelpro", model = "SMT402AD" } -- added M.Colmenarejo } local is_stelpro_thermostat = function(opts, driver, device) for _, fingerprint in ipairs(STELPRO_THERMOSTAT_FINGERPRINTS) do - if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then - return true - end + if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then + return true + end end return false end +local do_refresh = function(self, device) + local attributes = { + Thermostat.attributes.LocalTemperature, + Thermostat.attributes.PIHeatingDemand, + Thermostat.attributes.OccupiedHeatingSetpoint, + ThermostatUserInterfaceConfiguration.attributes.TemperatureDisplayMode, + ThermostatUserInterfaceConfiguration.attributes.KeypadLockout, + RelativeHumidity.attributes.MeasuredValue + } + for _, attribute in pairs(attributes) do + device:send(attribute:read(device)) + end + device:send(cluster_base.read_manufacturer_specific_attribute(device, Thermostat.ID, POWER_ATTTRIBUTE, MFG_CODE)) +end + +local device_added = function(self, device) + device:emit_event(capabilities.temperatureAlarm.temperatureAlarm.cleared()) + do_refresh(self, device) +end + +local do_configure = function(self, device) + device:send(Thermostat.attributes.LocalTemperature:configure_reporting(device, 10, 60, 50)) + device:send(Thermostat.attributes.OccupiedHeatingSetpoint:configure_reporting(device, 1, 600, 50)) + device:send(Thermostat.attributes.PIHeatingDemand:configure_reporting(device, 1, 60, 1)) + device:send(cluster_base.configure_reporting(device, data_types.ClusterId(Thermostat.ID) , POWER_ATTTRIBUTE, 0x23 , 1, 60, 1)) + + device:send(ThermostatUserInterfaceConfiguration.attributes.TemperatureDisplayMode:configure_reporting(device, 1, 0, 1)) + device:send(ThermostatUserInterfaceConfiguration.attributes.KeypadLockout:configure_reporting(device, 1, 0, 1)) + device:send(RelativeHumidity.attributes.MeasuredValue:configure_reporting(device, 10, 300, 1)) + + device:emit_event(ThermostatMode.thermostatMode.heat()) +end + +local device_init = function(self, device) + do_configure(self, device) +end + local function get_temperature(temperature) return temperature / 100 end @@ -73,11 +120,11 @@ local function thermostat_local_temp_attr_handler(driver, device, value, zb_rx) (last_alarm == "freeze" and temperature > FREEZE_ALRAM_TEMPERATURE) or (last_alarm == "heat" and temperature < HEAT_ALRAM_TEMPERATURE) ) then - if last_alarm == "freeze" then - event = capabilities.temperatureAlarm.temperatureAlarm.freeze() - else - event = capabilities.temperatureAlarm.temperatureAlarm.heat() - end + if last_alarm == "freeze" then + event = capabilities.temperatureAlarm.temperatureAlarm.freeze() + else + event = capabilities.temperatureAlarm.temperatureAlarm.heat() + end end else if temperature <= FREEZE_ALRAM_TEMPERATURE then @@ -98,21 +145,22 @@ local function thermostat_heating_set_point_attr_handler(driver, device, value, end local function thermostat_heating_demand_attr_handler(driver, device, value, zb_rx) - local event = value.value < 10 and ThermostatOperatingState.thermostatOperatingState.idle() or - ThermostatOperatingState.thermostatOperatingState.heating() + local event = value.value < 1 and ThermostatOperatingState.thermostatOperatingState.idle() or + ThermostatOperatingState.thermostatOperatingState.heating() device:emit_event(event) end +local function thermostat_power_attr_handler(driver, device, zb_rx) + local point_value = zb_rx.value + device:emit_event(capabilities.powerMeter.power({value = point_value, unit = "W"})) +end + local function info_changed(driver, device, event, args) if device.preferences ~= nil and device.preferences.lock ~= args.old_st_store.preferences.lock then device:send(ThermostatUserInterfaceConfiguration.attributes.KeypadLockout:write(device, tonumber(device.preferences.lock))) end end -local device_added = function(self, device) - -- device:emit_event(capabilities.temperatureAlarm.temperatureAlarm.cleared()) -end - local stelpro_thermostat = { NAME = "Stelpro Thermostat Handler", zigbee_handlers = { @@ -121,15 +169,22 @@ local stelpro_thermostat = { [Thermostat.attributes.PIHeatingDemand.ID] = thermostat_heating_demand_attr_handler, [Thermostat.attributes.LocalTemperature.ID] = thermostat_local_temp_attr_handler, [Thermostat.attributes.OccupiedHeatingSetpoint.ID] = thermostat_heating_set_point_attr_handler, + [POWER_ATTTRIBUTE] = thermostat_power_attr_handler } } }, lifecycle_handlers = { + init = device_init, + doConfigure = do_configure, added = device_added, infoChanged = info_changed }, - sub_drivers = { require("stelpro.stelpro_sorb"), require("stelpro.stelpro_maestrostat") }, - can_handle = is_stelpro_thermostat + capability_handlers = { + [capabilities.refresh.ID] = { + [capabilities.refresh.commands.refresh.NAME] = do_refresh, + } + }, + can_handle = is_stelpro_thermostat, } return stelpro_thermostat From e590f43db497968c65ab2a2bb093cb6851e0a405 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 4 Aug 2023 23:39:48 -0400 Subject: [PATCH 02/49] Update init.lua --- drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua b/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua index e23cb4ed0c..5c9e580dec 100644 --- a/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua +++ b/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua @@ -38,7 +38,7 @@ local STELPRO_THERMOSTAT_FINGERPRINTS = { { mfr = "Stelpro", model = "MaestroStat" }, { mfr = "Stelpro", model = "SORB" }, { mfr = "Stelpro", model = "SonomaStyle" }, - { mfr = "Stelpro", model = "SMT402AD" } -- added M.Colmenarejo + { mfr = "Stelpro", model = "SMT402AD" } } local is_stelpro_thermostat = function(opts, driver, device) From 7db786413ca3ce251877f0e06eaa6ceaa0acb029 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 4 Aug 2023 23:40:20 -0400 Subject: [PATCH 03/49] Delete stelpro_maestrostat.lua --- .../src/stelpro/stelpro_maestrostat.lua | 80 ------------------- 1 file changed, 80 deletions(-) delete mode 100644 drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_maestrostat.lua diff --git a/drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_maestrostat.lua b/drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_maestrostat.lua deleted file mode 100644 index e7742e8195..0000000000 --- a/drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_maestrostat.lua +++ /dev/null @@ -1,80 +0,0 @@ --- Copyright 2022 SmartThings --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - -local capabilities = require "st.capabilities" -local clusters = require "st.zigbee.zcl.clusters" -local device_management = require "st.zigbee.device_management" - -local RelativeHumidity = clusters.RelativeHumidity -local Thermostat = clusters.Thermostat -local ThermostatUserInterfaceConfiguration = clusters.ThermostatUserInterfaceConfiguration - -local STELPRO_THERMOSTAT_FINGERPRINTS = { - { mfr = "Stelpro", model = "MaestroStat" }, -} - -local is_stelpro_thermostat = function(opts, driver, device) - for _, fingerprint in ipairs(STELPRO_THERMOSTAT_FINGERPRINTS) do - if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then - return true - end - end - return false -end - -local do_refresh = function(self, device) - local attributes = { - Thermostat.attributes.LocalTemperature, - Thermostat.attributes.PIHeatingDemand, - Thermostat.attributes.OccupiedHeatingSetpoint, - ThermostatUserInterfaceConfiguration.attributes.TemperatureDisplayMode, - ThermostatUserInterfaceConfiguration.attributes.KeypadLockout, - RelativeHumidity.attributes.MeasuredValue - } - for _, attribute in pairs(attributes) do - device:send(attribute:read(device)) - end -end - -local device_added = function(self, device) - -- device:emit_event(capabilities.temperatureAlarm.temperatureAlarm.cleared()) - do_refresh(self, device) -end - -local function do_configure(self, device) - device:send(device_management.build_bind_request(device, Thermostat.ID, self.environment_info.hub_zigbee_eui)) - device:send(Thermostat.attributes.LocalTemperature:configure_reporting(device, 10, 60, 50)) - device:send(Thermostat.attributes.OccupiedHeatingSetpoint:configure_reporting(device, 1, 600, 50)) - device:send(Thermostat.attributes.PIHeatingDemand:configure_reporting(device, 1, 3600, 1)) - - device:send(ThermostatUserInterfaceConfiguration.attributes.TemperatureDisplayMode:configure_reporting(device, 1, 0, 1)) - device:send(ThermostatUserInterfaceConfiguration.attributes.KeypadLockout:configure_reporting(device, 1, 0, 1)) - device:send(RelativeHumidity.attributes.MeasuredValue:configure_reporting(device, 10, 300, 1)) -end - -local stelpro_maestro_othermostat = { - NAME = "Stelpro Maestro Thermostat Handler", - capability_handlers = { - [capabilities.refresh.ID] = { - [capabilities.refresh.commands.refresh.NAME] = do_refresh, - } - }, - lifecycle_handlers = { - added = device_added, - doConfigure = do_configure - }, - can_handle = is_stelpro_thermostat -} - -return stelpro_maestro_othermostat From c377860f38fa7d8cbee12c7b5889a650beab0d17 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 4 Aug 2023 23:40:29 -0400 Subject: [PATCH 04/49] Delete stelpro_sorb.lua --- .../src/stelpro/stelpro_sorb.lua | 81 ------------------- 1 file changed, 81 deletions(-) delete mode 100644 drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_sorb.lua diff --git a/drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_sorb.lua b/drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_sorb.lua deleted file mode 100644 index 1a43347239..0000000000 --- a/drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_sorb.lua +++ /dev/null @@ -1,81 +0,0 @@ --- Copyright 2022 SmartThings --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - -local capabilities = require "st.capabilities" -local clusters = require "st.zigbee.zcl.clusters" -local device_management = require "st.zigbee.device_management" - -local RelativeHumidity = clusters.RelativeHumidity -local Thermostat = clusters.Thermostat -local ThermostatUserInterfaceConfiguration = clusters.ThermostatUserInterfaceConfiguration - -local STELPRO_THERMOSTAT_FINGERPRINTS = { - { mfr = "Stelpro", model = "SORB" }, - { mfr = "Stelpro", model = "SonomaStyle" } -} - -local is_stelpro_sorb_thermostat = function(opts, driver, device) - for _, fingerprint in ipairs(STELPRO_THERMOSTAT_FINGERPRINTS) do - if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then - return true - end - end - return false -end - -local do_refresh = function(self, device) - local attributes = { - Thermostat.attributes.LocalTemperature, - Thermostat.attributes.PIHeatingDemand, - Thermostat.attributes.OccupiedHeatingSetpoint, - ThermostatUserInterfaceConfiguration.attributes.TemperatureDisplayMode, - ThermostatUserInterfaceConfiguration.attributes.KeypadLockout, - RelativeHumidity.attributes.MeasuredValue - } - for _, attribute in pairs(attributes) do - device:send(attribute:read(device)) - end -end - -local device_added = function(self, device) - -- device:emit_event(capabilities.temperatureAlarm.temperatureAlarm.cleared()) - do_refresh(self, device) -end - -local function do_configure(self, device) - device:send(device_management.build_bind_request(device, Thermostat.ID, self.environment_info.hub_zigbee_eui)) - device:send(Thermostat.attributes.LocalTemperature:configure_reporting(device, 10, 60, 50)) - device:send(Thermostat.attributes.OccupiedHeatingSetpoint:configure_reporting(device, 1, 600, 50)) - device:send(Thermostat.attributes.PIHeatingDemand:configure_reporting(device, 1, 3600, 1)) - - device:send(ThermostatUserInterfaceConfiguration.attributes.TemperatureDisplayMode:configure_reporting(device, 1, 0, 1)) - device:send(ThermostatUserInterfaceConfiguration.attributes.KeypadLockout:configure_reporting(device, 1, 0, 1)) - device:send(RelativeHumidity.attributes.MeasuredValue:configure_reporting(device, 10, 300, 1)) -end - -local stelpro_sorb_thermostat = { - NAME = "Stelpro SORB SonomaStyle Thermostat Handler", - capability_handlers = { - [capabilities.refresh.ID] = { - [capabilities.refresh.commands.refresh.NAME] = do_refresh, - } - }, - lifecycle_handlers = { - added = device_added, - doConfigure = do_configure - }, - can_handle = is_stelpro_sorb_thermostat -} - -return stelpro_sorb_thermostat From 56769274527d02b6576386a6f5f1d179ab6456cd Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 4 Aug 2023 23:41:26 -0400 Subject: [PATCH 05/49] Update thermostat-stelpro-profile.yml Adding missing capabilities needed for HomeAssistant support --- .../profiles/thermostat-stelpro-profile.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/SmartThings/zigbee-thermostat/profiles/thermostat-stelpro-profile.yml b/drivers/SmartThings/zigbee-thermostat/profiles/thermostat-stelpro-profile.yml index aee0c42f2d..02ddb08a4f 100644 --- a/drivers/SmartThings/zigbee-thermostat/profiles/thermostat-stelpro-profile.yml +++ b/drivers/SmartThings/zigbee-thermostat/profiles/thermostat-stelpro-profile.yml @@ -14,6 +14,15 @@ components: values: - key: "heatingSetpoint.value" range: [ 5, 30 ] + - id: thermostatCoolingSetpoint + - id: thermostatMode + version: 1 + config: + values: + - key: "thermostatMode.value" + enabledValues: + - heat + default: heat - id: thermostatOperatingState version: 1 config: @@ -22,6 +31,11 @@ components: enabledValues: - heating - idle + - id: powerMeter + version: 1 + config: + values: + - key: "powerMeter.value" - id: temperatureAlarm version: 1 config: From 20b1ddc8a7b1d9f5067def0fb297f93e17bac4cc Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 4 Aug 2023 23:43:25 -0400 Subject: [PATCH 06/49] Adding support for SMT402AD Thanks to M.colmenarejo --- drivers/SmartThings/zigbee-thermostat/fingerprints.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/SmartThings/zigbee-thermostat/fingerprints.yml b/drivers/SmartThings/zigbee-thermostat/fingerprints.yml index d1ced3a2cf..0d6640947b 100644 --- a/drivers/SmartThings/zigbee-thermostat/fingerprints.yml +++ b/drivers/SmartThings/zigbee-thermostat/fingerprints.yml @@ -69,6 +69,11 @@ zigbeeManufacturer: manufacturer: Stelpro model: STZB402+ deviceProfileName: thermostat-temperature-temperaturealarm + - id: "Stelpro/SMT402AD" + deviceLabel: Stelpro Thermostat + manufacturer: Stelpro + model: SMT402AD + deviceProfileName: thermostat-stelpro-profile - id: "Stelpro/ST218" deviceLabel: Stelpro Thermostat manufacturer: Stelpro From 1a90c02322b9bf3a0af385ead0e5a165e53fa046 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 4 Aug 2023 23:46:08 -0400 Subject: [PATCH 07/49] Update init.lua --- drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua b/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua index 5c9e580dec..35bca818b3 100644 --- a/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua +++ b/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua @@ -43,9 +43,9 @@ local STELPRO_THERMOSTAT_FINGERPRINTS = { local is_stelpro_thermostat = function(opts, driver, device) for _, fingerprint in ipairs(STELPRO_THERMOSTAT_FINGERPRINTS) do - if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then - return true - end + if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then + return true + end end return false end From 1e176c43e48579c9ff3129539fce426d2ad828fd Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 5 Aug 2023 21:52:55 -0400 Subject: [PATCH 08/49] Update fingerprints.yml Corrected indentation error --- drivers/SmartThings/zigbee-thermostat/fingerprints.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-thermostat/fingerprints.yml b/drivers/SmartThings/zigbee-thermostat/fingerprints.yml index 0d6640947b..34bc0d6dd1 100644 --- a/drivers/SmartThings/zigbee-thermostat/fingerprints.yml +++ b/drivers/SmartThings/zigbee-thermostat/fingerprints.yml @@ -69,7 +69,7 @@ zigbeeManufacturer: manufacturer: Stelpro model: STZB402+ deviceProfileName: thermostat-temperature-temperaturealarm - - id: "Stelpro/SMT402AD" + - id: "Stelpro/SMT402AD" deviceLabel: Stelpro Thermostat manufacturer: Stelpro model: SMT402AD From 9da09d43932a0abe009780f9059e83d7f35a1420 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 6 Oct 2023 23:08:39 -0400 Subject: [PATCH 09/49] Update init.lua --- .../zigbee-thermostat/src/stelpro/init.lua | 88 ++++++++++++++++--- 1 file changed, 75 insertions(+), 13 deletions(-) diff --git a/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua b/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua index 063f423517..6188ae72d7 100644 --- a/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua +++ b/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua @@ -14,11 +14,20 @@ local capabilities = require "st.capabilities" local clusters = require "st.zigbee.zcl.clusters" +local device_management = require "st.zigbee.device_management" +local cluster_base = require "st.zigbee.cluster_base" +local data_types = require "st.zigbee.data_types" +local RelativeHumidity = clusters.RelativeHumidity local Thermostat = clusters.Thermostat local ThermostatUserInterfaceConfiguration = clusters.ThermostatUserInterfaceConfiguration +local ThermostatMode = capabilities.thermostatMode local ThermostatOperatingState = capabilities.thermostatOperatingState +local ThermostatPower = capabilities.powerMeter + +local POWER_ATTTRIBUTE = 0x4008 +local MFG_CODE = 0x1185 local RX_FREEZE_VALUE = 0x7ffd local RX_HEAT_VALUE = 0x7fff @@ -28,18 +37,56 @@ local HEAT_ALRAM_TEMPERATURE = 50 local STELPRO_THERMOSTAT_FINGERPRINTS = { { mfr = "Stelpro", model = "MaestroStat" }, { mfr = "Stelpro", model = "SORB" }, - { mfr = "Stelpro", model = "SonomaStyle" } + { mfr = "Stelpro", model = "SonomaStyle" }, + { mfr = "Stelpro", model = "SMT402AD" } -- added M.Colmenarejo } local is_stelpro_thermostat = function(opts, driver, device) for _, fingerprint in ipairs(STELPRO_THERMOSTAT_FINGERPRINTS) do - if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then - return true - end + if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then + return true + end end return false end +local do_refresh = function(self, device) + local attributes = { + Thermostat.attributes.LocalTemperature, + Thermostat.attributes.PIHeatingDemand, + Thermostat.attributes.OccupiedHeatingSetpoint, + ThermostatUserInterfaceConfiguration.attributes.TemperatureDisplayMode, + ThermostatUserInterfaceConfiguration.attributes.KeypadLockout, + RelativeHumidity.attributes.MeasuredValue + } + for _, attribute in pairs(attributes) do + device:send(attribute:read(device)) + end + device:send(cluster_base.read_manufacturer_specific_attribute(device, Thermostat.ID, POWER_ATTTRIBUTE, MFG_CODE)) +end + +local device_added = function(self, device) + device:emit_event(capabilities.temperatureAlarm.temperatureAlarm.cleared()) + do_refresh(self, device) +end + +local do_configure = function(self, device) + device:send(Thermostat.attributes.LocalTemperature:configure_reporting(device, 10, 60, 50)) + device:send(Thermostat.attributes.OccupiedHeatingSetpoint:configure_reporting(device, 1, 600, 50)) + device:send(Thermostat.attributes.PIHeatingDemand:configure_reporting(device, 1, 60, 1)) + device:send(cluster_base.configure_reporting(device, data_types.ClusterId(Thermostat.ID) , POWER_ATTTRIBUTE, 0x23 , 1, 60, 1)) + + device:send(ThermostatUserInterfaceConfiguration.attributes.TemperatureDisplayMode:configure_reporting(device, 1, 0, 1)) + device:send(ThermostatUserInterfaceConfiguration.attributes.KeypadLockout:configure_reporting(device, 1, 0, 1)) + device:send(RelativeHumidity.attributes.MeasuredValue:configure_reporting(device, 10, 300, 1)) + + device:emit_event(ThermostatMode.thermostatMode.heat()) +end + +local device_init = function(self, device) + do_configure(self, device) +end + local function get_temperature(temperature) return temperature / 100 end @@ -73,11 +120,11 @@ local function thermostat_local_temp_attr_handler(driver, device, value, zb_rx) (last_alarm == "freeze" and temperature > FREEZE_ALRAM_TEMPERATURE) or (last_alarm == "heat" and temperature < HEAT_ALRAM_TEMPERATURE) ) then - if last_alarm == "freeze" then - event = capabilities.temperatureAlarm.temperatureAlarm.freeze() - else - event = capabilities.temperatureAlarm.temperatureAlarm.heat() - end + if last_alarm == "freeze" then + event = capabilities.temperatureAlarm.temperatureAlarm.freeze() + else + event = capabilities.temperatureAlarm.temperatureAlarm.heat() + end end else if temperature <= FREEZE_ALRAM_TEMPERATURE then @@ -98,21 +145,29 @@ local function thermostat_heating_set_point_attr_handler(driver, device, value, end local function thermostat_heating_demand_attr_handler(driver, device, value, zb_rx) - local event = value.value < 10 and ThermostatOperatingState.thermostatOperatingState.idle() or - ThermostatOperatingState.thermostatOperatingState.heating() + local event = value.value < 1 and ThermostatOperatingState.thermostatOperatingState.idle() or + ThermostatOperatingState.thermostatOperatingState.heating() device:emit_event(event) end +local function thermostat_power_attr_handler(driver, device, zb_rx) + local point_value = zb_rx.value + device:emit_event(capabilities.powerMeter.power({value = point_value, unit = "W"})) +end + local function info_changed(driver, device, event, args) if device.preferences ~= nil and device.preferences.lock ~= args.old_st_store.preferences.lock then device:send(ThermostatUserInterfaceConfiguration.attributes.KeypadLockout:write(device, tonumber(device.preferences.lock))) end end +<<<<<<< HEAD local device_added = function(self, device) device:emit_event(capabilities.temperatureAlarm.temperatureAlarm.cleared()) end +======= +>>>>>>> aae45d2 (Adding Power consumption measurment to stelpro device ) local stelpro_thermostat = { NAME = "Stelpro Thermostat Handler", zigbee_handlers = { @@ -121,15 +176,22 @@ local stelpro_thermostat = { [Thermostat.attributes.PIHeatingDemand.ID] = thermostat_heating_demand_attr_handler, [Thermostat.attributes.LocalTemperature.ID] = thermostat_local_temp_attr_handler, [Thermostat.attributes.OccupiedHeatingSetpoint.ID] = thermostat_heating_set_point_attr_handler, + [POWER_ATTTRIBUTE] = thermostat_power_attr_handler } } }, lifecycle_handlers = { + init = device_init, + doConfigure = do_configure, added = device_added, infoChanged = info_changed }, - sub_drivers = { require("stelpro.stelpro_sorb"), require("stelpro.stelpro_maestrostat") }, - can_handle = is_stelpro_thermostat + capability_handlers = { + [capabilities.refresh.ID] = { + [capabilities.refresh.commands.refresh.NAME] = do_refresh, + } + }, + can_handle = is_stelpro_thermostat, } return stelpro_thermostat From a03d971097027109aa379a14ff51d3f580092dca Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 6 Oct 2023 23:19:51 -0400 Subject: [PATCH 10/49] Update fingerprints.yml From e3f207a41d9ae9beb4549b5a4ec7b8da36542e85 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 6 Oct 2023 23:21:08 -0400 Subject: [PATCH 11/49] Update init.lua --- .../zigbee-thermostat/src/stelpro/init.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua b/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua index 35bca818b3..a6c46fb6a9 100644 --- a/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua +++ b/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua @@ -38,14 +38,14 @@ local STELPRO_THERMOSTAT_FINGERPRINTS = { { mfr = "Stelpro", model = "MaestroStat" }, { mfr = "Stelpro", model = "SORB" }, { mfr = "Stelpro", model = "SonomaStyle" }, - { mfr = "Stelpro", model = "SMT402AD" } + { mfr = "Stelpro", model = "SMT402AD" } -- added M.Colmenarejo } local is_stelpro_thermostat = function(opts, driver, device) for _, fingerprint in ipairs(STELPRO_THERMOSTAT_FINGERPRINTS) do - if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then - return true - end + if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then + return true + end end return false end @@ -161,6 +161,10 @@ local function info_changed(driver, device, event, args) end end +local device_added = function(self, device) + device:emit_event(capabilities.temperatureAlarm.temperatureAlarm.cleared()) +end + local stelpro_thermostat = { NAME = "Stelpro Thermostat Handler", zigbee_handlers = { From 06a6735cede63c2f00ec47da3bf7e7adb13f9575 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 6 Oct 2023 23:34:45 -0400 Subject: [PATCH 12/49] Delete drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_maestrostat.lua --- .../src/stelpro/stelpro_maestrostat.lua | 80 ------------------- 1 file changed, 80 deletions(-) delete mode 100644 drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_maestrostat.lua diff --git a/drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_maestrostat.lua b/drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_maestrostat.lua deleted file mode 100644 index c4d81b944a..0000000000 --- a/drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_maestrostat.lua +++ /dev/null @@ -1,80 +0,0 @@ --- Copyright 2022 SmartThings --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - -local capabilities = require "st.capabilities" -local clusters = require "st.zigbee.zcl.clusters" -local device_management = require "st.zigbee.device_management" - -local RelativeHumidity = clusters.RelativeHumidity -local Thermostat = clusters.Thermostat -local ThermostatUserInterfaceConfiguration = clusters.ThermostatUserInterfaceConfiguration - -local STELPRO_THERMOSTAT_FINGERPRINTS = { - { mfr = "Stelpro", model = "MaestroStat" }, -} - -local is_stelpro_thermostat = function(opts, driver, device) - for _, fingerprint in ipairs(STELPRO_THERMOSTAT_FINGERPRINTS) do - if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then - return true - end - end - return false -end - -local do_refresh = function(self, device) - local attributes = { - Thermostat.attributes.LocalTemperature, - Thermostat.attributes.PIHeatingDemand, - Thermostat.attributes.OccupiedHeatingSetpoint, - ThermostatUserInterfaceConfiguration.attributes.TemperatureDisplayMode, - ThermostatUserInterfaceConfiguration.attributes.KeypadLockout, - RelativeHumidity.attributes.MeasuredValue - } - for _, attribute in pairs(attributes) do - device:send(attribute:read(device)) - end -end - -local device_added = function(self, device) - device:emit_event(capabilities.temperatureAlarm.temperatureAlarm.cleared()) - do_refresh(self, device) -end - -local function do_configure(self, device) - device:send(device_management.build_bind_request(device, Thermostat.ID, self.environment_info.hub_zigbee_eui)) - device:send(Thermostat.attributes.LocalTemperature:configure_reporting(device, 10, 60, 50)) - device:send(Thermostat.attributes.OccupiedHeatingSetpoint:configure_reporting(device, 1, 600, 50)) - device:send(Thermostat.attributes.PIHeatingDemand:configure_reporting(device, 1, 3600, 1)) - - device:send(ThermostatUserInterfaceConfiguration.attributes.TemperatureDisplayMode:configure_reporting(device, 1, 0, 1)) - device:send(ThermostatUserInterfaceConfiguration.attributes.KeypadLockout:configure_reporting(device, 1, 0, 1)) - device:send(RelativeHumidity.attributes.MeasuredValue:configure_reporting(device, 10, 300, 1)) -end - -local stelpro_maestro_othermostat = { - NAME = "Stelpro Maestro Thermostat Handler", - capability_handlers = { - [capabilities.refresh.ID] = { - [capabilities.refresh.commands.refresh.NAME] = do_refresh, - } - }, - lifecycle_handlers = { - added = device_added, - doConfigure = do_configure - }, - can_handle = is_stelpro_thermostat -} - -return stelpro_maestro_othermostat From 623d89bec24d005c466583a0fc99b40c5bb61818 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 6 Oct 2023 23:34:52 -0400 Subject: [PATCH 13/49] Delete drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_sorb.lua --- .../src/stelpro/stelpro_sorb.lua | 81 ------------------- 1 file changed, 81 deletions(-) delete mode 100644 drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_sorb.lua diff --git a/drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_sorb.lua b/drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_sorb.lua deleted file mode 100644 index 504d4351e0..0000000000 --- a/drivers/SmartThings/zigbee-thermostat/src/stelpro/stelpro_sorb.lua +++ /dev/null @@ -1,81 +0,0 @@ --- Copyright 2022 SmartThings --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - -local capabilities = require "st.capabilities" -local clusters = require "st.zigbee.zcl.clusters" -local device_management = require "st.zigbee.device_management" - -local RelativeHumidity = clusters.RelativeHumidity -local Thermostat = clusters.Thermostat -local ThermostatUserInterfaceConfiguration = clusters.ThermostatUserInterfaceConfiguration - -local STELPRO_THERMOSTAT_FINGERPRINTS = { - { mfr = "Stelpro", model = "SORB" }, - { mfr = "Stelpro", model = "SonomaStyle" } -} - -local is_stelpro_sorb_thermostat = function(opts, driver, device) - for _, fingerprint in ipairs(STELPRO_THERMOSTAT_FINGERPRINTS) do - if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then - return true - end - end - return false -end - -local do_refresh = function(self, device) - local attributes = { - Thermostat.attributes.LocalTemperature, - Thermostat.attributes.PIHeatingDemand, - Thermostat.attributes.OccupiedHeatingSetpoint, - ThermostatUserInterfaceConfiguration.attributes.TemperatureDisplayMode, - ThermostatUserInterfaceConfiguration.attributes.KeypadLockout, - RelativeHumidity.attributes.MeasuredValue - } - for _, attribute in pairs(attributes) do - device:send(attribute:read(device)) - end -end - -local device_added = function(self, device) - device:emit_event(capabilities.temperatureAlarm.temperatureAlarm.cleared()) - do_refresh(self, device) -end - -local function do_configure(self, device) - device:send(device_management.build_bind_request(device, Thermostat.ID, self.environment_info.hub_zigbee_eui)) - device:send(Thermostat.attributes.LocalTemperature:configure_reporting(device, 10, 60, 50)) - device:send(Thermostat.attributes.OccupiedHeatingSetpoint:configure_reporting(device, 1, 600, 50)) - device:send(Thermostat.attributes.PIHeatingDemand:configure_reporting(device, 1, 3600, 1)) - - device:send(ThermostatUserInterfaceConfiguration.attributes.TemperatureDisplayMode:configure_reporting(device, 1, 0, 1)) - device:send(ThermostatUserInterfaceConfiguration.attributes.KeypadLockout:configure_reporting(device, 1, 0, 1)) - device:send(RelativeHumidity.attributes.MeasuredValue:configure_reporting(device, 10, 300, 1)) -end - -local stelpro_sorb_thermostat = { - NAME = "Stelpro SORB SonomaStyle Thermostat Handler", - capability_handlers = { - [capabilities.refresh.ID] = { - [capabilities.refresh.commands.refresh.NAME] = do_refresh, - } - }, - lifecycle_handlers = { - added = device_added, - doConfigure = do_configure - }, - can_handle = is_stelpro_sorb_thermostat -} - -return stelpro_sorb_thermostat From def353dea342a40ec38b80a3a5521d0cfa567767 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 6 Oct 2023 23:48:52 -0400 Subject: [PATCH 14/49] Update fingerprints.yml --- drivers/SmartThings/zigbee-motion-sensor/fingerprints.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/SmartThings/zigbee-motion-sensor/fingerprints.yml b/drivers/SmartThings/zigbee-motion-sensor/fingerprints.yml index 79b98bc247..301c2d8cf2 100644 --- a/drivers/SmartThings/zigbee-motion-sensor/fingerprints.yml +++ b/drivers/SmartThings/zigbee-motion-sensor/fingerprints.yml @@ -4,6 +4,11 @@ zigbeeManufacturer: manufacturer: LUMI model: lumi.motion.agl02 deviceProfileName: motion-illuminance-battery-aqara + - id: "LUMI/lumi.motion.ac02" + deviceLabel: Aqara Motion Sensor P1 + manufacturer: LUMI + model: lumi.motion.ac02 + deviceProfileName: motion-illuminance-battery-aqara - id: "LUMI/lumi.motion.agl04" deviceLabel: Aqara High Precision Motion Sensor manufacturer: LUMI From c1cfa856e5b265fbe993b8705d77145377ad7f98 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 6 Oct 2023 23:51:06 -0400 Subject: [PATCH 15/49] Update motion-illuminance-battery-aqara.yml --- .../profiles/motion-illuminance-battery-aqara.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/SmartThings/zigbee-motion-sensor/profiles/motion-illuminance-battery-aqara.yml b/drivers/SmartThings/zigbee-motion-sensor/profiles/motion-illuminance-battery-aqara.yml index ff58019d63..73ba1816e6 100644 --- a/drivers/SmartThings/zigbee-motion-sensor/profiles/motion-illuminance-battery-aqara.yml +++ b/drivers/SmartThings/zigbee-motion-sensor/profiles/motion-illuminance-battery-aqara.yml @@ -14,5 +14,9 @@ components: version: 1 - id: refresh version: 1 + - id: Voltage + version: 1 + - id: Signal Strenght + version: 1 categories: - name: MotionSensor From 435dbc65f7bc80c7d53f9baa70486a528d2c0fa6 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 6 Oct 2023 23:52:37 -0400 Subject: [PATCH 16/49] Update motion-illuminance-battery-aqara.yml --- .../profiles/motion-illuminance-battery-aqara.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-motion-sensor/profiles/motion-illuminance-battery-aqara.yml b/drivers/SmartThings/zigbee-motion-sensor/profiles/motion-illuminance-battery-aqara.yml index 73ba1816e6..23b3f027d1 100644 --- a/drivers/SmartThings/zigbee-motion-sensor/profiles/motion-illuminance-battery-aqara.yml +++ b/drivers/SmartThings/zigbee-motion-sensor/profiles/motion-illuminance-battery-aqara.yml @@ -6,9 +6,11 @@ components: version: 1 - id: illuminanceMeasurement version: 1 + - id: battery + version: 1 - id: stse.detectionFrequency version: 1 - - id: battery + - id: stse.sensitivityAdjustment version: 1 - id: firmwareUpdate version: 1 From c12d2cc9267609c834de311214eda73ae5ee7b64 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 7 Oct 2023 10:46:54 -0400 Subject: [PATCH 17/49] Update fingerprints.yml --- drivers/SmartThings/zigbee-thermostat/fingerprints.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-thermostat/fingerprints.yml b/drivers/SmartThings/zigbee-thermostat/fingerprints.yml index 82a1ad7860..0ddfe161fe 100644 --- a/drivers/SmartThings/zigbee-thermostat/fingerprints.yml +++ b/drivers/SmartThings/zigbee-thermostat/fingerprints.yml @@ -75,7 +75,7 @@ zigbeeManufacturer: model: STZB402+ deviceProfileName: thermostat-temperature-temperaturealarm - id: "Stelpro/SMT402AD" - deviceLabel: Stelpro Thermostat + deviceLabel: Stelpro Thermostat vX manufacturer: Stelpro model: SMT402AD deviceProfileName: thermostat-stelpro-profile From 2f93ca16428cafd1ac329119ae0cd18251c4420d Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 7 Oct 2023 11:15:42 -0400 Subject: [PATCH 18/49] commit --- drivers/SmartThings/zigbee-thermostat/fingerprints.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-thermostat/fingerprints.yml b/drivers/SmartThings/zigbee-thermostat/fingerprints.yml index 0ddfe161fe..82a1ad7860 100644 --- a/drivers/SmartThings/zigbee-thermostat/fingerprints.yml +++ b/drivers/SmartThings/zigbee-thermostat/fingerprints.yml @@ -75,7 +75,7 @@ zigbeeManufacturer: model: STZB402+ deviceProfileName: thermostat-temperature-temperaturealarm - id: "Stelpro/SMT402AD" - deviceLabel: Stelpro Thermostat vX + deviceLabel: Stelpro Thermostat manufacturer: Stelpro model: SMT402AD deviceProfileName: thermostat-stelpro-profile From 5471edfcd91c989a581ed5f6b1bf0ab36c78323b Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 7 Oct 2023 11:15:54 -0400 Subject: [PATCH 19/49] commit --- drivers/SmartThings/zigbee-thermostat/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-thermostat/config.yml b/drivers/SmartThings/zigbee-thermostat/config.yml index 8c0d6d79fd..f36485b693 100644 --- a/drivers/SmartThings/zigbee-thermostat/config.yml +++ b/drivers/SmartThings/zigbee-thermostat/config.yml @@ -1,4 +1,4 @@ -name: 'Zigbee Thermostat' +name: 'Zigbee Thermostat vX' defaultProfile: 'thermostat-battery-powerSource' packageKey: 'zigbee-thermostat' permissions: From 3d0d0cd01dd1c1271962c73f63b36d0abe3513c5 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 7 Oct 2023 11:34:04 -0400 Subject: [PATCH 20/49] removed bad headers --- drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua b/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua index 6188ae72d7..a6c46fb6a9 100644 --- a/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua +++ b/drivers/SmartThings/zigbee-thermostat/src/stelpro/init.lua @@ -161,13 +161,10 @@ local function info_changed(driver, device, event, args) end end -<<<<<<< HEAD local device_added = function(self, device) device:emit_event(capabilities.temperatureAlarm.temperatureAlarm.cleared()) end -======= ->>>>>>> aae45d2 (Adding Power consumption measurment to stelpro device ) local stelpro_thermostat = { NAME = "Stelpro Thermostat Handler", zigbee_handlers = { From 882e684cb263164acd513a193a77efda42251fbf Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 7 Oct 2023 12:00:50 -0400 Subject: [PATCH 21/49] aqara comit --- drivers/SmartThings/zigbee-motion-sensor/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-motion-sensor/config.yml b/drivers/SmartThings/zigbee-motion-sensor/config.yml index 209ccb2762..da33a7cb38 100644 --- a/drivers/SmartThings/zigbee-motion-sensor/config.yml +++ b/drivers/SmartThings/zigbee-motion-sensor/config.yml @@ -1,4 +1,4 @@ -name: 'Zigbee Motion Sensor' +name: 'Zigbee Motion Sensor vX' packageKey: 'zigbee-motion-sensor' permissions: zigbee: {} From 9cd7026ac5adcd2847c74708f554e1f1e626f8bc Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 7 Oct 2023 12:05:03 -0400 Subject: [PATCH 22/49] capabilities --- .../profiles/motion-illuminance-battery-aqara.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/SmartThings/zigbee-motion-sensor/profiles/motion-illuminance-battery-aqara.yml b/drivers/SmartThings/zigbee-motion-sensor/profiles/motion-illuminance-battery-aqara.yml index 23b3f027d1..4e5f20b2da 100644 --- a/drivers/SmartThings/zigbee-motion-sensor/profiles/motion-illuminance-battery-aqara.yml +++ b/drivers/SmartThings/zigbee-motion-sensor/profiles/motion-illuminance-battery-aqara.yml @@ -16,9 +16,9 @@ components: version: 1 - id: refresh version: 1 - - id: Voltage + - id: voltageMeasurement version: 1 - - id: Signal Strenght + - id: signalStrength version: 1 categories: - name: MotionSensor From 5df33994cf68a8a142d55ca33cd34b55e0e40cd5 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 7 Oct 2023 12:17:26 -0400 Subject: [PATCH 23/49] commit aqara P1 --- drivers/SmartThings/zigbee-motion-sensor/src/aqara/init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-motion-sensor/src/aqara/init.lua b/drivers/SmartThings/zigbee-motion-sensor/src/aqara/init.lua index 989e41655b..a08856fa69 100644 --- a/drivers/SmartThings/zigbee-motion-sensor/src/aqara/init.lua +++ b/drivers/SmartThings/zigbee-motion-sensor/src/aqara/init.lua @@ -18,7 +18,8 @@ local MOTION_DETECTED_UINT32 = 65536 local FINGERPRINTS = { { mfr = "LUMI", model = "lumi.motion.agl02" }, - { mfr = "LUMI", model = "lumi.motion.agl04" } + { mfr = "LUMI", model = "lumi.motion.agl04" }, + { mfr = "LUMI", model = "lumi.motion.ac02" } } local CONFIGURATIONS = { From 5780f0a5968d9aba174c04e107b638bceba787ab Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 5 Dec 2025 22:20:35 -0500 Subject: [PATCH 24/49] Add profile for sinope-switch-rm3500zb --- .../profiles/sinope-switch-rm3500zb | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 drivers/SmartThings/zigbee-switch/profiles/sinope-switch-rm3500zb diff --git a/drivers/SmartThings/zigbee-switch/profiles/sinope-switch-rm3500zb b/drivers/SmartThings/zigbee-switch/profiles/sinope-switch-rm3500zb new file mode 100644 index 0000000000..882f81440e --- /dev/null +++ b/drivers/SmartThings/zigbee-switch/profiles/sinope-switch-rm3500zb @@ -0,0 +1,26 @@ +name: sinope-switch-rm3500zb +components: + - id: main + capabilities: + - id: switch + version: 1 + - id: powerMeter + version: 1 + - id: voltageMeasurement + version: 1 + - id: currentMeter + version: 1 + - id: energyMeter + version: 1 + - id: temperatureMeasurement + version: 1 + - id: waterLeak + version: 1 + - id: thermostatCoolingSetpoint + version: 1 # For low water temp protection setpoint + - id: firmwareUpdate + version: 1 + - id: refresh + version: 1 + categories: + - name: SmartPlug From 50e117f839bf4c9d1001ba6d8bd028a48e392f44 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 5 Dec 2025 22:21:47 -0500 Subject: [PATCH 25/49] Update deviceProfileName for Sinope Load Controller --- drivers/SmartThings/zigbee-switch/fingerprints.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/SmartThings/zigbee-switch/fingerprints.yml b/drivers/SmartThings/zigbee-switch/fingerprints.yml index 4c91da651e..af25221e26 100644 --- a/drivers/SmartThings/zigbee-switch/fingerprints.yml +++ b/drivers/SmartThings/zigbee-switch/fingerprints.yml @@ -395,7 +395,7 @@ zigbeeManufacturer: deviceLabel: "RM3500ZB Sinope Load Controller" manufacturer: Sinope Technologies model: RM3500ZB - deviceProfileName: switch-power-smartplug + deviceProfileName: sinope-switch-rm3500zb - id: "Sinope Dimmer Switch/DM2500ZB" deviceLabel: "DM2500ZB Sinope Dimmer" manufacturer: Sinope Technologies @@ -2484,4 +2484,4 @@ zigbeeGeneric: - 0x0006 #OnOff - 0x0702 #Simple Metering - 0x0B04 #Electrical Measurment - deviceProfileName: switch-power \ No newline at end of file + deviceProfileName: switch-power From 7a0d4076b54eb4ded3a7fe4b85f051703d65167e Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 5 Dec 2025 22:37:51 -0500 Subject: [PATCH 26/49] Rename sinope-switch-rm3500zb to sinope-switch-rm3500zb.yml --- .../{sinope-switch-rm3500zb => sinope-switch-rm3500zb.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename drivers/SmartThings/zigbee-switch/profiles/{sinope-switch-rm3500zb => sinope-switch-rm3500zb.yml} (100%) diff --git a/drivers/SmartThings/zigbee-switch/profiles/sinope-switch-rm3500zb b/drivers/SmartThings/zigbee-switch/profiles/sinope-switch-rm3500zb.yml similarity index 100% rename from drivers/SmartThings/zigbee-switch/profiles/sinope-switch-rm3500zb rename to drivers/SmartThings/zigbee-switch/profiles/sinope-switch-rm3500zb.yml From fc124f2d642adbd3f5fb18c697bf35bfb9de823c Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 5 Dec 2025 23:22:17 -0500 Subject: [PATCH 27/49] Add temperature measurement handler to init.lua --- .../src/zigbee-switch-power/init.lua | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/init.lua b/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/init.lua index 0ea2224a6f..555ef75425 100644 --- a/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/init.lua @@ -7,6 +7,7 @@ local constants = require "st.zigbee.constants" local SimpleMetering = clusters.SimpleMetering local ElectricalMeasurement = clusters.ElectricalMeasurement +local TemperatureMeasurement = clusters.TemperatureMeasurement local function active_power_meter_handler(driver, device, value, zb_rx) local raw_value = value.value @@ -43,3 +44,24 @@ local zigbee_switch_power = { } return zigbee_switch_power + + +local function electrical_measurement_handler(driver, device, value, raw) + if value.attr_id == 0x0400 then -- Active Power + device:emit_event(capabilities.powerMeter.power({value = value.value, unit = "W"})) + elseif value.attr_id == 0x0505 then -- RMS Voltage + device:emit_event(capabilities.voltageMeasurement.voltage({value = value.value, unit = "V"})) + elseif value.attr_id == 0x0508 then -- RMS Current + device:emit_event(capabilities.currentMeter.current({value = value.value, unit = "A"})) + end +end + +local function metering_handler(driver, device, value, raw) + if value.attr_id == 0x0000 then -- Cumulative Energy + device:emit_event(capabilities.energyMeter.energy({value = value.value / 1000, unit = "kWh"})) + end +end + +local function temperature_handler(driver, device, value, raw) + device:emit_event(capabilities.temperatureMeasurement.temperature({value = value.value / 100, unit = "C"})) +end From c2d00c88e754c23644d01672107b0cb2f19fec1f Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 5 Dec 2025 23:39:15 -0500 Subject: [PATCH 28/49] Add handlers for RMS voltage, current, and metering --- .../src/zigbee-switch-power/init.lua | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/init.lua b/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/init.lua index 555ef75425..372610774b 100644 --- a/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/init.lua @@ -8,6 +8,7 @@ local constants = require "st.zigbee.constants" local SimpleMetering = clusters.SimpleMetering local ElectricalMeasurement = clusters.ElectricalMeasurement local TemperatureMeasurement = clusters.TemperatureMeasurement +local Metering = clusters.Metering local function active_power_meter_handler(driver, device, value, zb_rx) local raw_value = value.value @@ -27,15 +28,40 @@ local function instantaneous_demand_handler(driver, device, value, zb_rx) device:emit_event(capabilities.powerMeter.power({value = raw_value, unit = "W"})) end +local function rms_voltage_handler(driver, device, value, zb_rx) + device:emit_event(capabilities.voltageMeasurement.voltage({value = value.value, unit = "V"})) +end + +local function rms_current_handler(driver, device, value, zb_rx) + device:emit_event(capabilities.currentMeter.current({value = value.value, unit = "A"})) +end + +local function metering_handler(driver, device, value, zb_rx) + local raw_value = value.value / 1000 -- Convert Wh to kWh + device:emit_event(capabilities.energyMeter.energy({value = raw_value, unit = "kWh"})) +end + +local function temperature_handler(driver, device, value, zb_rx) + device:emit_event(capabilities.temperatureMeasurement.temperature({value = value.value / 100, unit = "C"})) +end + local zigbee_switch_power = { NAME = "Zigbee Switch Power", zigbee_handlers = { attr = { [ElectricalMeasurement.ID] = { - [ElectricalMeasurement.attributes.ActivePower.ID] = active_power_meter_handler + [ElectricalMeasurement.attributes.ActivePower.ID] = active_power_meter_handler, + [ElectricalMeasurement.attributes.RmsVoltage.ID] = rms_voltage_handler, + [ElectricalMeasurement.attributes.RmsCurrent.ID] = rms_current_handler }, [SimpleMetering.ID] = { [SimpleMetering.attributes.InstantaneousDemand.ID] = instantaneous_demand_handler + }, + [Metering.ID] = { + [Metering.attributes.CumulativeEnergy.ID] = metering_handler + }, + [TemperatureMeasurement.ID] = { + [TemperatureMeasurement.attributes.MeasuredValue.ID] = temperature_handler } } }, @@ -44,24 +70,3 @@ local zigbee_switch_power = { } return zigbee_switch_power - - -local function electrical_measurement_handler(driver, device, value, raw) - if value.attr_id == 0x0400 then -- Active Power - device:emit_event(capabilities.powerMeter.power({value = value.value, unit = "W"})) - elseif value.attr_id == 0x0505 then -- RMS Voltage - device:emit_event(capabilities.voltageMeasurement.voltage({value = value.value, unit = "V"})) - elseif value.attr_id == 0x0508 then -- RMS Current - device:emit_event(capabilities.currentMeter.current({value = value.value, unit = "A"})) - end -end - -local function metering_handler(driver, device, value, raw) - if value.attr_id == 0x0000 then -- Cumulative Energy - device:emit_event(capabilities.energyMeter.energy({value = value.value / 1000, unit = "kWh"})) - end -end - -local function temperature_handler(driver, device, value, raw) - device:emit_event(capabilities.temperatureMeasurement.temperature({value = value.value / 100, unit = "C"})) -end From a27425c748befa622c4b38d220f5bee673ab7138 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 6 Dec 2025 20:59:53 -0500 Subject: [PATCH 29/49] Rename profile to switch-power-waterheater and add capabilities --- .../profiles/switch-power-waterheater.yml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml diff --git a/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml b/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml new file mode 100644 index 0000000000..f1090a8ef3 --- /dev/null +++ b/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml @@ -0,0 +1,22 @@ +name: switch-power-waterheater +components: + - id: main + capabilities: + - id: switch + version: 1 + - id: powerMeter + version: 1 + - id: temperatureMeasurement + version: 1 + - id: waterSensor   + version: 1 + - id: voltageMeasurement  + version: 1 + - id: energyMeter + version: 1 + - id: firmwareUpdate + version: 1 + - id: refresh + version: 1 + categories: + - name: SmartPlug From c36116f26a07de4354ddb7a2153d20e69d0329c0 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 6 Dec 2025 21:00:40 -0500 Subject: [PATCH 30/49] Update deviceProfileName for RM3500ZB device --- drivers/SmartThings/zigbee-switch/fingerprints.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-switch/fingerprints.yml b/drivers/SmartThings/zigbee-switch/fingerprints.yml index af25221e26..7f52fa2b72 100644 --- a/drivers/SmartThings/zigbee-switch/fingerprints.yml +++ b/drivers/SmartThings/zigbee-switch/fingerprints.yml @@ -395,7 +395,7 @@ zigbeeManufacturer: deviceLabel: "RM3500ZB Sinope Load Controller" manufacturer: Sinope Technologies model: RM3500ZB - deviceProfileName: sinope-switch-rm3500zb + deviceProfileName: switch-power-waterheater - id: "Sinope Dimmer Switch/DM2500ZB" deviceLabel: "DM2500ZB Sinope Dimmer" manufacturer: Sinope Technologies From 60f4e02db00e455f63e05d32b70892d06501a62c Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 6 Dec 2025 21:03:42 -0500 Subject: [PATCH 31/49] Create Sinope-waterheater --- drivers/SmartThings/zigbee-switch/src/Sinope-waterheater | 1 + 1 file changed, 1 insertion(+) create mode 100644 drivers/SmartThings/zigbee-switch/src/Sinope-waterheater diff --git a/drivers/SmartThings/zigbee-switch/src/Sinope-waterheater b/drivers/SmartThings/zigbee-switch/src/Sinope-waterheater new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/drivers/SmartThings/zigbee-switch/src/Sinope-waterheater @@ -0,0 +1 @@ + From 27181215db9a472e86beb6c3e7c58d15c2ab98e8 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 6 Dec 2025 21:04:01 -0500 Subject: [PATCH 32/49] Delete drivers/SmartThings/zigbee-switch/src/Sinope-waterheater --- drivers/SmartThings/zigbee-switch/src/Sinope-waterheater | 1 - 1 file changed, 1 deletion(-) delete mode 100644 drivers/SmartThings/zigbee-switch/src/Sinope-waterheater diff --git a/drivers/SmartThings/zigbee-switch/src/Sinope-waterheater b/drivers/SmartThings/zigbee-switch/src/Sinope-waterheater deleted file mode 100644 index 8b13789179..0000000000 --- a/drivers/SmartThings/zigbee-switch/src/Sinope-waterheater +++ /dev/null @@ -1 +0,0 @@ - From dc5f98479a63a73839e4b60839215aa9091f0298 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 6 Dec 2025 21:05:44 -0500 Subject: [PATCH 33/49] Create init.lua --- .../SmartThings/zigbee-switch/src/sinope-waterheater/init.lua | 1 + 1 file changed, 1 insertion(+) create mode 100644 drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua diff --git a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua @@ -0,0 +1 @@ + From b0aa01e7183e9421cd0421a5cebe8a35385c7bbf Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 6 Dec 2025 21:08:00 -0500 Subject: [PATCH 34/49] Update device model check for Sinope water heater --- .../src/sinope-waterheater/can_handle.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 drivers/SmartThings/zigbee-switch/src/sinope-waterheater/can_handle.lua diff --git a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/can_handle.lua b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/can_handle.lua new file mode 100644 index 0000000000..2d215d1b36 --- /dev/null +++ b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/can_handle.lua @@ -0,0 +1,12 @@ +-- Copyright 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +return function(opts, driver, device, ...) + local can_handle = device:get_manufacturer() == "Sinope Technologies" and device:get_model() == "RM3500ZB" + if can_handle then + local subdriver = require("sinope-waterheater") + return true, subdriver + else + return false + end + end From e4e73d464d761cebb47b4d5e51b1632c7f22786b Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 6 Dec 2025 21:19:16 -0500 Subject: [PATCH 35/49] Update device capabilities in water heater profile --- .../profiles/switch-power-waterheater.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml b/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml index f1090a8ef3..1cf883b081 100644 --- a/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml +++ b/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml @@ -6,14 +6,18 @@ components: version: 1 - id: powerMeter version: 1 - - id: temperatureMeasurement - version: 1 - - id: waterSensor   + - id: voltageMeasurement version: 1 - - id: voltageMeasurement  + - id: currentMeter version: 1 - id: energyMeter version: 1 + - id: temperatureMeasurement + version: 1 + - id: waterLeak + version: 1 + - id: thermostatCoolingSetpoint + version: 1 # For low water temp protection setpoint - id: firmwareUpdate version: 1 - id: refresh From 31665dc926c521cc0a7d7257f7d8fa9c1907ed41 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 6 Dec 2025 21:19:41 -0500 Subject: [PATCH 36/49] Delete drivers/SmartThings/zigbee-switch/profiles/sinope-switch-rm3500zb.yml --- .../profiles/sinope-switch-rm3500zb.yml | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 drivers/SmartThings/zigbee-switch/profiles/sinope-switch-rm3500zb.yml diff --git a/drivers/SmartThings/zigbee-switch/profiles/sinope-switch-rm3500zb.yml b/drivers/SmartThings/zigbee-switch/profiles/sinope-switch-rm3500zb.yml deleted file mode 100644 index 882f81440e..0000000000 --- a/drivers/SmartThings/zigbee-switch/profiles/sinope-switch-rm3500zb.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: sinope-switch-rm3500zb -components: - - id: main - capabilities: - - id: switch - version: 1 - - id: powerMeter - version: 1 - - id: voltageMeasurement - version: 1 - - id: currentMeter - version: 1 - - id: energyMeter - version: 1 - - id: temperatureMeasurement - version: 1 - - id: waterLeak - version: 1 - - id: thermostatCoolingSetpoint - version: 1 # For low water temp protection setpoint - - id: firmwareUpdate - version: 1 - - id: refresh - version: 1 - categories: - - name: SmartPlug From 5cee9ceaa001e2291818c2fd687c0af48bac735a Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 6 Dec 2025 21:32:30 -0500 Subject: [PATCH 37/49] Add water and voltage sensor capabilities --- drivers/SmartThings/zigbee-switch/src/init.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/SmartThings/zigbee-switch/src/init.lua b/drivers/SmartThings/zigbee-switch/src/init.lua index 1b694d2a5e..438e623dc4 100644 --- a/drivers/SmartThings/zigbee-switch/src/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/init.lua @@ -68,6 +68,9 @@ local zigbee_switch_driver_template = { capabilities.illuminanceMeasurement, capabilities.relativeHumidityMeasurement, capabilities.temperatureMeasurement, + capabilities.waterSensor, + capabilities.voltageMeasurement, + }, sub_drivers = { lazy_load_if_possible("non_zigbee_devices"), @@ -78,6 +81,7 @@ local zigbee_switch_driver_template = { lazy_load_if_possible("rexense"), lazy_load_if_possible("sinope"), lazy_load_if_possible("sinope-dimmer"), + lazy_load_if_possible("sinope-waterheater"), lazy_load_if_possible("zigbee-dimmer-power-energy"), lazy_load_if_possible("zigbee-metering-plug-power-consumption-report"), lazy_load_if_possible("jasco"), @@ -99,6 +103,7 @@ local zigbee_switch_driver_template = { lazy_load_if_possible("laisiao"), lazy_load_if_possible("tuya-multi"), lazy_load_if_possible("frient") + }, zigbee_handlers = { global = { From c8fc2bed346c76e2a7e4e90d2485cdcf0b465651 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 6 Dec 2025 22:29:50 -0500 Subject: [PATCH 38/49] Update init.lua --- .../src/sinope-waterheater/init.lua | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua index 8b13789179..7a9a9c9553 100644 --- a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua @@ -1 +1,157 @@ +-- Copyright 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local cluster_base = require "st.zigbee.cluster_base" +local data_types = require "st.zigbee.data_types" +local capabilities = require "st.capabilities" +local clusters = require "st.zigbee.zcl.clusters" + +local SINOPE_SWITCH_CLUSTER = 0xFF01 +local SINOPE_MAX_INTENSITY_ON_ATTRIBUTE = 0x0052 +local SINOPE_MAX_INTENSITY_OFF_ATTRIBUTE = 0x0053 + +-- RM3500ZB additional manufacturer attributes (0xFF01)[web:45] +local SINOPE_CONNECTED_LOAD_ATTR = 0x0060 -- ConnectedLoad (W)[web:45] +local SINOPE_CURRENT_LOAD_ATTR = 0x0070 -- CurrentLoad (W-ish bitmap)[web:45] +local SINOPE_DR_MIN_WATER_TEMP_ATTR = 0x0076 -- drConfigWaterTempMin (°C)[web:45] +local SINOPE_DR_MIN_WATER_TEMP_TIME_ATTR = 0x0077 -- drConfigWaterTempTime[web:45] +local SINOPE_TIMER_ATTR = 0x00A0 -- Timer seconds[web:45] +local SINOPE_TIMER_COUNTDOWN_ATTR = 0x00A1 -- Timer_countDown[web:45] +local SINOPE_MIN_MEASURED_TEMP_ATTR = 0x007C -- min_measured_temp (°C×100)[web:45] +local SINOPE_MAX_MEASURED_TEMP_ATTR = 0x007D -- max_measured_temp (°C×100)[web:45] +local SINOPE_ENERGY_INTERNAL_ATTR = 0x0090 -- currentSummationDelivered (internal)[web:45] + +local OnOff = clusters.OnOff -- 0x0006 +local TempMeas = clusters.TemperatureMeasurement -- 0x0402 +local IASZone = clusters.IASZone -- 0x0500 +local ElectricalMeasurement = clusters.ElectricalMeasurement -- 0x0B04 +local Metering = clusters.Metering -- 0x0702 + + + +local function info_changed(driver, device, event, args) + -- handle ledIntensity preference setting + if (args.old_st_store.preferences.ledIntensity ~= device.preferences.ledIntensity) then + local ledIntensity = device.preferences.ledIntensity + + device:send(cluster_base.write_attribute(device, + data_types.ClusterId(SINOPE_SWITCH_CLUSTER), + data_types.AttributeId(SINOPE_MAX_INTENSITY_ON_ATTRIBUTE), + data_types.validate_or_build_type(ledIntensity, data_types.Uint8, "payload"))) + device:send(cluster_base.write_attribute(device, + data_types.ClusterId(SINOPE_SWITCH_CLUSTER), + data_types.AttributeId(SINOPE_MAX_INTENSITY_OFF_ATTRIBUTE), + data_types.validate_or_build_type(ledIntensity, data_types.Uint8, "payload"))) + + end + -- Example: DR minimum water temperature preference + if args.old_st_store.preferences.drMinWaterTemp ~= device.preferences.drMinWaterTemp then + local minTemp = device.preferences.drMinWaterTemp -- in °C + device:send(cluster_base.write_attribute( + device, + data_types.ClusterId(SINOPE_SWITCH_CLUSTER), + data_types.AttributeId(SINOPE_DR_MIN_WATER_TEMP_ATTR), + data_types.validate_or_build_type(minTemp, data_types.Uint8, "payload") + )) + end + +end + +local zigbee_sinope_switch = { + NAME = "Zigbee Sinope switch", + lifecycle_handlers = { + infoChanged = info_changed + }, + + zigbee_handlers = { + attr = { + -- Standard clusters used by RM3500ZB[web:41][web:45] + [OnOff.ID] = { + [OnOff.attributes.OnOff.ID] = onoff_attr_handler + }, + [TempMeas.ID] = { + [TempMeas.attributes.MeasuredValue.ID] = water_temp_attr_handler + }, + [IASZone.ID] = { + [IASZone.attributes.ZoneStatus.ID] = leak_attr_handler + }, + [ElectricalMeasurement.ID] = { + [ElectricalMeasurement.attributes.ActivePower.ID] = active_power_attr_handler + }, + [Metering.ID] = { + [Metering.attributes.CurrentSummationDelivered.ID] = energy_attr_handler + }, + + -- Optional: manufacturer-specific 0xFF01 attributes for RM3500ZB[web:45] + [SINOPE_SWITCH_CLUSTER] = { + [SINOPE_CONNECTED_LOAD_ATTR] = sinope_connected_load_handler, + [SINOPE_MIN_MEASURED_TEMP_ATTR] = sinope_min_temp_handler, + [SINOPE_MAX_MEASURED_TEMP_ATTR] = sinope_max_temp_handler, + -- add more as needed (Timer, DR min temp, etc.) + } + } + }, + + capability_handlers = { + [capabilities.switch.ID] = { + [capabilities.switch.commands.on.NAME] = switch_on_handler, + [capabilities.switch.commands.off.NAME] = switch_off_handler, + -- Add mappings for any custom capabilities you define for DR/timer/load here + } + }, + can_handle = require("sinope.can_handle"), +} + +return zigbee_sinope_switch + +-- Switch on/off from 0x0006/0x0000[web:45] +local function onoff_attr_handler(driver, device, value, zb_rx) + if value.value then + device:emit_event(capabilities.switch.switch.on()) + else + device:emit_event(capabilities.switch.switch.off()) + end +end + +-- Water temperature from 0x0402/0x0000 (°C×100)[web:45] +local function water_temp_attr_handler(driver, device, value, zb_rx) + local temp_c = value.value / 100.0 + device:emit_event(capabilities.temperatureMeasurement.temperature({ value = temp_c, unit = "C" })) +end + +-- Leak state from IAS Zone 0x0500/0x0002[web:45] +local function leak_attr_handler(driver, device, value, zb_rx) + local zone_status = value.value + local wet = (bit32.band(zone_status, 0x0001) ~= 0) -- example mask + if wet then + device:emit_event(capabilities.waterSensor.water.wet()) + else + device:emit_event(capabilities.waterSensor.water.dry()) + end +end + +-- Active power from 0x0B04/0x050B[web:45] +local function active_power_attr_handler(driver, device, value, zb_rx) + local power_w = value.value -- apply multiplier/divisor if needed + device:emit_event(capabilities.powerMeter.power({ value = power_w, unit = "W" })) +end + +-- Energy from 0x0702/0x0000 (Wh or kWh with multiplier/divisor)[web:41][web:45] +local function energy_attr_handler(driver, device, value, zb_rx) + local raw = value.value + -- If you read AC Power Multiplier/Divisor, apply here; otherwise assume Wh and convert + local kwh = raw / 1000.0 + device:emit_event(capabilities.energyMeter.energy({ value = kwh, unit = "kWh" })) +end + +local function sinope_min_temp_handler(driver, device, value, zb_rx) + local temp_c = value.value / 100.0 + -- device:emit_event(capabilities["yourNamespace.minTankTemp"].temperature({ value = temp_c, unit = "C" })) +end + +local function sinope_max_temp_handler(driver, device, value, zb_rx) + local temp_c = value.value / 100.0 + -- device:emit_event(capabilities["yourNamespace.maxTankTemp"].temperature({ value = temp_c, unit = "C" })) +end + From 3b375f5ff4d866be71af902b1ee56579f4a2a774 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 2 Jan 2026 21:07:33 -0500 Subject: [PATCH 39/49] Jan2 --- .../profiles/switch-power-waterheater.yml | 4 +- .../src/sinope-waterheater/init.lua | 232 +++++++++++++----- 2 files changed, 171 insertions(+), 65 deletions(-) diff --git a/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml b/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml index 1cf883b081..a9901d0588 100644 --- a/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml +++ b/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml @@ -17,10 +17,10 @@ components: - id: waterLeak version: 1 - id: thermostatCoolingSetpoint - version: 1 # For low water temp protection setpoint + version: 1 - id: firmwareUpdate version: 1 - id: refresh version: 1 categories: - - name: SmartPlug + - name: SmartPlug \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua index 7a9a9c9553..d522439f59 100644 --- a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua @@ -4,13 +4,14 @@ local cluster_base = require "st.zigbee.cluster_base" local data_types = require "st.zigbee.data_types" local capabilities = require "st.capabilities" +local ZigbeeDriver = require "st.zigbee" +local defaults = require "st.zigbee.defaults" local clusters = require "st.zigbee.zcl.clusters" +-- Sinopé manufacturer cluster and attributes local SINOPE_SWITCH_CLUSTER = 0xFF01 local SINOPE_MAX_INTENSITY_ON_ATTRIBUTE = 0x0052 local SINOPE_MAX_INTENSITY_OFF_ATTRIBUTE = 0x0053 - --- RM3500ZB additional manufacturer attributes (0xFF01)[web:45] local SINOPE_CONNECTED_LOAD_ATTR = 0x0060 -- ConnectedLoad (W)[web:45] local SINOPE_CURRENT_LOAD_ATTR = 0x0070 -- CurrentLoad (W-ish bitmap)[web:45] local SINOPE_DR_MIN_WATER_TEMP_ATTR = 0x0076 -- drConfigWaterTempMin (°C)[web:45] @@ -21,13 +22,157 @@ local SINOPE_MIN_MEASURED_TEMP_ATTR = 0x007C -- min_measured_temp (°C×1 local SINOPE_MAX_MEASURED_TEMP_ATTR = 0x007D -- max_measured_temp (°C×100)[web:45] local SINOPE_ENERGY_INTERNAL_ATTR = 0x0090 -- currentSummationDelivered (internal)[web:45] +-- Standard Zigbee clusters local OnOff = clusters.OnOff -- 0x0006 local TempMeas = clusters.TemperatureMeasurement -- 0x0402 local IASZone = clusters.IASZone -- 0x0500 local ElectricalMeasurement = clusters.ElectricalMeasurement -- 0x0B04 local Metering = clusters.Metering -- 0x0702 +local Thermostat = clusters.Thermostat + +-- Switch on/off from 0x0006/0x0000 +local function onoff_attr_handler(driver, device, value, zb_rx) + if value.value then + device:emit_event(capabilities.switch.switch.on()) + else + device:emit_event(capabilities.switch.switch.off()) + end +end +-- Water temperature from cluster 0x0402, attribute 0x0000 (°C×100) +local function water_temp_attr_handler(driver, device, value, zb_rx) + local temp_c = value.value / 100.0 + device:emit_event(capabilities.temperatureMeasurement.temperature({ + value = temp_c, + unit = "C" + })) +end + +-- Leak state from IAS Zone 0x0500/0x0002[web:45] +local function leak_attr_handler(driver, device, value, zb_rx) + local zone_status = value.value + local wet = (bit32.band(zone_status, 0x0001) ~= 0) -- example mask + if wet then + device:emit_event(capabilities.waterLeak.water.wet()) + else + device:emit_event(capabilities.waterLeak.water.dry()) + end +end + +-- Active power from 0x0B04/0x050B[web:45] +local function active_power_attr_handler(driver, device, value, zb_rx) + local power_w = value.value -- apply multiplier/divisor if needed + device:emit_event(capabilities.powerMeter.power({ value = power_w, unit = "W" })) +end + +-- Energy from 0x0702/0x0000 (Wh or kWh with multiplier/divisor)[web:41][web:45] +local function energy_attr_handler(driver, device, value, zb_rx) + local raw = value.value + -- If you read AC Power Multiplier/Divisor, apply here; otherwise assume Wh and convert + local kwh = raw / 1000.0 + device:emit_event(capabilities.energyMeter.energy({ value = kwh, unit = "kWh" })) + +end + +-- Voltage from Electrical Measurement cluster 0x0B04, attribute 0x0505 +local function voltage_attr_handler(driver, device, value, zb_rx) + local voltage_raw = value.value + -- Device sends in 0.1V increments + local voltage_v = math.floor((voltage_raw / 10) + 0.5) + device:emit_event(capabilities.voltageMeasurement.voltage({ + value = voltage_v, + unit = "V" + })) +end + +-- Current from Electrical Measurement cluster 0x0B04, attribute 0x0508 +local function current_attr_handler(driver, device, value, zb_rx) + local current_raw = value.value + -- Device sends in 0.001A (1mA) increments + local current_a = math.floor((current_raw / 1000) + 0.5) + device:emit_event(capabilities.currentMeter.current({ + value = current_a, + unit = "A" + })) +end + +-- Cooling setpoint from Thermostat cluster 0x0201, attribute 0x0012 +local function cooling_setpoint_attr_handler(driver, device, value, zb_rx) + local setpoint_raw = value.value or 4600 -- Default 46°C + local setpoint_c = setpoint_raw / 100 + device:emit_event(capabilities.thermostatCoolingSetpoint.coolingSetpoint({ + value = math.floor(setpoint_c + 0.5), + unit = "C" + })) +end + +-- Manufacturer-specific: Connected load +local function sinope_connected_load_handler(driver, device, value, zb_rx) + local load_w = value.value + -- You could store this or emit a custom capability event + device:set_field("connected_load", load_w, {persist = true}) +end + +local function sinope_min_temp_handler(driver, device, value, zb_rx) + local temp_c = value.value / 100.0 + -- device:emit_event(capabilities["yourNamespace.minTankTemp"].temperature({ value = temp_c, unit = "C" })) + -- device:set_field("min_measured_temp", temp_c, {persist = true}) +end + +local function sinope_max_temp_handler(driver, device, value, zb_rx) + local temp_c = value.value / 100.0 + -- device:emit_event(capabilities["yourNamespace.maxTankTemp"].temperature({ value = temp_c, unit = "C" })) + -- device:set_field("max_measured_temp", temp_c, {persist = true}) +end +-- ============================================================================= +-- COMMAND HANDLERS +-- ============================================================================= + +-- Handle switch.on command +local function switch_on_handler(driver, device, command) + device:send(OnOff.server.commands.On(device)) +end + +-- Handle switch.off command +local function switch_off_handler(driver, device, command) + device:send(OnOff.server.commands.Off(device)) +end + +-- Handle thermostatCoolingSetpoint.setCoolingSetpoint command +local function set_cooling_setpoint_handler(driver, device, command) + local setpoint_c = command.args.setpoint or 46 + + -- Validate range (RM3500ZB supports 46-55°C) + if setpoint_c < 46 then setpoint_c = 46 end + if setpoint_c > 55 then setpoint_c = 55 end + + local zigbee_value = setpoint_c * 100 + + -- Get current setpoint to calculate delta + local current_setpoint = device:get_field("current_cooling_setpoint") or (46 * 100) + local delta = zigbee_value - current_setpoint + + device:send(Thermostat.server.commands.SetpointRaiseLower(device, 0x02, delta)) + device:set_field("current_cooling_setpoint", zigbee_value, {persist = true}) +end + +-- Handle refresh command +local function refresh_handler(driver, device, command) + -- Read all important attributes + device:send(OnOff.attributes.OnOff:read(device)) + device:send(TempMeas.attributes.MeasuredValue:read(device)) + device:send(Metering.attributes.InstantaneousDemand:read(device)) + device:send(Metering.attributes.CurrentSummationDelivered:read(device)) + device:send(ElectricalMeasurement.attributes.RMSVoltage:read(device)) + device:send(ElectricalMeasurement.attributes.RMSCurrent:read(device)) + device:send(Thermostat.attributes.CoolingSetpoint:read(device)) + device:send(IASZone.attributes.ZoneStatus:read(device)) +end + +-- ============================================================================= +-- PREFERENCE CHANGE HANDLER +-- ============================================================================= local function info_changed(driver, device, event, args) -- handle ledIntensity preference setting @@ -42,7 +187,6 @@ local function info_changed(driver, device, event, args) data_types.ClusterId(SINOPE_SWITCH_CLUSTER), data_types.AttributeId(SINOPE_MAX_INTENSITY_OFF_ATTRIBUTE), data_types.validate_or_build_type(ledIntensity, data_types.Uint8, "payload"))) - end -- Example: DR minimum water temperature preference if args.old_st_store.preferences.drMinWaterTemp ~= device.preferences.drMinWaterTemp then @@ -54,9 +198,12 @@ local function info_changed(driver, device, event, args) data_types.validate_or_build_type(minTemp, data_types.Uint8, "payload") )) end - end +-- ============================================================================= +-- DRIVER DEFINITION (NOW handlers are defined above and can be referenced) +-- ============================================================================= + local zigbee_sinope_switch = { NAME = "Zigbee Sinope switch", lifecycle_handlers = { @@ -65,7 +212,7 @@ local zigbee_sinope_switch = { zigbee_handlers = { attr = { - -- Standard clusters used by RM3500ZB[web:41][web:45] + -- Standard clusters used by RM3500ZB [OnOff.ID] = { [OnOff.attributes.OnOff.ID] = onoff_attr_handler }, @@ -76,15 +223,20 @@ local zigbee_sinope_switch = { [IASZone.attributes.ZoneStatus.ID] = leak_attr_handler }, [ElectricalMeasurement.ID] = { - [ElectricalMeasurement.attributes.ActivePower.ID] = active_power_attr_handler + [ElectricalMeasurement.attributes.RMSVoltage.ID] = voltage_attr_handler, + [ElectricalMeasurement.attributes.RMSCurrent.ID] = current_attr_handler, }, [Metering.ID] = { - [Metering.attributes.CurrentSummationDelivered.ID] = energy_attr_handler + [Metering.attributes.InstantaneousDemand.ID] = power_attr_handler, + [Metering.attributes.CurrentSummationDelivered.ID] = energy_attr_handler, + }, + [Thermostat.ID] = { + [Thermostat.attributes.CoolingSetpoint.ID] = cooling_setpoint_attr_handler, }, -- Optional: manufacturer-specific 0xFF01 attributes for RM3500ZB[web:45] [SINOPE_SWITCH_CLUSTER] = { - [SINOPE_CONNECTED_LOAD_ATTR] = sinope_connected_load_handler, + [SINOPE_CONNECTED_LOAD_ATTR] = sinope_connected_load_handler, [SINOPE_MIN_MEASURED_TEMP_ATTR] = sinope_min_temp_handler, [SINOPE_MAX_MEASURED_TEMP_ATTR] = sinope_max_temp_handler, -- add more as needed (Timer, DR min temp, etc.) @@ -94,64 +246,18 @@ local zigbee_sinope_switch = { capability_handlers = { [capabilities.switch.ID] = { - [capabilities.switch.commands.on.NAME] = switch_on_handler, + [capabilities.switch.commands.on.NAME] = switch_on_handler, [capabilities.switch.commands.off.NAME] = switch_off_handler, -- Add mappings for any custom capabilities you define for DR/timer/load here - } }, + [capabilities.thermostatCoolingSetpoint.ID] = { + [capabilities.thermostatCoolingSetpoint.commands.setCoolingSetpoint.NAME] = set_cooling_setpoint_handler, + }, + [capabilities.refresh.ID] = { + [capabilities.refresh.commands.refresh.NAME] = refresh_handler, + }, +}, can_handle = require("sinope.can_handle"), } -return zigbee_sinope_switch - --- Switch on/off from 0x0006/0x0000[web:45] -local function onoff_attr_handler(driver, device, value, zb_rx) - if value.value then - device:emit_event(capabilities.switch.switch.on()) - else - device:emit_event(capabilities.switch.switch.off()) - end -end - --- Water temperature from 0x0402/0x0000 (°C×100)[web:45] -local function water_temp_attr_handler(driver, device, value, zb_rx) - local temp_c = value.value / 100.0 - device:emit_event(capabilities.temperatureMeasurement.temperature({ value = temp_c, unit = "C" })) -end - --- Leak state from IAS Zone 0x0500/0x0002[web:45] -local function leak_attr_handler(driver, device, value, zb_rx) - local zone_status = value.value - local wet = (bit32.band(zone_status, 0x0001) ~= 0) -- example mask - if wet then - device:emit_event(capabilities.waterSensor.water.wet()) - else - device:emit_event(capabilities.waterSensor.water.dry()) - end -end - --- Active power from 0x0B04/0x050B[web:45] -local function active_power_attr_handler(driver, device, value, zb_rx) - local power_w = value.value -- apply multiplier/divisor if needed - device:emit_event(capabilities.powerMeter.power({ value = power_w, unit = "W" })) -end - --- Energy from 0x0702/0x0000 (Wh or kWh with multiplier/divisor)[web:41][web:45] -local function energy_attr_handler(driver, device, value, zb_rx) - local raw = value.value - -- If you read AC Power Multiplier/Divisor, apply here; otherwise assume Wh and convert - local kwh = raw / 1000.0 - device:emit_event(capabilities.energyMeter.energy({ value = kwh, unit = "kWh" })) -end - -local function sinope_min_temp_handler(driver, device, value, zb_rx) - local temp_c = value.value / 100.0 - -- device:emit_event(capabilities["yourNamespace.minTankTemp"].temperature({ value = temp_c, unit = "C" })) -end - -local function sinope_max_temp_handler(driver, device, value, zb_rx) - local temp_c = value.value / 100.0 - -- device:emit_event(capabilities["yourNamespace.maxTankTemp"].temperature({ value = temp_c, unit = "C" })) -end - - +return zigbee_sinope_switch \ No newline at end of file From d62e1250046bf796b400a448ed0f5ee0f4a5f5e4 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 3 Jan 2026 13:19:03 -0500 Subject: [PATCH 40/49] Fixed Sub-driver structure --- drivers/SmartThings/zigbee-switch/config.yml | 2 +- .../profiles/switch-power-waterheater.yml | 6 +- .../SmartThings/zigbee-switch/src/init.lua | 2 +- .../src/sinope-waterheater/can_handle.lua | 20 +- .../src/sinope-waterheater/init.lua | 345 ++++++++---------- .../src/zigbee-switch-power/init.lua | 15 +- 6 files changed, 178 insertions(+), 212 deletions(-) diff --git a/drivers/SmartThings/zigbee-switch/config.yml b/drivers/SmartThings/zigbee-switch/config.yml index 32ad44a36e..74e58c52da 100644 --- a/drivers/SmartThings/zigbee-switch/config.yml +++ b/drivers/SmartThings/zigbee-switch/config.yml @@ -1,4 +1,4 @@ -name: 'Zigbee Switch' +name: 'Zigbee Switch Vx' packageKey: 'zigbee-switch' permissions: zigbee: {} diff --git a/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml b/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml index a9901d0588..327a3dca2b 100644 --- a/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml +++ b/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml @@ -8,13 +8,13 @@ components: version: 1 - id: voltageMeasurement version: 1 - - id: currentMeter - version: 1 + # - id: currentMeter + # version: 1 - id: energyMeter version: 1 - id: temperatureMeasurement version: 1 - - id: waterLeak + - id: waterSensor version: 1 - id: thermostatCoolingSetpoint version: 1 diff --git a/drivers/SmartThings/zigbee-switch/src/init.lua b/drivers/SmartThings/zigbee-switch/src/init.lua index 438e623dc4..cc2c6a45ec 100644 --- a/drivers/SmartThings/zigbee-switch/src/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/init.lua @@ -81,7 +81,7 @@ local zigbee_switch_driver_template = { lazy_load_if_possible("rexense"), lazy_load_if_possible("sinope"), lazy_load_if_possible("sinope-dimmer"), - lazy_load_if_possible("sinope-waterheater"), + require("sinope-waterheater"), lazy_load_if_possible("zigbee-dimmer-power-energy"), lazy_load_if_possible("zigbee-metering-plug-power-consumption-report"), lazy_load_if_possible("jasco"), diff --git a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/can_handle.lua b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/can_handle.lua index 2d215d1b36..4a1b216130 100644 --- a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/can_handle.lua +++ b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/can_handle.lua @@ -1,12 +1,16 @@ -- Copyright 2025 SmartThings, Inc. -- Licensed under the Apache License, Version 2.0 -return function(opts, driver, device, ...) - local can_handle = device:get_manufacturer() == "Sinope Technologies" and device:get_model() == "RM3500ZB" - if can_handle then - local subdriver = require("sinope-waterheater") - return true, subdriver - else - return false - end +local function can_handle_sinope_rm3500zb(opts, driver, device, ...) + if device ~= nil then + local manufacturer = device:get_manufacturer() + local model = device:get_model() + + -- Check if this is an RM3500ZB + return manufacturer == "Sinope Technologies" and model == "RM3500ZB" end + + return false +end + +return can_handle_sinope_rm3500zb \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua index d522439f59..8a23dd3f4b 100644 --- a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua @@ -7,6 +7,7 @@ local capabilities = require "st.capabilities" local ZigbeeDriver = require "st.zigbee" local defaults = require "st.zigbee.defaults" local clusters = require "st.zigbee.zcl.clusters" +local log = require "log" -- Sinopé manufacturer cluster and attributes local SINOPE_SWITCH_CLUSTER = 0xFF01 @@ -23,241 +24,201 @@ local SINOPE_MAX_MEASURED_TEMP_ATTR = 0x007D -- max_measured_temp (°C×1 local SINOPE_ENERGY_INTERNAL_ATTR = 0x0090 -- currentSummationDelivered (internal)[web:45] -- Standard Zigbee clusters +local SimpleMetering = clusters.SimpleMetering -- 0x0702 local OnOff = clusters.OnOff -- 0x0006 -local TempMeas = clusters.TemperatureMeasurement -- 0x0402 +local TemperatureMeasurement = clusters.TemperatureMeasurement -- 0x0402 local IASZone = clusters.IASZone -- 0x0500 local ElectricalMeasurement = clusters.ElectricalMeasurement -- 0x0B04 -local Metering = clusters.Metering -- 0x0702 -local Thermostat = clusters.Thermostat - - --- Switch on/off from 0x0006/0x0000 -local function onoff_attr_handler(driver, device, value, zb_rx) - if value.value then - device:emit_event(capabilities.switch.switch.on()) - else - device:emit_event(capabilities.switch.switch.off()) - end -end - --- Water temperature from cluster 0x0402, attribute 0x0000 (°C×100) -local function water_temp_attr_handler(driver, device, value, zb_rx) - local temp_c = value.value / 100.0 - device:emit_event(capabilities.temperatureMeasurement.temperature({ - value = temp_c, - unit = "C" - })) -end +local Thermostat = clusters.Thermostat -- Leak state from IAS Zone 0x0500/0x0002[web:45] local function leak_attr_handler(driver, device, value, zb_rx) + log.info("========================================") + log.info("RM3500ZB LEAK HANDLER CALLED") local zone_status = value.value local wet = (bit32.band(zone_status, 0x0001) ~= 0) -- example mask if wet then - device:emit_event(capabilities.waterLeak.water.wet()) + device:emit_event(capabilities.waterSensor.water.wet()) else - device:emit_event(capabilities.waterLeak.water.dry()) + device:emit_event(capabilities.waterSensor.water.dry()) end end --- Active power from 0x0B04/0x050B[web:45] -local function active_power_attr_handler(driver, device, value, zb_rx) - local power_w = value.value -- apply multiplier/divisor if needed - device:emit_event(capabilities.powerMeter.power({ value = power_w, unit = "W" })) -end +-- -- Active power from 0x0B04/0x050B[web:45] +-- local function active_power_meter_handler(driver, device, value, zb_rx) +-- log.info("========================================") +-- log.info("RM3500ZB POWERMETER HANDLER CALLED") +-- local raw_value = value.value +-- local divisor = device:get_field(constants.ELECTRICAL_MEASUREMENT_DIVISOR_KEY) or 10 --- Energy from 0x0702/0x0000 (Wh or kWh with multiplier/divisor)[web:41][web:45] -local function energy_attr_handler(driver, device, value, zb_rx) - local raw = value.value - -- If you read AC Power Multiplier/Divisor, apply here; otherwise assume Wh and convert - local kwh = raw / 1000.0 - device:emit_event(capabilities.energyMeter.energy({ value = kwh, unit = "kWh" })) - -end +-- raw_value = raw_value / divisor --- Voltage from Electrical Measurement cluster 0x0B04, attribute 0x0505 -local function voltage_attr_handler(driver, device, value, zb_rx) - local voltage_raw = value.value - -- Device sends in 0.1V increments - local voltage_v = math.floor((voltage_raw / 10) + 0.5) - device:emit_event(capabilities.voltageMeasurement.voltage({ - value = voltage_v, - unit = "V" - })) -end +-- device:emit_event(capabilities.powerMeter.power({value = raw_value, unit = "W"})) +-- end --- Current from Electrical Measurement cluster 0x0B04, attribute 0x0508 -local function current_attr_handler(driver, device, value, zb_rx) - local current_raw = value.value - -- Device sends in 0.001A (1mA) increments - local current_a = math.floor((current_raw / 1000) + 0.5) - device:emit_event(capabilities.currentMeter.current({ - value = current_a, - unit = "A" - })) -end +local function instantaneous_demand_handler(driver, device, value, zb_rx) + log.info("========================================") + log.info("RM3500ZB INSTANTMETER HANDLER CALLED") + local raw_value = value.value + local divisor = device:get_field(constants.SIMPLE_METERING_DIVISOR_KEY) or 10 --- Cooling setpoint from Thermostat cluster 0x0201, attribute 0x0012 -local function cooling_setpoint_attr_handler(driver, device, value, zb_rx) - local setpoint_raw = value.value or 4600 -- Default 46°C - local setpoint_c = setpoint_raw / 100 - device:emit_event(capabilities.thermostatCoolingSetpoint.coolingSetpoint({ - value = math.floor(setpoint_c + 0.5), - unit = "C" - })) -end + raw_value = raw_value / divisor --- Manufacturer-specific: Connected load -local function sinope_connected_load_handler(driver, device, value, zb_rx) - local load_w = value.value - -- You could store this or emit a custom capability event - device:set_field("connected_load", load_w, {persist = true}) + device:emit_event(capabilities.powerMeter.power({value = raw_value, unit = "W"})) end - -local function sinope_min_temp_handler(driver, device, value, zb_rx) - local temp_c = value.value / 100.0 - -- device:emit_event(capabilities["yourNamespace.minTankTemp"].temperature({ value = temp_c, unit = "C" })) - -- device:set_field("min_measured_temp", temp_c, {persist = true}) +local function rms_voltage_handler(driver, device, value, zb_rx) + log.info("========================================") + log.info("RM3500ZB RMSVOLTS HANDLER CALLED") + device:emit_event(capabilities.voltageMeasurement.voltage({value = value.value, unit = "V"})) end -local function sinope_max_temp_handler(driver, device, value, zb_rx) - local temp_c = value.value / 100.0 - -- device:emit_event(capabilities["yourNamespace.maxTankTemp"].temperature({ value = temp_c, unit = "C" })) - -- device:set_field("max_measured_temp", temp_c, {persist = true}) -end --- ============================================================================= --- COMMAND HANDLERS --- ============================================================================= +-- local function rms_current_handler(driver, device, value, zb_rx) +-- log.info("========================================") +-- log.info("RM3500ZB RMS CURRENT HANDLER CALLED") +-- device:emit_event(capabilities.currentMeter.current({value = value.value, unit = "A"})) +-- end --- Handle switch.on command -local function switch_on_handler(driver, device, command) - device:send(OnOff.server.commands.On(device)) +local function metering_handler(driver, device, value, zb_rx) + log.info("========================================") + log.info("RM3500ZB ENERGYMETER HANDLER CALLED") + local raw_value = value.value / 1000 -- Convert Wh to kWh + device:emit_event(capabilities.energyMeter.energy({value = raw_value, unit = "kWh"})) end --- Handle switch.off command -local function switch_off_handler(driver, device, command) - device:send(OnOff.server.commands.Off(device)) +local function temperature_handler(driver, device, value, zb_rx) + log.info("========================================") + log.info("RM3500ZB TEMPERATURE HANDLER CALLED") + log.info("========================================") + local temp_c = value.value /100 + log.warn(string.format("RM3500ZB: Temperature below 0°C (%.2f°C) - ignoring", temp_c)) + if temp_c > -100 then + device:emit_event(capabilities.temperatureMeasurement.temperature({value = value.value / 100, unit = "C"})) + return + end end --- Handle thermostatCoolingSetpoint.setCoolingSetpoint command -local function set_cooling_setpoint_handler(driver, device, command) - local setpoint_c = command.args.setpoint or 46 - - -- Validate range (RM3500ZB supports 46-55°C) - if setpoint_c < 46 then setpoint_c = 46 end - if setpoint_c > 55 then setpoint_c = 55 end - - local zigbee_value = setpoint_c * 100 - - -- Get current setpoint to calculate delta - local current_setpoint = device:get_field("current_cooling_setpoint") or (46 * 100) - local delta = zigbee_value - current_setpoint - - device:send(Thermostat.server.commands.SetpointRaiseLower(device, 0x02, delta)) - device:set_field("current_cooling_setpoint", zigbee_value, {persist = true}) -end +-- ============================================================================ +-- CONFIGURE HANDLER - Set reporting intervals +-- ============================================================================ --- Handle refresh command -local function refresh_handler(driver, device, command) - -- Read all important attributes - device:send(OnOff.attributes.OnOff:read(device)) - device:send(TempMeas.attributes.MeasuredValue:read(device)) - device:send(Metering.attributes.InstantaneousDemand:read(device)) - device:send(Metering.attributes.CurrentSummationDelivered:read(device)) +local function do_configure(driver, device) + log.info("========================================") + log.info("RM3500ZB DOCONFIG HANDLER CALLED") + log.info("========================================") + + -- Configure Temperature reporting (every 30s to 5min, on 0.5°C change) + device:send(TemperatureMeasurement.attributes.MeasuredValue:configure_reporting( + device, + 60, -- min_report_interval (seconds) + 900, -- max_report_interval (seconds) + 50 -- reportable_change (0.5°C, in 0.01°C units) + )) + + -- Configure Power reporting (every 10s to 60s, on 5W change) + device:send(SimpleMetering.attributes.InstantaneousDemand:configure_reporting( + device, + 10, -- min interval: 10 seconds + 900, -- max interval: 15 minute + 50 -- reportable change: 5W (in 0.1W units) + )) + + -- Configure Energy reporting (every 5min to 30min, on 0.01 kWh change) + device:send(SimpleMetering.attributes.CurrentSummationDelivered:configure_reporting( + device, + 300, -- min interval: 5 minutes + 1800, -- max interval: 30 minutes + 10 -- reportable change: 0.01 kWh (in 0.001 kWh units) + )) + + -- Configure Voltage reporting (every 60s to 5min, on 1V change) + device:send(ElectricalMeasurement.attributes.RMSVoltage:configure_reporting( + device, + 60, -- min interval: 1 minute + 1800, -- max interval: 30 minutes + 10 -- reportable change: 1V (in 0.1V units) + )) + + -- Configure Current reporting (every 10s to 60s, on 0.1A change) + device:send(ElectricalMeasurement.attributes.RMSCurrent:configure_reporting( + device, + 10, -- min interval: 10 seconds + 900, -- max interval: 15 minute + 500 -- reportable change: 0.1A (in 0.001A units) + )) + + -- Configure IAS Zone reporting (water leak detection) + device:send(IASZone.attributes.ZoneStatus:configure_reporting( + device, + 0, -- min interval: immediate + 1800, -- max interval: 30 minutes + 1 -- reportable change: any change + )) + + -- Configure On/Off reporting + device:send(OnOff.attributes.OnOff:configure_reporting( + device, + 0, -- min interval: immediate + 1800, -- max interval: 30 minutes + 1 -- reportable change: any change + )) + + -- Refresh all values after configuration + device:send(TemperatureMeasurement.attributes.MeasuredValue:read(device)) + device:send(SimpleMetering.attributes.InstantaneousDemand:read(device)) + device:send(SimpleMetering.attributes.CurrentSummationDelivered:read(device)) device:send(ElectricalMeasurement.attributes.RMSVoltage:read(device)) device:send(ElectricalMeasurement.attributes.RMSCurrent:read(device)) - device:send(Thermostat.attributes.CoolingSetpoint:read(device)) device:send(IASZone.attributes.ZoneStatus:read(device)) + device:send(OnOff.attributes.OnOff:read(device)) end --- ============================================================================= --- PREFERENCE CHANGE HANDLER --- ============================================================================= - -local function info_changed(driver, device, event, args) - -- handle ledIntensity preference setting - if (args.old_st_store.preferences.ledIntensity ~= device.preferences.ledIntensity) then - local ledIntensity = device.preferences.ledIntensity - - device:send(cluster_base.write_attribute(device, - data_types.ClusterId(SINOPE_SWITCH_CLUSTER), - data_types.AttributeId(SINOPE_MAX_INTENSITY_ON_ATTRIBUTE), - data_types.validate_or_build_type(ledIntensity, data_types.Uint8, "payload"))) - device:send(cluster_base.write_attribute(device, - data_types.ClusterId(SINOPE_SWITCH_CLUSTER), - data_types.AttributeId(SINOPE_MAX_INTENSITY_OFF_ATTRIBUTE), - data_types.validate_or_build_type(ledIntensity, data_types.Uint8, "payload"))) - end - -- Example: DR minimum water temperature preference - if args.old_st_store.preferences.drMinWaterTemp ~= device.preferences.drMinWaterTemp then - local minTemp = device.preferences.drMinWaterTemp -- in °C - device:send(cluster_base.write_attribute( - device, - data_types.ClusterId(SINOPE_SWITCH_CLUSTER), - data_types.AttributeId(SINOPE_DR_MIN_WATER_TEMP_ATTR), - data_types.validate_or_build_type(minTemp, data_types.Uint8, "payload") - )) - end +local function do_refresh(driver, device) + log.info("========================================") + log.info("RM3500ZB DOREFRESH HANDLER CALLED") + log.info("========================================") + + -- Refresh all values + device:send(TemperatureMeasurement.attributes.MeasuredValue:read(device)) + device:send(SimpleMetering.attributes.InstantaneousDemand:read(device)) + device:send(SimpleMetering.attributes.CurrentSummationDelivered:read(device)) + device:send(ElectricalMeasurement.attributes.RMSVoltage:read(device)) + device:send(ElectricalMeasurement.attributes.RMSCurrent:read(device)) + device:send(IASZone.attributes.ZoneStatus:read(device)) + device:send(OnOff.attributes.OnOff:read(device)) end --- ============================================================================= --- DRIVER DEFINITION (NOW handlers are defined above and can be referenced) --- ============================================================================= +-- ============================================================================ +-- DRIVER DEFINITION +-- ============================================================================ -local zigbee_sinope_switch = { - NAME = "Zigbee Sinope switch", +local sinope_rm3500zb = { + NAME = "Sinopé RM3500ZB Water Heater", + capability_handlers = { + [capabilities.refresh.ID] = { + [capabilities.refresh.commands.refresh.NAME] = do_refresh, + } + }, lifecycle_handlers = { - infoChanged = info_changed + doConfigure = do_configure, -- Called when device is added/configured }, - zigbee_handlers = { attr = { - -- Standard clusters used by RM3500ZB - [OnOff.ID] = { - [OnOff.attributes.OnOff.ID] = onoff_attr_handler - }, - [TempMeas.ID] = { - [TempMeas.attributes.MeasuredValue.ID] = water_temp_attr_handler - }, - [IASZone.ID] = { - [IASZone.attributes.ZoneStatus.ID] = leak_attr_handler - }, [ElectricalMeasurement.ID] = { - [ElectricalMeasurement.attributes.RMSVoltage.ID] = voltage_attr_handler, - [ElectricalMeasurement.attributes.RMSCurrent.ID] = current_attr_handler, + [ElectricalMeasurement.attributes.ActivePower.ID] = active_power_meter_handler, + [ElectricalMeasurement.attributes.RMSVoltage.ID] = rms_voltage_handler, + [ElectricalMeasurement.attributes.RMSCurrent.ID] = rms_current_handler }, - [Metering.ID] = { - [Metering.attributes.InstantaneousDemand.ID] = power_attr_handler, - [Metering.attributes.CurrentSummationDelivered.ID] = energy_attr_handler, + [SimpleMetering.ID] = { + [SimpleMetering.attributes.InstantaneousDemand.ID] = instantaneous_demand_handler, + [SimpleMetering.attributes.CurrentSummationDelivered.ID] = metering_handler }, - [Thermostat.ID] = { - [Thermostat.attributes.CoolingSetpoint.ID] = cooling_setpoint_attr_handler, + [TemperatureMeasurement.ID] = { + [TemperatureMeasurement.attributes.MeasuredValue.ID] = temperature_handler }, - - -- Optional: manufacturer-specific 0xFF01 attributes for RM3500ZB[web:45] - [SINOPE_SWITCH_CLUSTER] = { - [SINOPE_CONNECTED_LOAD_ATTR] = sinope_connected_load_handler, - [SINOPE_MIN_MEASURED_TEMP_ATTR] = sinope_min_temp_handler, - [SINOPE_MAX_MEASURED_TEMP_ATTR] = sinope_max_temp_handler, - -- add more as needed (Timer, DR min temp, etc.) - } - } + }, }, - - capability_handlers = { - [capabilities.switch.ID] = { - [capabilities.switch.commands.on.NAME] = switch_on_handler, - [capabilities.switch.commands.off.NAME] = switch_off_handler, - -- Add mappings for any custom capabilities you define for DR/timer/load here - }, - [capabilities.thermostatCoolingSetpoint.ID] = { - [capabilities.thermostatCoolingSetpoint.commands.setCoolingSetpoint.NAME] = set_cooling_setpoint_handler, - }, - [capabilities.refresh.ID] = { - [capabilities.refresh.commands.refresh.NAME] = refresh_handler, - }, -}, - can_handle = require("sinope.can_handle"), + can_handle = require("sinope-waterheater.can_handle") } -return zigbee_sinope_switch \ No newline at end of file + +return sinope_rm3500zb \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/init.lua b/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/init.lua index 372610774b..eb2138b84c 100644 --- a/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/init.lua @@ -8,7 +8,6 @@ local constants = require "st.zigbee.constants" local SimpleMetering = clusters.SimpleMetering local ElectricalMeasurement = clusters.ElectricalMeasurement local TemperatureMeasurement = clusters.TemperatureMeasurement -local Metering = clusters.Metering local function active_power_meter_handler(driver, device, value, zb_rx) local raw_value = value.value @@ -42,6 +41,9 @@ local function metering_handler(driver, device, value, zb_rx) end local function temperature_handler(driver, device, value, zb_rx) + log.info("========================================") + log.info("ZIGBEE GENERIC SWITCHPOWER TEMPERATURE HANDLER CALLED") + log.info("========================================") device:emit_event(capabilities.temperatureMeasurement.temperature({value = value.value / 100, unit = "C"})) end @@ -51,14 +53,13 @@ local zigbee_switch_power = { attr = { [ElectricalMeasurement.ID] = { [ElectricalMeasurement.attributes.ActivePower.ID] = active_power_meter_handler, - [ElectricalMeasurement.attributes.RmsVoltage.ID] = rms_voltage_handler, - [ElectricalMeasurement.attributes.RmsCurrent.ID] = rms_current_handler + [ElectricalMeasurement.attributes.RMSVoltage.ID] = rms_voltage_handler, + [ElectricalMeasurement.attributes.RMSCurrent.ID] = rms_current_handler }, [SimpleMetering.ID] = { - [SimpleMetering.attributes.InstantaneousDemand.ID] = instantaneous_demand_handler - }, - [Metering.ID] = { - [Metering.attributes.CumulativeEnergy.ID] = metering_handler + [SimpleMetering.attributes.InstantaneousDemand.ID] = instantaneous_demand_handler, + [SimpleMetering.attributes.CurrentSummationDelivered.ID] = metering_handler + }, [TemperatureMeasurement.ID] = { [TemperatureMeasurement.attributes.MeasuredValue.ID] = temperature_handler From eaf4d788e88ce1b60331ad5ce674b02676e6c514 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 3 Jan 2026 13:22:04 -0500 Subject: [PATCH 41/49] Refactor active power meter handler function removed comment for leak sensor --- .../src/sinope-waterheater/init.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua index 8a23dd3f4b..aa50dff907 100644 --- a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua @@ -45,16 +45,16 @@ local function leak_attr_handler(driver, device, value, zb_rx) end -- -- Active power from 0x0B04/0x050B[web:45] --- local function active_power_meter_handler(driver, device, value, zb_rx) --- log.info("========================================") --- log.info("RM3500ZB POWERMETER HANDLER CALLED") --- local raw_value = value.value --- local divisor = device:get_field(constants.ELECTRICAL_MEASUREMENT_DIVISOR_KEY) or 10 + local function active_power_meter_handler(driver, device, value, zb_rx) + log.info("========================================") + log.info("RM3500ZB POWERMETER HANDLER CALLED") + local raw_value = value.value + local divisor = device:get_field(constants.ELECTRICAL_MEASUREMENT_DIVISOR_KEY) or 10 --- raw_value = raw_value / divisor + raw_value = raw_value / divisor --- device:emit_event(capabilities.powerMeter.power({value = raw_value, unit = "W"})) --- end + device:emit_event(capabilities.powerMeter.power({value = raw_value, unit = "W"})) + end local function instantaneous_demand_handler(driver, device, value, zb_rx) log.info("========================================") @@ -221,4 +221,4 @@ local sinope_rm3500zb = { } -return sinope_rm3500zb \ No newline at end of file +return sinope_rm3500zb From 49869aae9d62da86ad5b69358fa50a3b146051b7 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 3 Jan 2026 13:56:26 -0500 Subject: [PATCH 42/49] First Working version of the RM3500ZB --- .../src/sinope-waterheater/init.lua | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua index 8a23dd3f4b..5e1aedc128 100644 --- a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua @@ -32,7 +32,7 @@ local ElectricalMeasurement = clusters.ElectricalMeasurement -- 0x0B04 local Thermostat = clusters.Thermostat -- Leak state from IAS Zone 0x0500/0x0002[web:45] -local function leak_attr_handler(driver, device, value, zb_rx) +local function water_sensor_handler(driver, device, value, zb_rx) log.info("========================================") log.info("RM3500ZB LEAK HANDLER CALLED") local zone_status = value.value @@ -101,11 +101,11 @@ end -- CONFIGURE HANDLER - Set reporting intervals -- ============================================================================ -local function do_configure(driver, device) +local function configure_device(driver, device) log.info("========================================") - log.info("RM3500ZB DOCONFIG HANDLER CALLED") + log.info("RM3500ZB CONFIGURE DEVICE HANDLER CALLED") log.info("========================================") - + -- Configure Temperature reporting (every 30s to 5min, on 0.5°C change) device:send(TemperatureMeasurement.attributes.MeasuredValue:configure_reporting( device, @@ -199,7 +199,7 @@ local sinope_rm3500zb = { } }, lifecycle_handlers = { - doConfigure = do_configure, -- Called when device is added/configured + init = configure_device, -- Called when device is added/configured }, zigbee_handlers = { attr = { @@ -215,10 +215,16 @@ local sinope_rm3500zb = { [TemperatureMeasurement.ID] = { [TemperatureMeasurement.attributes.MeasuredValue.ID] = temperature_handler }, + [IASZone.ID] = { + [IASZone.attributes.ZoneStatus.ID] = water_sensor_handler + }, }, }, can_handle = require("sinope-waterheater.can_handle") } -return sinope_rm3500zb \ No newline at end of file +return sinope_rm3500zb + + + From 5ca40d35764952916de27dd5d43d28471437a6b3 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Tue, 6 Jan 2026 20:32:18 -0500 Subject: [PATCH 43/49] Commit --- .../src/sinope-waterheater/init.lua | 64 +++++++++++-------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua index 5e1aedc128..0c177c1627 100644 --- a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua @@ -10,9 +10,9 @@ local clusters = require "st.zigbee.zcl.clusters" local log = require "log" -- Sinopé manufacturer cluster and attributes -local SINOPE_SWITCH_CLUSTER = 0xFF01 -local SINOPE_MAX_INTENSITY_ON_ATTRIBUTE = 0x0052 -local SINOPE_MAX_INTENSITY_OFF_ATTRIBUTE = 0x0053 +local SINOPE_SWITCH_CLUSTER = 0xFF01 +local SINOPE_MAX_INTENSITY_ON_ATTRIBUTE = 0x0052 +local SINOPE_MAX_INTENSITY_OFF_ATTRIBUTE = 0x0053 local SINOPE_CONNECTED_LOAD_ATTR = 0x0060 -- ConnectedLoad (W)[web:45] local SINOPE_CURRENT_LOAD_ATTR = 0x0070 -- CurrentLoad (W-ish bitmap)[web:45] local SINOPE_DR_MIN_WATER_TEMP_ATTR = 0x0076 -- drConfigWaterTempMin (°C)[web:45] @@ -24,12 +24,22 @@ local SINOPE_MAX_MEASURED_TEMP_ATTR = 0x007D -- max_measured_temp (°C×1 local SINOPE_ENERGY_INTERNAL_ATTR = 0x0090 -- currentSummationDelivered (internal)[web:45] -- Standard Zigbee clusters -local SimpleMetering = clusters.SimpleMetering -- 0x0702 -local OnOff = clusters.OnOff -- 0x0006 -local TemperatureMeasurement = clusters.TemperatureMeasurement -- 0x0402 -local IASZone = clusters.IASZone -- 0x0500 -local ElectricalMeasurement = clusters.ElectricalMeasurement -- 0x0B04 -local Thermostat = clusters.Thermostat +local SimpleMetering = clusters.SimpleMetering -- 0x0702 +local OnOff = clusters.OnOff -- 0x0006 +local TemperatureMeasurement = clusters.TemperatureMeasurement -- 0x0402 +local IASZone = clusters.IASZone -- 0x0500 +local ElectricalMeasurement = clusters.ElectricalMeasurement -- 0x0B04 +local Thermostat = clusters.Thermostat + +--Water mon temp config +-- Cluster: 0xff01 +-- Attribute: 0x0076 +-- Data type: t.uint8_t +-- Function: drConfigWaterTempMin +-- Values: 45 or 0 + + + -- Leak state from IAS Zone 0x0500/0x0002[web:45] local function water_sensor_handler(driver, device, value, zb_rx) @@ -44,39 +54,36 @@ local function water_sensor_handler(driver, device, value, zb_rx) end end --- -- Active power from 0x0B04/0x050B[web:45] --- local function active_power_meter_handler(driver, device, value, zb_rx) --- log.info("========================================") --- log.info("RM3500ZB POWERMETER HANDLER CALLED") --- local raw_value = value.value --- local divisor = device:get_field(constants.ELECTRICAL_MEASUREMENT_DIVISOR_KEY) or 10 - --- raw_value = raw_value / divisor +-- Active power from 0x0B04/0x050B[web:45] +local function active_power_meter_handler(driver, device, value, zb_rx) + log.info("========================================") + log.info("RM3500ZB POWERMETER HANDLER CALLED") + local raw_value = value.value + raw_value = raw_value / 10 --- device:emit_event(capabilities.powerMeter.power({value = raw_value, unit = "W"})) --- end + device:emit_event(capabilities.powerMeter.power({value = raw_value, unit = "W"})) +end local function instantaneous_demand_handler(driver, device, value, zb_rx) log.info("========================================") log.info("RM3500ZB INSTANTMETER HANDLER CALLED") local raw_value = value.value - local divisor = device:get_field(constants.SIMPLE_METERING_DIVISOR_KEY) or 10 - - raw_value = raw_value / divisor + raw_value = raw_value / 10 device:emit_event(capabilities.powerMeter.power({value = raw_value, unit = "W"})) end + local function rms_voltage_handler(driver, device, value, zb_rx) log.info("========================================") log.info("RM3500ZB RMSVOLTS HANDLER CALLED") device:emit_event(capabilities.voltageMeasurement.voltage({value = value.value, unit = "V"})) end --- local function rms_current_handler(driver, device, value, zb_rx) --- log.info("========================================") --- log.info("RM3500ZB RMS CURRENT HANDLER CALLED") --- device:emit_event(capabilities.currentMeter.current({value = value.value, unit = "A"})) --- end +local function rms_current_handler(driver, device, value, zb_rx) + log.info("========================================") + log.info("RM3500ZB RMS CURRENT HANDLER CALLED") + device:emit_event(capabilities.currentMeasurement.current({value = value.value, unit = "A"})) +end local function metering_handler(driver, device, value, zb_rx) log.info("========================================") @@ -90,7 +97,7 @@ local function temperature_handler(driver, device, value, zb_rx) log.info("RM3500ZB TEMPERATURE HANDLER CALLED") log.info("========================================") local temp_c = value.value /100 - log.warn(string.format("RM3500ZB: Temperature below 0°C (%.2f°C) - ignoring", temp_c)) + -- the RM3500ZB may report 2 types of values with one in the -300 range. This only sends the value if it is over -100 deg C if temp_c > -100 then device:emit_event(capabilities.temperatureMeasurement.temperature({value = value.value / 100, unit = "C"})) return @@ -181,6 +188,7 @@ local function do_refresh(driver, device) device:send(TemperatureMeasurement.attributes.MeasuredValue:read(device)) device:send(SimpleMetering.attributes.InstantaneousDemand:read(device)) device:send(SimpleMetering.attributes.CurrentSummationDelivered:read(device)) + device:send(ElectricalMeasurement.attributes.ActivePower:read(device)) device:send(ElectricalMeasurement.attributes.RMSVoltage:read(device)) device:send(ElectricalMeasurement.attributes.RMSCurrent:read(device)) device:send(IASZone.attributes.ZoneStatus:read(device)) From 48c23f81215e7235e9bf0763d0443c45502c9f59 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Tue, 6 Jan 2026 20:32:34 -0500 Subject: [PATCH 44/49] current MEter --- .../zigbee-switch/profiles/switch-power-waterheater.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml b/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml index 327a3dca2b..650c4a1510 100644 --- a/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml +++ b/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml @@ -8,8 +8,8 @@ components: version: 1 - id: voltageMeasurement version: 1 - # - id: currentMeter - # version: 1 + - id: currentMeasurement + version: 1 - id: energyMeter version: 1 - id: temperatureMeasurement From 8659421ae9fc94e4cd73de62d4b511c23d2d8ec8 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 9 Jan 2026 07:24:44 -0500 Subject: [PATCH 45/49] All working excetp settings --- .../profiles/switch-power-waterheater.yml | 70 +++- .../src/sinope-waterheater/init.lua | 300 +++++++++++++++--- 2 files changed, 323 insertions(+), 47 deletions(-) diff --git a/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml b/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml index 650c4a1510..5be7899c86 100644 --- a/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml +++ b/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml @@ -6,21 +6,77 @@ components: version: 1 - id: powerMeter version: 1 + - id: temperatureMeasurement + version: 1 + - id: waterSensor + version: 1 - id: voltageMeasurement version: 1 - id: currentMeasurement version: 1 - id: energyMeter version: 1 - - id: temperatureMeasurement - version: 1 - - id: waterSensor - version: 1 - - id: thermostatCoolingSetpoint - version: 1 - id: firmwareUpdate version: 1 - id: refresh version: 1 categories: - - name: SmartPlug \ No newline at end of file + - name: SmartPlug + +preferences: + - name: "lowTempProtection" + title: "Low Water Temperature Protection" + description: "Minimum temperature threshold to restart water heater" + required: false + preferenceType: enumeration + definition: + options: + 0: "OFF" + 45: "45 °C" + 46: "46 °C" + 47: "47 °C" + 48: "48 °C" + 49: "49 °C" + 50: "50 °C" + 51: "51 °C" + 52: "52 °C" + 53: "53 °C" + 54: "54 °C" + 55: "55 °C" + default: 45 + + - title: "Min Measured Temperature" + name: minMeasuredTemp + description: "Lowest temperature recorded (°C) - Read Only" + required: false + preferenceType: number + definition: + minimum: -50 + maximum: 100 + default: 0 + + - title: "Max Measured Temperature" + name: maxMeasuredTemp + description: "Highest temperature recorded (°C) - Read Only" + required: false + preferenceType: number + definition: + minimum: -50 + maximum: 100 + default: 0 + + + + # Tank Size not used yet + # - name: "tankSize" + # title: "Tank Size" + # description: "Select the size of the hot water tank" + # required: false + # preferenceType: enumeration + # definition: + # options: + # 40: "40 Gal" + # 50: "50 Gal" + # 60: "60 Gal" + # 80: "80 Gal" + # default: 40 \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua index 0c177c1627..0cc10563f7 100644 --- a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua @@ -8,20 +8,21 @@ local ZigbeeDriver = require "st.zigbee" local defaults = require "st.zigbee.defaults" local clusters = require "st.zigbee.zcl.clusters" local log = require "log" +local zb_utils = require "st.zigbee.utils" -- Sinopé manufacturer cluster and attributes local SINOPE_SWITCH_CLUSTER = 0xFF01 local SINOPE_MAX_INTENSITY_ON_ATTRIBUTE = 0x0052 local SINOPE_MAX_INTENSITY_OFF_ATTRIBUTE = 0x0053 -local SINOPE_CONNECTED_LOAD_ATTR = 0x0060 -- ConnectedLoad (W)[web:45] -local SINOPE_CURRENT_LOAD_ATTR = 0x0070 -- CurrentLoad (W-ish bitmap)[web:45] -local SINOPE_DR_MIN_WATER_TEMP_ATTR = 0x0076 -- drConfigWaterTempMin (°C)[web:45] -local SINOPE_DR_MIN_WATER_TEMP_TIME_ATTR = 0x0077 -- drConfigWaterTempTime[web:45] -local SINOPE_TIMER_ATTR = 0x00A0 -- Timer seconds[web:45] -local SINOPE_TIMER_COUNTDOWN_ATTR = 0x00A1 -- Timer_countDown[web:45] -local SINOPE_MIN_MEASURED_TEMP_ATTR = 0x007C -- min_measured_temp (°C×100)[web:45] -local SINOPE_MAX_MEASURED_TEMP_ATTR = 0x007D -- max_measured_temp (°C×100)[web:45] -local SINOPE_ENERGY_INTERNAL_ATTR = 0x0090 -- currentSummationDelivered (internal)[web:45] +local SINOPE_CONNECTED_LOAD_ATTR = 0x0060 -- ConnectedLoad (W) +local SINOPE_CURRENT_LOAD_ATTR = 0x0070 -- CurrentLoad (W-ish bitmap) +local SINOPE_DR_MIN_WATER_TEMP_ATTR = 0x0076 -- drConfigWaterTempMin (°C) +local SINOPE_DR_MIN_WATER_TEMP_TIME_ATTR = 0x0077 -- drConfigWaterTempTime +local SINOPE_TIMER_ATTR = 0x00A0 -- Timer seconds +local SINOPE_TIMER_COUNTDOWN_ATTR = 0x00A1 -- Timer_countDown +local SINOPE_MIN_MEASURED_TEMP_ATTR = 0x007C -- min_measured_temp (°C×100) +local SINOPE_MAX_MEASURED_TEMP_ATTR = 0x007D -- max_measured_temp (°C×100) +local SINOPE_ENERGY_INTERNAL_ATTR = 0x0090 -- currentSummationDelivered (internal) -- Standard Zigbee clusters local SimpleMetering = clusters.SimpleMetering -- 0x0702 @@ -38,10 +39,11 @@ local Thermostat = clusters.Thermostat -- Function: drConfigWaterTempMin -- Values: 45 or 0 +-- ============================================================================ +-- STANDARD ATTRIBUTE HANDLERS +-- ============================================================================ - - --- Leak state from IAS Zone 0x0500/0x0002[web:45] +-- Leak state from IAS Zone 0x0500/0x0002 local function water_sensor_handler(driver, device, value, zb_rx) log.info("========================================") log.info("RM3500ZB LEAK HANDLER CALLED") @@ -54,23 +56,19 @@ local function water_sensor_handler(driver, device, value, zb_rx) end end --- Active power from 0x0B04/0x050B[web:45] +-- Active power from 0x0B04/0x050B local function active_power_meter_handler(driver, device, value, zb_rx) log.info("========================================") log.info("RM3500ZB POWERMETER HANDLER CALLED") - local raw_value = value.value - raw_value = raw_value / 10 - device:emit_event(capabilities.powerMeter.power({value = raw_value, unit = "W"})) + device:emit_event(capabilities.powerMeter.power({value = value.value, unit = "W"})) end local function instantaneous_demand_handler(driver, device, value, zb_rx) log.info("========================================") log.info("RM3500ZB INSTANTMETER HANDLER CALLED") - local raw_value = value.value - raw_value = raw_value / 10 - device:emit_event(capabilities.powerMeter.power({value = raw_value, unit = "W"})) + device:emit_event(capabilities.powerMeter.power({value = value.value, unit = "W"})) end local function rms_voltage_handler(driver, device, value, zb_rx) @@ -82,7 +80,10 @@ end local function rms_current_handler(driver, device, value, zb_rx) log.info("========================================") log.info("RM3500ZB RMS CURRENT HANDLER CALLED") - device:emit_event(capabilities.currentMeasurement.current({value = value.value, unit = "A"})) + local raw_value = value.value + raw_value = raw_value / 1000 + + device:emit_event(capabilities.currentMeasurement.current({value = raw_value, unit = "A"})) end local function metering_handler(driver, device, value, zb_rx) @@ -104,6 +105,143 @@ local function temperature_handler(driver, device, value, zb_rx) end end +-- ============================================================================ +-- SINOPE MANUFACTURER ATTRIBUTE HANDLERS +-- ============================================================================ + +-- Handler for Low Temperature Protection (0xFF01/0x0076) +local function sinope_low_temp_protection_handler(driver, device, value, zb_rx) + log.info("========================================") + log.info("RM3500ZB LOW TEMP PROTECTION HANDLER CALLED") + local temp_value = value.value + log.info(string.format("Low temp protection value from device: %d°C", temp_value)) + + -- Store in field + device:set_field("lowTempProtection", temp_value, {persist = true}) +end + +-- Handler for Tank Size (0xFF01/0x0060) +local function sinope_tank_size_handler(driver, device, value, zb_rx) + log.info("========================================") + log.info("RM3500ZB TANK SIZE HANDLER CALLED") + local tank_size = value.value + log.info(string.format("Tank size from device: %d Gallons", tank_size)) + + -- Store in field + device:set_field("tankSize", tank_size, {persist = true}) +end + +-- Handler for Min Measured Temp (0xFF01/0x007C) - READ ONLY +local function sinope_min_measured_temp_handler(driver, device, value, zb_rx) + log.info("========================================") + log.info("RM3500ZB MIN MEASURED TEMP HANDLER CALLED") + local temp_value = value.value / 100 -- Convert from °C×100 + log.info(string.format("Min measured temp from device: %.2f°C", temp_value)) + + -- Store in field + device:set_field("minMeasuredTemp", temp_value, {persist = true}) + + -- -- Update the preference value (this makes it show in settings) + -- device:try_update_metadata({ + -- preferences = { + -- minMeasuredTemp = temp_value + -- } + -- }) +end + + +local function sinope_max_measured_temp_handler(driver, device, value, zb_rx) + local temp_value = value.value / 100 + log.info(string.format("Max measured temp from device: %.2f°C", temp_value)) + -- Store in field + device:set_field("maxMeasuredTemp", temp_value, {persist = true}) + + -- -- Update the preference value (this makes it show in settings) + -- device:try_update_metadata({ + -- preferences = { + -- maxMeasuredTemp = temp_value + -- } + -- }) +end + + +-- Handler for Max Measured Temp (0xFF01/0x007D) - READ ONLY +local function sinope_max_measured_temp_handler(driver, device, value, zb_rx) + log.info("========================================") + log.info("RM3500ZB MAX MEASURED TEMP HANDLER CALLED") + local temp_value = value.value / 100 -- Convert from °C×100 + log.info(string.format("Max measured temp from device: %.2f°C", temp_value)) + + -- Store in field + device:set_field("maxMeasuredTemp", temp_value, {persist = true}) +end + +-- ============================================================================ +-- REFRESH SETTINGS FUNCTION +-- ============================================================================ + +local function refresh_settings(device) + log.info("========================================") + log.info("RM3500ZB REFRESHING SETTINGS") + log.info("========================================") + + -- Read Low Temperature Protection setting + device:send(cluster_base.read_manufacturer_specific_attribute( + device, + SINOPE_SWITCH_CLUSTER, + SINOPE_DR_MIN_WATER_TEMP_ATTR, + 0x119C + )) + + -- Read Tank Size setting (Connected Load) + device:send(cluster_base.read_manufacturer_specific_attribute( + device, + SINOPE_SWITCH_CLUSTER, + SINOPE_CONNECTED_LOAD_ATTR, + 0x119C + )) + + -- Read Min Measured Temp + device:send(cluster_base.read_manufacturer_specific_attribute( + device, + SINOPE_SWITCH_CLUSTER, + SINOPE_MIN_MEASURED_TEMP_ATTR, + 0x119C + )) + + -- Read Max Measured Temp + device:send(cluster_base.read_manufacturer_specific_attribute( + device, + SINOPE_SWITCH_CLUSTER, + SINOPE_MAX_MEASURED_TEMP_ATTR, + 0x119C + )) +end + +-- ============================================================================ +-- REFRESH ALL VALUES + SETTINGS +-- ============================================================================ + + +local function do_refresh(driver, device) + log.info("========================================") + log.info("RM3500ZB DOREFRESH HANDLER CALLED") + log.info("========================================") + + -- Refresh all values + device:send(TemperatureMeasurement.attributes.MeasuredValue:read(device)) + device:send(SimpleMetering.attributes.InstantaneousDemand:read(device)) + device:send(SimpleMetering.attributes.CurrentSummationDelivered:read(device)) + device:send(ElectricalMeasurement.attributes.ActivePower:read(device)) + device:send(ElectricalMeasurement.attributes.RMSVoltage:read(device)) + device:send(ElectricalMeasurement.attributes.RMSCurrent:read(device)) + device:send(IASZone.attributes.ZoneStatus:read(device)) + device:send(OnOff.attributes.OnOff:read(device)) + + -- Refresh settings values + refresh_settings(device) +end + -- ============================================================================ -- CONFIGURE HANDLER - Set reporting intervals -- ============================================================================ @@ -169,32 +307,106 @@ local function configure_device(driver, device) 1 -- reportable change: any change )) - -- Refresh all values after configuration - device:send(TemperatureMeasurement.attributes.MeasuredValue:read(device)) - device:send(SimpleMetering.attributes.InstantaneousDemand:read(device)) - device:send(SimpleMetering.attributes.CurrentSummationDelivered:read(device)) - device:send(ElectricalMeasurement.attributes.RMSVoltage:read(device)) - device:send(ElectricalMeasurement.attributes.RMSCurrent:read(device)) - device:send(IASZone.attributes.ZoneStatus:read(device)) - device:send(OnOff.attributes.OnOff:read(device)) + + -- Refresh all values + settings + do_refresh(driver, device) + + end -local function do_refresh(driver, device) + + +local function log_all_unhandled_messages(driver, device, zb_rx) + log.debug("===== INCOMING ZIGBEE MESSAGE =====") + log.debug(zb_rx:pretty_print(zb_utils.MULTI_LINE_FORMAT_CONFIG)) + log.debug("===================================") +end + + +-- ============================================================================ +-- PREFERENCES CHANGE HANDLER +-- ============================================================================ + +local function info_changed_handler(driver, device, event, args) log.info("========================================") - log.info("RM3500ZB DOREFRESH HANDLER CALLED") + log.info("RM3500ZB INFO_CHANGED HANDLER CALLED") log.info("========================================") - -- Refresh all values - device:send(TemperatureMeasurement.attributes.MeasuredValue:read(device)) - device:send(SimpleMetering.attributes.InstantaneousDemand:read(device)) - device:send(SimpleMetering.attributes.CurrentSummationDelivered:read(device)) - device:send(ElectricalMeasurement.attributes.ActivePower:read(device)) - device:send(ElectricalMeasurement.attributes.RMSVoltage:read(device)) - device:send(ElectricalMeasurement.attributes.RMSCurrent:read(device)) - device:send(IASZone.attributes.ZoneStatus:read(device)) - device:send(OnOff.attributes.OnOff:read(device)) + -- -- Check if this is a settings page open (no old preferences) + if not args.old_st_store.preferences then + log.info("Settings page opened - refreshing values") + refresh_settings(device) + return + end + + -- Handle Low Temperature Protection change + if args.old_st_store.preferences.lowTempProtection ~= device.preferences.lowTempProtection then + local new_value = tonumber(device.preferences.lowTempProtection) + log.info(string.format("Low temp protection changed to: %d°C", new_value)) + + -- Write to device + device:send(cluster_base.write_manufacturer_specific_attribute( + device, + SINOPE_SWITCH_CLUSTER, + SINOPE_DR_MIN_WATER_TEMP_ATTR, + 0x119C, + data_types.Uint8, + new_value + )) + + -- Read back after 2 seconds to verify + device.thread:call_with_delay(2, function() + log.info("Reading back lowTempProtection after write...") + device:send(cluster_base.read_manufacturer_specific_attribute( + device, + SINOPE_SWITCH_CLUSTER, + SINOPE_DR_MIN_WATER_TEMP_ATTR, + 0x119C + )) + end) + end + + -- Handle Tank Size change + if args.old_st_store.preferences.tankSize ~= device.preferences.tankSize then + local new_value = device.preferences.tankSize + log.info(string.format("Tank size changed to: %d Gallons", new_value)) + + -- Write to device + device:send(cluster_base.write_manufacturer_specific_attribute( + device, + SINOPE_SWITCH_CLUSTER, + SINOPE_CONNECTED_LOAD_ATTR, + 0x119C, + data_types.Uint16, + new_value + )) + + -- Read back after 2 seconds to verify + device.thread:call_with_delay(2, function() + log.info("Reading back tankSize after write...") + device:send(cluster_base.read_manufacturer_specific_attribute( + device, + SINOPE_SWITCH_CLUSTER, + SINOPE_CONNECTED_LOAD_ATTR, + 0x119C + )) + end) + end end + + +-- ============================================================================ +-- THINGS LEFT TO DO +-- Refresh setting Value after setting it +-- Add Min + Max measured values to preferences +-- Add ConnectedLoad to Preferences ? +-- Move +-- ============================================================================ + + + + -- ============================================================================ -- DRIVER DEFINITION -- ============================================================================ @@ -208,8 +420,11 @@ local sinope_rm3500zb = { }, lifecycle_handlers = { init = configure_device, -- Called when device is added/configured + doConfigure = configure_device, + infoChanged = info_changed_handler, }, - zigbee_handlers = { + zigbee_handlers = { + fallback = log_all_unhandled_messages, attr = { [ElectricalMeasurement.ID] = { [ElectricalMeasurement.attributes.ActivePower.ID] = active_power_meter_handler, @@ -226,6 +441,11 @@ local sinope_rm3500zb = { [IASZone.ID] = { [IASZone.attributes.ZoneStatus.ID] = water_sensor_handler }, + [SINOPE_SWITCH_CLUSTER] = { + [SINOPE_DR_MIN_WATER_TEMP_ATTR] = sinope_low_temp_protection_handler, + [SINOPE_MIN_MEASURED_TEMP_ATTR] = sinope_min_measured_temp_handler, + [SINOPE_MAX_MEASURED_TEMP_ATTR] = sinope_max_measured_temp_handler, + }, }, }, can_handle = require("sinope-waterheater.can_handle") From a96db10a476df0fd6c8453433e6d725a83fec932 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 9 Jan 2026 08:25:00 -0500 Subject: [PATCH 46/49] Final Version --- .../profiles/switch-power-waterheater.yml | 36 +-- .../src/sinope-waterheater/init.lua | 278 ++++++++---------- 2 files changed, 135 insertions(+), 179 deletions(-) diff --git a/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml b/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml index 5be7899c86..56acb58b47 100644 --- a/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml +++ b/drivers/SmartThings/zigbee-switch/profiles/switch-power-waterheater.yml @@ -45,25 +45,25 @@ preferences: 55: "55 °C" default: 45 - - title: "Min Measured Temperature" - name: minMeasuredTemp - description: "Lowest temperature recorded (°C) - Read Only" - required: false - preferenceType: number - definition: - minimum: -50 - maximum: 100 - default: 0 + # - title: "Min Measured Temperature" + # name: "minMeasuredTempp" + # description: "Lowest temperature recorded (°C) - Read Only" + # required: false + # preferenceType: number + # definition: + # minimum: -50 + # maximum: 100 + # default: 0 - - title: "Max Measured Temperature" - name: maxMeasuredTemp - description: "Highest temperature recorded (°C) - Read Only" - required: false - preferenceType: number - definition: - minimum: -50 - maximum: 100 - default: 0 + # - title: "Max Measured Temperature" + # name: "maxMeasuredTempp" + # description: "Highest temperature recorded (°C) - Read Only" + # required: false + # preferenceType: number + # definition: + # minimum: -50 + # maximum: 100 + # default: 0 diff --git a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua index 0cc10563f7..850e553c0f 100644 --- a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua @@ -7,7 +7,7 @@ local capabilities = require "st.capabilities" local ZigbeeDriver = require "st.zigbee" local defaults = require "st.zigbee.defaults" local clusters = require "st.zigbee.zcl.clusters" -local log = require "log" +-- local log = require "log" local zb_utils = require "st.zigbee.utils" -- Sinopé manufacturer cluster and attributes @@ -32,12 +32,6 @@ local IASZone = clusters.IASZone -- 0x0500 local ElectricalMeasurement = clusters.ElectricalMeasurement -- 0x0B04 local Thermostat = clusters.Thermostat ---Water mon temp config --- Cluster: 0xff01 --- Attribute: 0x0076 --- Data type: t.uint8_t --- Function: drConfigWaterTempMin --- Values: 45 or 0 -- ============================================================================ -- STANDARD ATTRIBUTE HANDLERS @@ -45,8 +39,8 @@ local Thermostat = clusters.Thermostat -- Leak state from IAS Zone 0x0500/0x0002 local function water_sensor_handler(driver, device, value, zb_rx) - log.info("========================================") - log.info("RM3500ZB LEAK HANDLER CALLED") + -- log.info("========================================") + -- log.info("RM3500ZB LEAK HANDLER CALLED") local zone_status = value.value local wet = (bit32.band(zone_status, 0x0001) ~= 0) -- example mask if wet then @@ -58,28 +52,28 @@ end -- Active power from 0x0B04/0x050B local function active_power_meter_handler(driver, device, value, zb_rx) - log.info("========================================") - log.info("RM3500ZB POWERMETER HANDLER CALLED") + -- log.info("========================================") + -- log.info("RM3500ZB POWERMETER HANDLER CALLED") device:emit_event(capabilities.powerMeter.power({value = value.value, unit = "W"})) end local function instantaneous_demand_handler(driver, device, value, zb_rx) - log.info("========================================") - log.info("RM3500ZB INSTANTMETER HANDLER CALLED") + -- log.info("========================================") + -- log.info("RM3500ZB INSTANTMETER HANDLER CALLED") device:emit_event(capabilities.powerMeter.power({value = value.value, unit = "W"})) end local function rms_voltage_handler(driver, device, value, zb_rx) - log.info("========================================") - log.info("RM3500ZB RMSVOLTS HANDLER CALLED") + -- log.info("========================================") + -- log.info("RM3500ZB RMSVOLTS HANDLER CALLED") device:emit_event(capabilities.voltageMeasurement.voltage({value = value.value, unit = "V"})) end local function rms_current_handler(driver, device, value, zb_rx) - log.info("========================================") - log.info("RM3500ZB RMS CURRENT HANDLER CALLED") + -- log.info("========================================") + -- log.info("RM3500ZB RMS CURRENT HANDLER CALLED") local raw_value = value.value raw_value = raw_value / 1000 @@ -87,16 +81,16 @@ local function rms_current_handler(driver, device, value, zb_rx) end local function metering_handler(driver, device, value, zb_rx) - log.info("========================================") - log.info("RM3500ZB ENERGYMETER HANDLER CALLED") + -- log.info("========================================") + -- log.info("RM3500ZB ENERGYMETER HANDLER CALLED") local raw_value = value.value / 1000 -- Convert Wh to kWh device:emit_event(capabilities.energyMeter.energy({value = raw_value, unit = "kWh"})) end local function temperature_handler(driver, device, value, zb_rx) - log.info("========================================") - log.info("RM3500ZB TEMPERATURE HANDLER CALLED") - log.info("========================================") + -- log.info("========================================") + -- log.info("RM3500ZB TEMPERATURE HANDLER CALLED") + -- log.info("========================================") local temp_c = value.value /100 -- the RM3500ZB may report 2 types of values with one in the -300 range. This only sends the value if it is over -100 deg C if temp_c > -100 then @@ -111,79 +105,64 @@ end -- Handler for Low Temperature Protection (0xFF01/0x0076) local function sinope_low_temp_protection_handler(driver, device, value, zb_rx) - log.info("========================================") - log.info("RM3500ZB LOW TEMP PROTECTION HANDLER CALLED") + -- log.info("========================================") + -- log.info("RM3500ZB LOW TEMP PROTECTION HANDLER CALLED") local temp_value = value.value - log.info(string.format("Low temp protection value from device: %d°C", temp_value)) + -- log.info(string.format("Low temp protection value from device: %d°C", temp_value)) -- Store in field device:set_field("lowTempProtection", temp_value, {persist = true}) end --- Handler for Tank Size (0xFF01/0x0060) -local function sinope_tank_size_handler(driver, device, value, zb_rx) - log.info("========================================") - log.info("RM3500ZB TANK SIZE HANDLER CALLED") - local tank_size = value.value - log.info(string.format("Tank size from device: %d Gallons", tank_size)) +-- -- Handler for Tank Size (0xFF01/0x0060) +-- local function sinope_tank_size_handler(driver, device, value, zb_rx) +-- log.info("========================================") +-- log.info("RM3500ZB TANK SIZE HANDLER CALLED") +-- local tank_size = value.value +-- log.info(string.format("Tank size from device: %d Gallons", tank_size)) - -- Store in field - device:set_field("tankSize", tank_size, {persist = true}) -end - --- Handler for Min Measured Temp (0xFF01/0x007C) - READ ONLY -local function sinope_min_measured_temp_handler(driver, device, value, zb_rx) - log.info("========================================") - log.info("RM3500ZB MIN MEASURED TEMP HANDLER CALLED") - local temp_value = value.value / 100 -- Convert from °C×100 - log.info(string.format("Min measured temp from device: %.2f°C", temp_value)) +-- -- Store in field +-- device:set_field("tankSize", tank_size, {persist = true}) +-- end + +-- -- Handler for Min Measured Temp (0xFF01/0x007C) - READ ONLY +-- local function sinope_min_measured_temp_handler(driver, device, value, zb_rx) +-- log.info("========================================") +-- log.info("RM3500ZB MIN MEASURED TEMP HANDLER CALLED") +-- local temp_value = value.value / 100 -- Convert from °C×100 +-- log.info(string.format("Min measured temp from device: %.2f°C", temp_value)) - -- Store in field - device:set_field("minMeasuredTemp", temp_value, {persist = true}) - - -- -- Update the preference value (this makes it show in settings) - -- device:try_update_metadata({ - -- preferences = { - -- minMeasuredTemp = temp_value - -- } - -- }) -end - - -local function sinope_max_measured_temp_handler(driver, device, value, zb_rx) - local temp_value = value.value / 100 - log.info(string.format("Max measured temp from device: %.2f°C", temp_value)) - -- Store in field - device:set_field("maxMeasuredTemp", temp_value, {persist = true}) - - -- -- Update the preference value (this makes it show in settings) - -- device:try_update_metadata({ - -- preferences = { - -- maxMeasuredTemp = temp_value - -- } - -- }) -end - - --- Handler for Max Measured Temp (0xFF01/0x007D) - READ ONLY -local function sinope_max_measured_temp_handler(driver, device, value, zb_rx) - log.info("========================================") - log.info("RM3500ZB MAX MEASURED TEMP HANDLER CALLED") - local temp_value = value.value / 100 -- Convert from °C×100 - log.info(string.format("Max measured temp from device: %.2f°C", temp_value)) +-- -- Store in field +-- device:set_field("minMeasuredTemp", temp_value, {persist = true}) + +-- -- -- Update the preference value (this makes it show in settings) +-- -- device:try_update_metadata({ +-- -- preferences = { +-- -- minMeasuredTemp = temp_value +-- -- } +-- -- }) +-- end + +-- -- Handler for Max Measured Temp (0xFF01/0x007D) - READ ONLY +-- local function sinope_max_measured_temp_handler(driver, device, value, zb_rx) +-- log.info("========================================") +-- log.info("RM3500ZB MAX MEASURED TEMP HANDLER CALLED") +-- local temp_value = value.value / 100 -- Convert from °C×100 +-- log.info(string.format("Max measured temp from device updated: %.2f°C", temp_value)) - -- Store in field - device:set_field("maxMeasuredTemp", temp_value, {persist = true}) -end +-- -- Store in field +-- device:set_field("maxMeasuredTemp", temp_value, {persist = true}) +-- local templog = device:get_field("maxMeasuredTemp") +-- log.info(string.format("Max measured temp from device read: %.2f°C", templog))end -- ============================================================================ -- REFRESH SETTINGS FUNCTION -- ============================================================================ local function refresh_settings(device) - log.info("========================================") - log.info("RM3500ZB REFRESHING SETTINGS") - log.info("========================================") + -- log.info("========================================") + -- log.info("RM3500ZB REFRESHING SETTINGS") + -- log.info("========================================") -- Read Low Temperature Protection setting device:send(cluster_base.read_manufacturer_specific_attribute( @@ -193,29 +172,29 @@ local function refresh_settings(device) 0x119C )) - -- Read Tank Size setting (Connected Load) - device:send(cluster_base.read_manufacturer_specific_attribute( - device, - SINOPE_SWITCH_CLUSTER, - SINOPE_CONNECTED_LOAD_ATTR, - 0x119C - )) + -- -- Read Tank Size setting (Connected Load) + -- device:send(cluster_base.read_manufacturer_specific_attribute( + -- device, + -- SINOPE_SWITCH_CLUSTER, + -- SINOPE_CONNECTED_LOAD_ATTR, + -- 0x119C + -- )) - -- Read Min Measured Temp - device:send(cluster_base.read_manufacturer_specific_attribute( - device, - SINOPE_SWITCH_CLUSTER, - SINOPE_MIN_MEASURED_TEMP_ATTR, - 0x119C - )) + -- -- Read Min Measured Temp + -- device:send(cluster_base.read_manufacturer_specific_attribute( + -- device, + -- SINOPE_SWITCH_CLUSTER, + -- SINOPE_MIN_MEASURED_TEMP_ATTR, + -- 0x119C + -- )) - -- Read Max Measured Temp - device:send(cluster_base.read_manufacturer_specific_attribute( - device, - SINOPE_SWITCH_CLUSTER, - SINOPE_MAX_MEASURED_TEMP_ATTR, - 0x119C - )) + -- -- Read Max Measured Temp + -- device:send(cluster_base.read_manufacturer_specific_attribute( + -- device, + -- SINOPE_SWITCH_CLUSTER, + -- SINOPE_MAX_MEASURED_TEMP_ATTR, + -- 0x119C + -- )) end -- ============================================================================ @@ -224,9 +203,9 @@ end local function do_refresh(driver, device) - log.info("========================================") - log.info("RM3500ZB DOREFRESH HANDLER CALLED") - log.info("========================================") + -- log.info("========================================") + -- log.info("RM3500ZB DOREFRESH HANDLER CALLED") + -- log.info("========================================") -- Refresh all values device:send(TemperatureMeasurement.attributes.MeasuredValue:read(device)) @@ -247,9 +226,9 @@ end -- ============================================================================ local function configure_device(driver, device) - log.info("========================================") - log.info("RM3500ZB CONFIGURE DEVICE HANDLER CALLED") - log.info("========================================") + -- log.info("========================================") + -- log.info("RM3500ZB CONFIGURE DEVICE HANDLER CALLED") + -- log.info("========================================") -- Configure Temperature reporting (every 30s to 5min, on 0.5°C change) device:send(TemperatureMeasurement.attributes.MeasuredValue:configure_reporting( @@ -314,27 +293,18 @@ local function configure_device(driver, device) end - - -local function log_all_unhandled_messages(driver, device, zb_rx) - log.debug("===== INCOMING ZIGBEE MESSAGE =====") - log.debug(zb_rx:pretty_print(zb_utils.MULTI_LINE_FORMAT_CONFIG)) - log.debug("===================================") -end - - -- ============================================================================ -- PREFERENCES CHANGE HANDLER -- ============================================================================ local function info_changed_handler(driver, device, event, args) - log.info("========================================") - log.info("RM3500ZB INFO_CHANGED HANDLER CALLED") - log.info("========================================") + -- log.info("========================================") + -- log.info("RM3500ZB INFO_CHANGED HANDLER CALLED") + -- log.info("========================================") -- -- Check if this is a settings page open (no old preferences) if not args.old_st_store.preferences then - log.info("Settings page opened - refreshing values") + -- log.info("Settings page opened - refreshing values") refresh_settings(device) return end @@ -342,7 +312,7 @@ local function info_changed_handler(driver, device, event, args) -- Handle Low Temperature Protection change if args.old_st_store.preferences.lowTempProtection ~= device.preferences.lowTempProtection then local new_value = tonumber(device.preferences.lowTempProtection) - log.info(string.format("Low temp protection changed to: %d°C", new_value)) + -- log.info(string.format("Low temp protection changed to: %d°C", new_value)) -- Write to device device:send(cluster_base.write_manufacturer_specific_attribute( @@ -366,47 +336,34 @@ local function info_changed_handler(driver, device, event, args) end) end - -- Handle Tank Size change - if args.old_st_store.preferences.tankSize ~= device.preferences.tankSize then - local new_value = device.preferences.tankSize - log.info(string.format("Tank size changed to: %d Gallons", new_value)) + -- -- Handle Tank Size change + -- if args.old_st_store.preferences.tankSize ~= device.preferences.tankSize then + -- local new_value = device.preferences.tankSize + -- log.info(string.format("Tank size changed to: %d Gallons", new_value)) - -- Write to device - device:send(cluster_base.write_manufacturer_specific_attribute( - device, - SINOPE_SWITCH_CLUSTER, - SINOPE_CONNECTED_LOAD_ATTR, - 0x119C, - data_types.Uint16, - new_value - )) + -- -- Write to device + -- device:send(cluster_base.write_manufacturer_specific_attribute( + -- device, + -- SINOPE_SWITCH_CLUSTER, + -- SINOPE_CONNECTED_LOAD_ATTR, + -- 0x119C, + -- data_types.Uint16, + -- new_value + -- )) - -- Read back after 2 seconds to verify - device.thread:call_with_delay(2, function() - log.info("Reading back tankSize after write...") - device:send(cluster_base.read_manufacturer_specific_attribute( - device, - SINOPE_SWITCH_CLUSTER, - SINOPE_CONNECTED_LOAD_ATTR, - 0x119C - )) - end) - end + -- -- Read back after 2 seconds to verify + -- device.thread:call_with_delay(2, function() + -- log.info("Reading back tankSize after write...") + -- device:send(cluster_base.read_manufacturer_specific_attribute( + -- device, + -- SINOPE_SWITCH_CLUSTER, + -- SINOPE_CONNECTED_LOAD_ATTR, + -- 0x119C + -- )) + -- end) + -- end end - - --- ============================================================================ --- THINGS LEFT TO DO --- Refresh setting Value after setting it --- Add Min + Max measured values to preferences --- Add ConnectedLoad to Preferences ? --- Move --- ============================================================================ - - - - -- ============================================================================ -- DRIVER DEFINITION -- ============================================================================ @@ -424,7 +381,6 @@ local sinope_rm3500zb = { infoChanged = info_changed_handler, }, zigbee_handlers = { - fallback = log_all_unhandled_messages, attr = { [ElectricalMeasurement.ID] = { [ElectricalMeasurement.attributes.ActivePower.ID] = active_power_meter_handler, @@ -443,8 +399,8 @@ local sinope_rm3500zb = { }, [SINOPE_SWITCH_CLUSTER] = { [SINOPE_DR_MIN_WATER_TEMP_ATTR] = sinope_low_temp_protection_handler, - [SINOPE_MIN_MEASURED_TEMP_ATTR] = sinope_min_measured_temp_handler, - [SINOPE_MAX_MEASURED_TEMP_ATTR] = sinope_max_measured_temp_handler, + -- [SINOPE_MIN_MEASURED_TEMP_ATTR] = sinope_min_measured_temp_handler, + -- [SINOPE_MAX_MEASURED_TEMP_ATTR] = sinope_max_measured_temp_handler, }, }, }, From ec06eb7c779eefaa1211fbc1045ca480553f0b7d Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Fri, 9 Jan 2026 22:30:06 -0500 Subject: [PATCH 47/49] Cleanup --- .../src/sinope-waterheater/init.lua | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua index 850e553c0f..bb7676a050 100644 --- a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua @@ -1,28 +1,28 @@ -- Copyright 2025 SmartThings, Inc. -- Licensed under the Apache License, Version 2.0 -local cluster_base = require "st.zigbee.cluster_base" -local data_types = require "st.zigbee.data_types" +-- local cluster_base = require "st.zigbee.cluster_base" +-- local data_types = require "st.zigbee.data_types" local capabilities = require "st.capabilities" -local ZigbeeDriver = require "st.zigbee" -local defaults = require "st.zigbee.defaults" +-- local ZigbeeDriver = require "st.zigbee" +-- local defaults = require "st.zigbee.defaults" local clusters = require "st.zigbee.zcl.clusters" -- local log = require "log" local zb_utils = require "st.zigbee.utils" -- Sinopé manufacturer cluster and attributes -local SINOPE_SWITCH_CLUSTER = 0xFF01 -local SINOPE_MAX_INTENSITY_ON_ATTRIBUTE = 0x0052 -local SINOPE_MAX_INTENSITY_OFF_ATTRIBUTE = 0x0053 -local SINOPE_CONNECTED_LOAD_ATTR = 0x0060 -- ConnectedLoad (W) -local SINOPE_CURRENT_LOAD_ATTR = 0x0070 -- CurrentLoad (W-ish bitmap) -local SINOPE_DR_MIN_WATER_TEMP_ATTR = 0x0076 -- drConfigWaterTempMin (°C) -local SINOPE_DR_MIN_WATER_TEMP_TIME_ATTR = 0x0077 -- drConfigWaterTempTime -local SINOPE_TIMER_ATTR = 0x00A0 -- Timer seconds -local SINOPE_TIMER_COUNTDOWN_ATTR = 0x00A1 -- Timer_countDown -local SINOPE_MIN_MEASURED_TEMP_ATTR = 0x007C -- min_measured_temp (°C×100) -local SINOPE_MAX_MEASURED_TEMP_ATTR = 0x007D -- max_measured_temp (°C×100) -local SINOPE_ENERGY_INTERNAL_ATTR = 0x0090 -- currentSummationDelivered (internal) +-- local SINOPE_SWITCH_CLUSTER = 0xFF01 +-- local SINOPE_MAX_INTENSITY_ON_ATTRIBUTE = 0x0052 +-- local SINOPE_MAX_INTENSITY_OFF_ATTRIBUTE = 0x0053 +-- local SINOPE_CONNECTED_LOAD_ATTR = 0x0060 -- ConnectedLoad (W) +-- local SINOPE_CURRENT_LOAD_ATTR = 0x0070 -- CurrentLoad (W-ish bitmap) +-- local SINOPE_DR_MIN_WATER_TEMP_ATTR = 0x0076 -- drConfigWaterTempMin (°C) +-- local SINOPE_DR_MIN_WATER_TEMP_TIME_ATTR = 0x0077 -- drConfigWaterTempTime +-- local SINOPE_TIMER_ATTR = 0x00A0 -- Timer seconds +-- local SINOPE_TIMER_COUNTDOWN_ATTR = 0x00A1 -- Timer_countDown +-- local SINOPE_MIN_MEASURED_TEMP_ATTR = 0x007C -- min_measured_temp (°C×100) +-- local SINOPE_MAX_MEASURED_TEMP_ATTR = 0x007D -- max_measured_temp (°C×100) +-- local SINOPE_ENERGY_INTERNAL_ATTR = 0x0090 -- currentSummationDelivered (internal) -- Standard Zigbee clusters local SimpleMetering = clusters.SimpleMetering -- 0x0702 @@ -30,8 +30,8 @@ local OnOff = clusters.OnOff -- 0x0006 local TemperatureMeasurement = clusters.TemperatureMeasurement -- 0x0402 local IASZone = clusters.IASZone -- 0x0500 local ElectricalMeasurement = clusters.ElectricalMeasurement -- 0x0B04 -local Thermostat = clusters.Thermostat - +-- local Thermostat = clusters.Thermostat + -- ============================================================================ -- STANDARD ATTRIBUTE HANDLERS From d561adf9cc7fb73dd419a8306983126d04188616 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Wed, 21 Jan 2026 07:41:49 -0500 Subject: [PATCH 48/49] Added Temp polling --- .../src/sinope-waterheater/init.lua | 99 ++++++++++++------- 1 file changed, 66 insertions(+), 33 deletions(-) diff --git a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua index bb7676a050..d7ae297c04 100644 --- a/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/sinope-waterheater/init.lua @@ -1,8 +1,8 @@ -- Copyright 2025 SmartThings, Inc. -- Licensed under the Apache License, Version 2.0 --- local cluster_base = require "st.zigbee.cluster_base" --- local data_types = require "st.zigbee.data_types" +local cluster_base = require "st.zigbee.cluster_base" +local data_types = require "st.zigbee.data_types" local capabilities = require "st.capabilities" -- local ZigbeeDriver = require "st.zigbee" -- local defaults = require "st.zigbee.defaults" @@ -11,18 +11,18 @@ local clusters = require "st.zigbee.zcl.clusters" local zb_utils = require "st.zigbee.utils" -- Sinopé manufacturer cluster and attributes --- local SINOPE_SWITCH_CLUSTER = 0xFF01 --- local SINOPE_MAX_INTENSITY_ON_ATTRIBUTE = 0x0052 --- local SINOPE_MAX_INTENSITY_OFF_ATTRIBUTE = 0x0053 --- local SINOPE_CONNECTED_LOAD_ATTR = 0x0060 -- ConnectedLoad (W) --- local SINOPE_CURRENT_LOAD_ATTR = 0x0070 -- CurrentLoad (W-ish bitmap) --- local SINOPE_DR_MIN_WATER_TEMP_ATTR = 0x0076 -- drConfigWaterTempMin (°C) --- local SINOPE_DR_MIN_WATER_TEMP_TIME_ATTR = 0x0077 -- drConfigWaterTempTime --- local SINOPE_TIMER_ATTR = 0x00A0 -- Timer seconds --- local SINOPE_TIMER_COUNTDOWN_ATTR = 0x00A1 -- Timer_countDown --- local SINOPE_MIN_MEASURED_TEMP_ATTR = 0x007C -- min_measured_temp (°C×100) --- local SINOPE_MAX_MEASURED_TEMP_ATTR = 0x007D -- max_measured_temp (°C×100) --- local SINOPE_ENERGY_INTERNAL_ATTR = 0x0090 -- currentSummationDelivered (internal) +local SINOPE_SWITCH_CLUSTER = 0xFF01 +local SINOPE_MAX_INTENSITY_ON_ATTRIBUTE = 0x0052 +local SINOPE_MAX_INTENSITY_OFF_ATTRIBUTE = 0x0053 +local SINOPE_CONNECTED_LOAD_ATTR = 0x0060 -- ConnectedLoad (W) +local SINOPE_CURRENT_LOAD_ATTR = 0x0070 -- CurrentLoad (W-ish bitmap) +local SINOPE_DR_MIN_WATER_TEMP_ATTR = 0x0076 -- drConfigWaterTempMin (°C) +local SINOPE_DR_MIN_WATER_TEMP_TIME_ATTR = 0x0077 -- drConfigWaterTempTime +local SINOPE_TIMER_ATTR = 0x00A0 -- Timer seconds +local SINOPE_TIMER_COUNTDOWN_ATTR = 0x00A1 -- Timer_countDown +local SINOPE_MIN_MEASURED_TEMP_ATTR = 0x007C -- min_measured_temp (°C×100) +local SINOPE_MAX_MEASURED_TEMP_ATTR = 0x007D -- max_measured_temp (°C×100) +local SINOPE_ENERGY_INTERNAL_ATTR = 0x0090 -- currentSummationDelivered (internal) -- Standard Zigbee clusters local SimpleMetering = clusters.SimpleMetering -- 0x0702 @@ -31,7 +31,7 @@ local TemperatureMeasurement = clusters.TemperatureMeasurement -- 0x0402 local IASZone = clusters.IASZone -- 0x0500 local ElectricalMeasurement = clusters.ElectricalMeasurement -- 0x0B04 -- local Thermostat = clusters.Thermostat - + -- ============================================================================ -- STANDARD ATTRIBUTE HANDLERS @@ -56,14 +56,15 @@ local function active_power_meter_handler(driver, device, value, zb_rx) -- log.info("RM3500ZB POWERMETER HANDLER CALLED") device:emit_event(capabilities.powerMeter.power({value = value.value, unit = "W"})) + device:send(TemperatureMeasurement.attributes.MeasuredValue:read(device)) end -local function instantaneous_demand_handler(driver, device, value, zb_rx) - -- log.info("========================================") - -- log.info("RM3500ZB INSTANTMETER HANDLER CALLED") +-- local function instantaneous_demand_handler(driver, device, value, zb_rx) +-- log.info("========================================") +-- log.info("RM3500ZB INSTANTMETER HANDLER CALLED") - device:emit_event(capabilities.powerMeter.power({value = value.value, unit = "W"})) -end +-- device:emit_event(capabilities.powerMeter.power({value = value.value, unit = "W"})) +-- end local function rms_voltage_handler(driver, device, value, zb_rx) -- log.info("========================================") @@ -197,6 +198,27 @@ local function refresh_settings(device) -- )) end +-- ============================================================================ +-- TEMPERATURE POLLING +-- ============================================================================ + +local POLL_TIMER_FIELD = "poll_temperature_timer" +local function schedule_temperature_poll(device) + -- 1. Cancel existing timer if any + local existing = device:get_field(POLL_TIMER_FIELD) + if existing then + device.thread:cancel_timer(existing) -- cancel by handle + end + + -- 2. Create new timer + local handle = device:call_with_delay(600, function(d) + device:send(TemperatureMeasurement.attributes.MeasuredValue:read(device)) + end) + + -- 3. Store handle for next time + device:set_field(POLL_TIMER_FIELD, handle) +end + -- ============================================================================ -- REFRESH ALL VALUES + SETTINGS -- ============================================================================ @@ -209,7 +231,7 @@ local function do_refresh(driver, device) -- Refresh all values device:send(TemperatureMeasurement.attributes.MeasuredValue:read(device)) - device:send(SimpleMetering.attributes.InstantaneousDemand:read(device)) + -- device:send(SimpleMetering.attributes.InstantaneousDemand:read(device)) device:send(SimpleMetering.attributes.CurrentSummationDelivered:read(device)) device:send(ElectricalMeasurement.attributes.ActivePower:read(device)) device:send(ElectricalMeasurement.attributes.RMSVoltage:read(device)) @@ -235,25 +257,33 @@ local function configure_device(driver, device) device, 60, -- min_report_interval (seconds) 900, -- max_report_interval (seconds) - 50 -- reportable_change (0.5°C, in 0.01°C units) + 1 -- reportable_change (0.5°C, in 0.01°C units) )) - -- Configure Power reporting (every 10s to 60s, on 5W change) - device:send(SimpleMetering.attributes.InstantaneousDemand:configure_reporting( - device, - 10, -- min interval: 10 seconds - 900, -- max interval: 15 minute - 50 -- reportable change: 5W (in 0.1W units) - )) + -- -- Configure Power reporting (every 10s to 60s, on 5W change) + -- device:send(SimpleMetering.attributes.InstantaneousDemand:configure_reporting( + -- device, + -- 10, -- min interval: 10 seconds + -- 900, -- max interval: 15 minute + -- 1 -- reportable change: 1W (in 0.1W units) + -- )) -- Configure Energy reporting (every 5min to 30min, on 0.01 kWh change) device:send(SimpleMetering.attributes.CurrentSummationDelivered:configure_reporting( device, - 300, -- min interval: 5 minutes - 1800, -- max interval: 30 minutes + 10, -- min interval: 10 sec + 900, -- max interval: 15 minutes 10 -- reportable change: 0.01 kWh (in 0.001 kWh units) )) + -- Configure Power reporting (every 10s to 60s, on 5W change) + device:send(ElectricalMeasurement.attributes.ActivePower:configure_reporting( + device, + 10, -- min interval: 10 seconds + 900, -- max interval: 15 minute + 1 -- reportable change: 1W (in 0.1W units) + )) + -- Configure Voltage reporting (every 60s to 5min, on 1V change) device:send(ElectricalMeasurement.attributes.RMSVoltage:configure_reporting( device, @@ -287,6 +317,9 @@ local function configure_device(driver, device) )) + -- Start (or restart) temperature polling every 600s + schedule_temperature_poll(device) + -- Refresh all values + settings do_refresh(driver, device) @@ -326,7 +359,7 @@ local function info_changed_handler(driver, device, event, args) -- Read back after 2 seconds to verify device.thread:call_with_delay(2, function() - log.info("Reading back lowTempProtection after write...") + -- log.info("Reading back lowTempProtection after write...") device:send(cluster_base.read_manufacturer_specific_attribute( device, SINOPE_SWITCH_CLUSTER, @@ -388,7 +421,7 @@ local sinope_rm3500zb = { [ElectricalMeasurement.attributes.RMSCurrent.ID] = rms_current_handler }, [SimpleMetering.ID] = { - [SimpleMetering.attributes.InstantaneousDemand.ID] = instantaneous_demand_handler, + -- [SimpleMetering.attributes.InstantaneousDemand.ID] = instantaneous_demand_handler, [SimpleMetering.attributes.CurrentSummationDelivered.ID] = metering_handler }, [TemperatureMeasurement.ID] = { From 6663bdede962e7e2091d5bb1af3057006da12be1 Mon Sep 17 00:00:00 2001 From: xmorand <60904575+xmorand@users.noreply.github.com> Date: Sat, 28 Feb 2026 20:41:38 -0500 Subject: [PATCH 49/49] Added suport for Ikea Inspelning --- drivers/SmartThings/zigbee-switch/fingerprints.yml | 5 +++++ .../zigbee-switch/src/zigbee-switch-power/fingerprints.lua | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-switch/fingerprints.yml b/drivers/SmartThings/zigbee-switch/fingerprints.yml index 5c0ef589ad..36919e00cf 100644 --- a/drivers/SmartThings/zigbee-switch/fingerprints.yml +++ b/drivers/SmartThings/zigbee-switch/fingerprints.yml @@ -930,6 +930,11 @@ zigbeeManufacturer: manufacturer: IKEA of Sweden model: "TRADFRI control outlet" deviceProfileName: basic-switch + - id: IKEA/INSPELNING Smart plug + deviceLabel: INSPELNING Smart plug + manufacturer: IKEA of Sweden + model: "INSPELNING Smart plug" + deviceProfileName: switch-power-energy - id: MEGAMAN/BSZTM005 deviceLabel: INGENIUM Switch manufacturer: MEGAMAN diff --git a/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/fingerprints.lua b/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/fingerprints.lua index d277d36967..3c60f23233 100644 --- a/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/fingerprints.lua +++ b/drivers/SmartThings/zigbee-switch/src/zigbee-switch-power/fingerprints.lua @@ -14,5 +14,6 @@ return { { mfr = "SALUS", model = "SX885ZB" }, { mfr = "AduroSmart Eria", model = "AD-SmartPlug3001" }, { mfr = "AduroSmart Eria", model = "BPU3" }, - { mfr = "AduroSmart Eria", model = "BDP3001" } + { mfr = "AduroSmart Eria", model = "BDP3001" }, + { mfr = "IKEA of Sweden", model = "INSPELNING Smart plug" } }