diff --git a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/attributes/AttributeList.lua b/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/attributes/AttributeList.lua deleted file mode 100644 index 320826dacc..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/attributes/AttributeList.lua +++ /dev/null @@ -1,76 +0,0 @@ -local cluster_base = require "st.matter.cluster_base" -local data_types = require "st.matter.data_types" -local TLVParser = require "st.matter.TLV.TLVParser" - -local AttributeList = { - ID = 0xFFFB, - NAME = "AttributeList", - base_type = require "st.matter.data_types.Array", - element_type = require "st.matter.data_types.Uint32", -} - -function AttributeList:augment_type(data_type_obj) - for i, v in ipairs(data_type_obj.elements) do - data_type_obj.elements[i] = data_types.validate_or_build_type(v, AttributeList.element_type) - end -end - -function AttributeList:new_value(...) - local o = self.base_type(table.unpack({...})) - - return o -end - -function AttributeList:read(device, endpoint_id) - return cluster_base.read( - device, - endpoint_id, - self._cluster.ID, - self.ID, - nil --event_id - ) -end - - -function AttributeList:subscribe(device, endpoint_id) - return cluster_base.subscribe( - device, - endpoint_id, - self._cluster.ID, - self.ID, - nil --event_id - ) -end - -function AttributeList:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function AttributeList:build_test_report_data( - device, - endpoint_id, - value, - status -) - local data = data_types.validate_or_build_type(value, self.base_type) - - return cluster_base.build_test_report_data( - device, - endpoint_id, - self._cluster.ID, - self.ID, - data, - status - ) -end - -function AttributeList:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - - return data -end - -setmetatable(AttributeList, {__call = AttributeList.new_value, __index = AttributeList.base_type}) -return AttributeList - diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/attributes/CumulativeEnergyExported.lua b/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/attributes/CumulativeEnergyExported.lua deleted file mode 100644 index 22befec642..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/attributes/CumulativeEnergyExported.lua +++ /dev/null @@ -1,68 +0,0 @@ -local cluster_base = require "st.matter.cluster_base" -local data_types = require "st.matter.data_types" -local TLVParser = require "st.matter.TLV.TLVParser" - -local CumulativeEnergyExported = { - ID = 0x0002, - NAME = "CumulativeEnergyExported", - base_type = require "ElectricalEnergyMeasurement.types.EnergyMeasurementStruct", -} - -function CumulativeEnergyExported:new_value(...) - local o = self.base_type(table.unpack({...})) - self:augment_type(o) - return o -end - -function CumulativeEnergyExported:read(device, endpoint_id) - return cluster_base.read( - device, - endpoint_id, - self._cluster.ID, - self.ID, - nil - ) -end - -function CumulativeEnergyExported:subscribe(device, endpoint_id) - return cluster_base.subscribe( - device, - endpoint_id, - self._cluster.ID, - self.ID, - nil - ) -end - -function CumulativeEnergyExported:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function CumulativeEnergyExported:build_test_report_data( - device, - endpoint_id, - value, - status -) - local data = data_types.validate_or_build_type(value, self.base_type) - self:augment_type(data) - return cluster_base.build_test_report_data( - device, - endpoint_id, - self._cluster.ID, - self.ID, - data, - status - ) -end - -function CumulativeEnergyExported:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - self:augment_type(data) - return data -end - -setmetatable(CumulativeEnergyExported, {__call = CumulativeEnergyExported.new_value, __index = CumulativeEnergyExported.base_type}) -return CumulativeEnergyExported - diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/attributes/PeriodicEnergyExported.lua b/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/attributes/PeriodicEnergyExported.lua deleted file mode 100644 index 4c1ee29274..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/attributes/PeriodicEnergyExported.lua +++ /dev/null @@ -1,68 +0,0 @@ -local cluster_base = require "st.matter.cluster_base" -local data_types = require "st.matter.data_types" -local TLVParser = require "st.matter.TLV.TLVParser" - -local PeriodicEnergyExported = { - ID = 0x0004, - NAME = "PeriodicEnergyExported", - base_type = require "ElectricalEnergyMeasurement.types.EnergyMeasurementStruct", -} - -function PeriodicEnergyExported:new_value(...) - local o = self.base_type(table.unpack({...})) - self:augment_type(o) - return o -end - -function PeriodicEnergyExported:read(device, endpoint_id) - return cluster_base.read( - device, - endpoint_id, - self._cluster.ID, - self.ID, - nil - ) -end - -function PeriodicEnergyExported:subscribe(device, endpoint_id) - return cluster_base.subscribe( - device, - endpoint_id, - self._cluster.ID, - self.ID, - nil - ) -end - -function PeriodicEnergyExported:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function PeriodicEnergyExported:build_test_report_data( - device, - endpoint_id, - value, - status -) - local data = data_types.validate_or_build_type(value, self.base_type) - self:augment_type(data) - return cluster_base.build_test_report_data( - device, - endpoint_id, - self._cluster.ID, - self.ID, - data, - status - ) -end - -function PeriodicEnergyExported:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - self:augment_type(data) - return data -end - -setmetatable(PeriodicEnergyExported, {__call = PeriodicEnergyExported.new_value, __index = PeriodicEnergyExported.base_type}) -return PeriodicEnergyExported - diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/events/CumulativeEnergyMeasured.lua b/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/events/CumulativeEnergyMeasured.lua deleted file mode 100644 index 13136bd791..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/events/CumulativeEnergyMeasured.lua +++ /dev/null @@ -1,108 +0,0 @@ -local data_types = require "st.matter.data_types" -local cluster_base = require "st.matter.cluster_base" -local TLVParser = require "st.matter.TLV.TLVParser" -local StructureABC = require "st.matter.data_types.base_defs.StructureABC" - -local CumulativeEnergyMeasured = { - ID = 0x0000, - NAME = "CumulativeEnergyMeasured", - base_type = data_types.Structure, -} - -CumulativeEnergyMeasured.field_defs = { - { - name = "energy_imported", - field_id = 0, - is_nullable = false, - is_optional = true, - data_type = require "ElectricalEnergyMeasurement.types.EnergyMeasurementStruct", - }, - { - name = "energy_exported", - field_id = 1, - is_nullable = false, - is_optional = true, - data_type = require "ElectricalEnergyMeasurement.types.EnergyMeasurementStruct", - }, -} - -function CumulativeEnergyMeasured:augment_type(base_type_obj) - local elems = {} - for _, v in ipairs(base_type_obj.elements) do - for _, field_def in ipairs(self.field_defs) do - if field_def.field_id == v.field_id and not - ((field_def.is_nullable or field_def.is_optional) and v.elements == nil) then - elems[field_def.name] = data_types.validate_or_build_type(v, field_def.data_type, field_def.field_name) - if field_def.element_type ~= nil then - for i, e in ipairs(elems[field_def.name].elements) do - elems[field_def.name].elements[i] = data_types.validate_or_build_type(e, field_def.element_type) - end - end - end - end - end - base_type_obj.elements = elems -end - -function CumulativeEnergyMeasured:read(device, endpoint_id) - return cluster_base.read( - device, - endpoint_id, - self._cluster.ID, - nil, --attribute_id - self.ID - ) -end - -function CumulativeEnergyMeasured:subscribe(device, endpoint_id) - return cluster_base.subscribe( - device, - endpoint_id, - self._cluster.ID, - nil, --attribute_id - self.ID - ) -end - -function CumulativeEnergyMeasured:build_test_event_report( - device, - endpoint_id, - fields, - status -) - local data = {} - data.elements = {} - data.num_elements = 0 - setmetatable(data, StructureABC.new_mt({NAME = "CumulativeEnergyMeasuredEventData", ID = 0x15})) - for idx, field_def in ipairs(self.field_defs) do --Note: idx is 1 when field_id is 0 - if (not field_def.is_optional and not field_def.is_nullable) and not fields[field_def.name] then - error("Missing non optional or non_nullable field: " .. field_def.name) - elseif fields[field_def.name] then - data.elements[field_def.name] = data_types.validate_or_build_type(fields[field_def.name], field_def.data_type, field_def.name) - data.elements[field_def.name].field_id = field_def.field_id - data.num_elements = data.num_elements + 1 - end - end - return cluster_base.build_test_event_report( - device, - endpoint_id, - self._cluster.ID, - self.ID, - data, - status - ) -end - -function CumulativeEnergyMeasured:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - self:augment_type(data) - return data -end - -function CumulativeEnergyMeasured:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -return CumulativeEnergyMeasured - diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/events/PeriodicEnergyMeasured.lua b/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/events/PeriodicEnergyMeasured.lua deleted file mode 100644 index e2c4b1b577..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/events/PeriodicEnergyMeasured.lua +++ /dev/null @@ -1,108 +0,0 @@ -local data_types = require "st.matter.data_types" -local cluster_base = require "st.matter.cluster_base" -local TLVParser = require "st.matter.TLV.TLVParser" -local StructureABC = require "st.matter.data_types.base_defs.StructureABC" - -local PeriodicEnergyMeasured = { - ID = 0x0001, - NAME = "PeriodicEnergyMeasured", - base_type = data_types.Structure, -} - -PeriodicEnergyMeasured.field_defs = { - { - name = "energy_imported", - field_id = 0, - is_nullable = false, - is_optional = true, - data_type = require "ElectricalEnergyMeasurement.types.EnergyMeasurementStruct", - }, - { - name = "energy_exported", - field_id = 1, - is_nullable = false, - is_optional = true, - data_type = require "ElectricalEnergyMeasurement.types.EnergyMeasurementStruct", - }, -} - -function PeriodicEnergyMeasured:augment_type(base_type_obj) - local elems = {} - for _, v in ipairs(base_type_obj.elements) do - for _, field_def in ipairs(self.field_defs) do - if field_def.field_id == v.field_id and not - ((field_def.is_nullable or field_def.is_optional) and v.elements == nil) then - elems[field_def.name] = data_types.validate_or_build_type(v, field_def.data_type, field_def.field_name) - if field_def.element_type ~= nil then - for i, e in ipairs(elems[field_def.name].elements) do - elems[field_def.name].elements[i] = data_types.validate_or_build_type(e, field_def.element_type) - end - end - end - end - end - base_type_obj.elements = elems -end - -function PeriodicEnergyMeasured:read(device, endpoint_id) - return cluster_base.read( - device, - endpoint_id, - self._cluster.ID, - nil, --attribute_id - self.ID - ) -end - -function PeriodicEnergyMeasured:subscribe(device, endpoint_id) - return cluster_base.subscribe( - device, - endpoint_id, - self._cluster.ID, - nil, --attribute_id - self.ID - ) -end - -function PeriodicEnergyMeasured:build_test_event_report( - device, - endpoint_id, - fields, - status -) - local data = {} - data.elements = {} - data.num_elements = 0 - setmetatable(data, StructureABC.new_mt({NAME = "PeriodicEnergyMeasuredEventData", ID = 0x15})) - for idx, field_def in ipairs(self.field_defs) do --Note: idx is 1 when field_id is 0 - if (not field_def.is_optional and not field_def.is_nullable) and not fields[field_def.name] then - error("Missing non optional or non_nullable field: " .. field_def.name) - elseif fields[field_def.name] then - data.elements[field_def.name] = data_types.validate_or_build_type(fields[field_def.name], field_def.data_type, field_def.name) - data.elements[field_def.name].field_id = field_def.field_id - data.num_elements = data.num_elements + 1 - end - end - return cluster_base.build_test_event_report( - device, - endpoint_id, - self._cluster.ID, - self.ID, - data, - status - ) -end - -function PeriodicEnergyMeasured:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - self:augment_type(data) - return data -end - -function PeriodicEnergyMeasured:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -return PeriodicEnergyMeasured - diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/events/init.lua b/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/events/init.lua deleted file mode 100644 index 02b085583e..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/events/init.lua +++ /dev/null @@ -1,25 +0,0 @@ -local event_mt = {} -event_mt.__event_cache = {} -event_mt.__index = function(self, key) - if event_mt.__event_cache[key] == nil then - local req_loc = string.format("ElectricalEnergyMeasurement.server.events.%s", key) - local raw_def = require(req_loc) - local cluster = rawget(self, "_cluster") - raw_def:set_parent_cluster(cluster) - event_mt.__event_cache[key] = raw_def - end - return event_mt.__event_cache[key] -end - - -local ElectricalEnergyMeasurementEvents = {} - -function ElectricalEnergyMeasurementEvents:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -setmetatable(ElectricalEnergyMeasurementEvents, event_mt) - -return ElectricalEnergyMeasurementEvents - diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/types/Feature.lua b/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/types/Feature.lua deleted file mode 100644 index 717ba6a2f3..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/types/Feature.lua +++ /dev/null @@ -1,116 +0,0 @@ -local data_types = require "st.matter.data_types" -local UintABC = require "st.matter.data_types.base_defs.UintABC" -local Feature = {} -local new_mt = UintABC.new_mt({NAME = "Feature", ID = data_types.name_to_id_map["Uint32"]}, 4) - -Feature.BASE_MASK = 0xFFFF -Feature.IMPORTED_ENERGY = 0x0001 -Feature.EXPORTED_ENERGY = 0x0002 -Feature.CUMULATIVE_ENERGY = 0x0004 -Feature.PERIODIC_ENERGY = 0x0008 - -Feature.mask_fields = { - BASE_MASK = 0xFFFF, - IMPORTED_ENERGY = 0x0001, - EXPORTED_ENERGY = 0x0002, - CUMULATIVE_ENERGY = 0x0004, - PERIODIC_ENERGY = 0x0008, -} - -Feature.is_imported_energy_set = function(self) - return (self.value & self.IMPORTED_ENERGY) ~= 0 -end - -Feature.set_imported_energy = function(self) - if self.value ~= nil then - self.value = self.value | self.IMPORTED_ENERGY - else - self.value = self.IMPORTED_ENERGY - end -end - -Feature.unset_imported_energy = function(self) - self.value = self.value & (~self.IMPORTED_ENERGY & self.BASE_MASK) -end -Feature.is_exported_energy_set = function(self) - return (self.value & self.EXPORTED_ENERGY) ~= 0 -end - -Feature.set_exported_energy = function(self) - if self.value ~= nil then - self.value = self.value | self.EXPORTED_ENERGY - else - self.value = self.EXPORTED_ENERGY - end -end - -Feature.unset_exported_energy = function(self) - self.value = self.value & (~self.EXPORTED_ENERGY & self.BASE_MASK) -end -Feature.is_cumulative_energy_set = function(self) - return (self.value & self.CUMULATIVE_ENERGY) ~= 0 -end - -Feature.set_cumulative_energy = function(self) - if self.value ~= nil then - self.value = self.value | self.CUMULATIVE_ENERGY - else - self.value = self.CUMULATIVE_ENERGY - end -end - -Feature.unset_cumulative_energy = function(self) - self.value = self.value & (~self.CUMULATIVE_ENERGY & self.BASE_MASK) -end -Feature.is_periodic_energy_set = function(self) - return (self.value & self.PERIODIC_ENERGY) ~= 0 -end - -Feature.set_periodic_energy = function(self) - if self.value ~= nil then - self.value = self.value | self.PERIODIC_ENERGY - else - self.value = self.PERIODIC_ENERGY - end -end - -Feature.unset_periodic_energy = function(self) - self.value = self.value & (~self.PERIODIC_ENERGY & self.BASE_MASK) -end - -function Feature.bits_are_valid(feature) - local max = - Feature.IMPORTED_ENERGY | - Feature.EXPORTED_ENERGY | - Feature.CUMULATIVE_ENERGY | - Feature.PERIODIC_ENERGY - if (feature <= max) and (feature >= 1) then - return true - else - return false - end -end - -Feature.mask_methods = { - is_imported_energy_set = Feature.is_imported_energy_set, - set_imported_energy = Feature.set_imported_energy, - unset_imported_energy = Feature.unset_imported_energy, - is_exported_energy_set = Feature.is_exported_energy_set, - set_exported_energy = Feature.set_exported_energy, - unset_exported_energy = Feature.unset_exported_energy, - is_cumulative_energy_set = Feature.is_cumulative_energy_set, - set_cumulative_energy = Feature.set_cumulative_energy, - unset_cumulative_energy = Feature.unset_cumulative_energy, - is_periodic_energy_set = Feature.is_periodic_energy_set, - set_periodic_energy = Feature.set_periodic_energy, - unset_periodic_energy = Feature.unset_periodic_energy, -} - -Feature.augment_type = function(cls, val) - setmetatable(val, new_mt) -end - -setmetatable(Feature, new_mt) - -return Feature - diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalPowerMeasurement/init.lua b/drivers/SmartThings/matter-thermostat/src/ElectricalPowerMeasurement/init.lua deleted file mode 100644 index 54785d16c6..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalPowerMeasurement/init.lua +++ /dev/null @@ -1,94 +0,0 @@ -local cluster_base = require "st.matter.cluster_base" -local ElectricalPowerMeasurementServerAttributes = require "ElectricalPowerMeasurement.server.attributes" -local ElectricalPowerMeasurementTypes = require "ElectricalPowerMeasurement.types" - -local ElectricalPowerMeasurement = {} - -ElectricalPowerMeasurement.ID = 0x0090 -ElectricalPowerMeasurement.NAME = "ElectricalPowerMeasurement" -ElectricalPowerMeasurement.server = {} -ElectricalPowerMeasurement.client = {} -ElectricalPowerMeasurement.server.attributes = ElectricalPowerMeasurementServerAttributes:set_parent_cluster(ElectricalPowerMeasurement) -ElectricalPowerMeasurement.types = ElectricalPowerMeasurementTypes - -function ElectricalPowerMeasurement:get_attribute_by_id(attr_id) - local attr_id_map = { - [0x0000] = "PowerMode", - [0x0001] = "NumberOfMeasurementTypes", - [0x0002] = "Accuracy", - [0x0003] = "Ranges", - [0x0004] = "Voltage", - [0x0005] = "ActiveCurrent", - [0x0006] = "ReactiveCurrent", - [0x0007] = "ApparentCurrent", - [0x0008] = "ActivePower", - [0x0009] = "ReactivePower", - [0x000A] = "ApparentPower", - [0x000B] = "RMSVoltage", - [0x000C] = "RMSCurrent", - [0x000D] = "RMSPower", - [0x000E] = "Frequency", - [0x000F] = "HarmonicCurrents", - [0x0010] = "HarmonicPhases", - [0x0011] = "PowerFactor", - [0x0012] = "NeutralCurrent", - [0xFFF9] = "AcceptedCommandList", - [0xFFFA] = "EventList", - [0xFFFB] = "AttributeList", - } - local attr_name = attr_id_map[attr_id] - if attr_name ~= nil then - return self.attributes[attr_name] - end - return nil -end - -ElectricalPowerMeasurement.attribute_direction_map = { - ["PowerMode"] = "server", - ["NumberOfMeasurementTypes"] = "server", - ["Accuracy"] = "server", - ["Ranges"] = "server", - ["Voltage"] = "server", - ["ActiveCurrent"] = "server", - ["ReactiveCurrent"] = "server", - ["ApparentCurrent"] = "server", - ["ActivePower"] = "server", - ["ReactivePower"] = "server", - ["ApparentPower"] = "server", - ["RMSVoltage"] = "server", - ["RMSCurrent"] = "server", - ["RMSPower"] = "server", - ["Frequency"] = "server", - ["HarmonicCurrents"] = "server", - ["HarmonicPhases"] = "server", - ["PowerFactor"] = "server", - ["NeutralCurrent"] = "server", - ["AcceptedCommandList"] = "server", - ["EventList"] = "server", - ["AttributeList"] = "server", -} - -ElectricalPowerMeasurement.FeatureMap = ElectricalPowerMeasurement.types.Feature - -function ElectricalPowerMeasurement.are_features_supported(feature, feature_map) - if (ElectricalPowerMeasurement.FeatureMap.bits_are_valid(feature)) then - return (feature & feature_map) == feature - end - return false -end - -local attribute_helper_mt = {} -attribute_helper_mt.__index = function(self, key) - local direction = ElectricalPowerMeasurement.attribute_direction_map[key] - if direction == nil then - error(string.format("Referenced unknown attribute %s on cluster %s", key, ElectricalPowerMeasurement.NAME)) - end - return ElectricalPowerMeasurement[direction].attributes[key] -end -ElectricalPowerMeasurement.attributes = {} -setmetatable(ElectricalPowerMeasurement.attributes, attribute_helper_mt) - -setmetatable(ElectricalPowerMeasurement, {__index = cluster_base}) - -return ElectricalPowerMeasurement - diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalPowerMeasurement/types/Feature.lua b/drivers/SmartThings/matter-thermostat/src/ElectricalPowerMeasurement/types/Feature.lua deleted file mode 100644 index cbda4f3478..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalPowerMeasurement/types/Feature.lua +++ /dev/null @@ -1,138 +0,0 @@ -local data_types = require "st.matter.data_types" -local UintABC = require "st.matter.data_types.base_defs.UintABC" - -local Feature = {} -local new_mt = UintABC.new_mt({NAME = "Feature", ID = data_types.name_to_id_map["Uint32"]}, 4) - -Feature.BASE_MASK = 0xFFFF -Feature.DIRECT_CURRENT = 0x0001 -Feature.ALTERNATING_CURRENT = 0x0002 -Feature.POLYPHASE_POWER = 0x0004 -Feature.HARMONICS = 0x0008 -Feature.POWER_QUALITY = 0x0010 - -Feature.mask_fields = { - BASE_MASK = 0xFFFF, - DIRECT_CURRENT = 0x0001, - ALTERNATING_CURRENT = 0x0002, - POLYPHASE_POWER = 0x0004, - HARMONICS = 0x0008, - POWER_QUALITY = 0x0010, -} - -Feature.is_direct_current_set = function(self) - return (self.value & self.DIRECT_CURRENT) ~= 0 -end - -Feature.set_direct_current = function(self) - if self.value ~= nil then - self.value = self.value | self.DIRECT_CURRENT - else - self.value = self.DIRECT_CURRENT - end -end - -Feature.unset_direct_current = function(self) - self.value = self.value & (~self.DIRECT_CURRENT & self.BASE_MASK) -end -Feature.is_alternating_current_set = function(self) - return (self.value & self.ALTERNATING_CURRENT) ~= 0 -end - -Feature.set_alternating_current = function(self) - if self.value ~= nil then - self.value = self.value | self.ALTERNATING_CURRENT - else - self.value = self.ALTERNATING_CURRENT - end -end - -Feature.unset_alternating_current = function(self) - self.value = self.value & (~self.ALTERNATING_CURRENT & self.BASE_MASK) -end -Feature.is_polyphase_power_set = function(self) - return (self.value & self.POLYPHASE_POWER) ~= 0 -end - -Feature.set_polyphase_power = function(self) - if self.value ~= nil then - self.value = self.value | self.POLYPHASE_POWER - else - self.value = self.POLYPHASE_POWER - end -end - -Feature.unset_polyphase_power = function(self) - self.value = self.value & (~self.POLYPHASE_POWER & self.BASE_MASK) -end -Feature.is_harmonics_set = function(self) - return (self.value & self.HARMONICS) ~= 0 -end - -Feature.set_harmonics = function(self) - if self.value ~= nil then - self.value = self.value | self.HARMONICS - else - self.value = self.HARMONICS - end -end - -Feature.unset_harmonics = function(self) - self.value = self.value & (~self.HARMONICS & self.BASE_MASK) -end -Feature.is_power_quality_set = function(self) - return (self.value & self.POWER_QUALITY) ~= 0 -end - -Feature.set_power_quality = function(self) - if self.value ~= nil then - self.value = self.value | self.POWER_QUALITY - else - self.value = self.POWER_QUALITY - end -end - -Feature.unset_power_quality = function(self) - self.value = self.value & (~self.POWER_QUALITY & self.BASE_MASK) -end - -function Feature.bits_are_valid(feature) - local max = - Feature.DIRECT_CURRENT | - Feature.ALTERNATING_CURRENT | - Feature.POLYPHASE_POWER | - Feature.HARMONICS | - Feature.POWER_QUALITY - if (feature <= max) and (feature >= 1) then - return true - else - return false - end -end - -Feature.mask_methods = { - is_direct_current_set = Feature.is_direct_current_set, - set_direct_current = Feature.set_direct_current, - unset_direct_current = Feature.unset_direct_current, - is_alternating_current_set = Feature.is_alternating_current_set, - set_alternating_current = Feature.set_alternating_current, - unset_alternating_current = Feature.unset_alternating_current, - is_polyphase_power_set = Feature.is_polyphase_power_set, - set_polyphase_power = Feature.set_polyphase_power, - unset_polyphase_power = Feature.unset_polyphase_power, - is_harmonics_set = Feature.is_harmonics_set, - set_harmonics = Feature.set_harmonics, - unset_harmonics = Feature.unset_harmonics, - is_power_quality_set = Feature.is_power_quality_set, - set_power_quality = Feature.set_power_quality, - unset_power_quality = Feature.unset_power_quality, -} - -Feature.augment_type = function(cls, val) - setmetatable(val, new_mt) -end - -setmetatable(Feature, new_mt) - -return Feature - diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalPowerMeasurement/types/init.lua b/drivers/SmartThings/matter-thermostat/src/ElectricalPowerMeasurement/types/init.lua deleted file mode 100644 index 16d13a0688..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalPowerMeasurement/types/init.lua +++ /dev/null @@ -1,15 +0,0 @@ -local types_mt = {} -types_mt.__types_cache = {} -types_mt.__index = function(self, key) - if types_mt.__types_cache[key] == nil then - types_mt.__types_cache[key] = require("ElectricalPowerMeasurement.types." .. key) - end - return types_mt.__types_cache[key] -end - -local ElectricalPowerMeasurementTypes = {} - -setmetatable(ElectricalPowerMeasurementTypes, types_mt) - -return ElectricalPowerMeasurementTypes - diff --git a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/attributes/AttributeList.lua b/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/attributes/AttributeList.lua deleted file mode 100644 index f2ca149f03..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/attributes/AttributeList.lua +++ /dev/null @@ -1,75 +0,0 @@ -local cluster_base = require "st.matter.cluster_base" -local data_types = require "st.matter.data_types" -local TLVParser = require "st.matter.TLV.TLVParser" - -local AttributeList = { - ID = 0xFFFB, - NAME = "AttributeList", - base_type = require "st.matter.data_types.Array", - element_type = require "st.matter.data_types.Uint32", -} - -function AttributeList:augment_type(data_type_obj) - for i, v in ipairs(data_type_obj.elements) do - data_type_obj.elements[i] = data_types.validate_or_build_type(v, AttributeList.element_type) - end -end - -function AttributeList:new_value(...) - local o = self.base_type(table.unpack({...})) - - return o -end - -function AttributeList:read(device, endpoint_id) - return cluster_base.read( - device, - endpoint_id, - self._cluster.ID, - self.ID, - nil --event_id - ) -end - -function AttributeList:subscribe(device, endpoint_id) - return cluster_base.subscribe( - device, - endpoint_id, - self._cluster.ID, - self.ID, - nil --event_id - ) -end - -function AttributeList:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function AttributeList:build_test_report_data( - device, - endpoint_id, - value, - status -) - local data = data_types.validate_or_build_type(value, self.base_type) - - return cluster_base.build_test_report_data( - device, - endpoint_id, - self._cluster.ID, - self.ID, - data, - status - ) -end - -function AttributeList:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - - return data -end - -setmetatable(AttributeList, {__call = AttributeList.new_value, __index = AttributeList.base_type}) -return AttributeList - diff --git a/drivers/SmartThings/matter-thermostat/src/OzoneConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/OzoneConcentrationMeasurement/server/attributes/LevelValue.lua deleted file mode 100644 index 50127a3537..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/OzoneConcentrationMeasurement/server/attributes/LevelValue.lua +++ /dev/null @@ -1,41 +0,0 @@ -local ConcentrationMeasurementServerAttributesLevelValue = require "ConcentrationMeasurement.server.attributes.LevelValue" - -local LevelValue = { - ID = 0x000A, - NAME = "LevelValue", - base_type = require "ConcentrationMeasurement.types.LevelValueEnum", -} - -function LevelValue:new_value(...) - ConcentrationMeasurementServerAttributesLevelValue:new_value(...) -end - -function LevelValue:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesLevelValue:read(device, endpoint_id, self._cluster.ID) -end - -function LevelValue:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesLevelValue:subscribe(device, endpoint_id, self._cluster.ID) -end - -function LevelValue:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function LevelValue:build_test_report_data( - device, - endpoint_id, - value, - status -) - return ConcentrationMeasurementServerAttributesLevelValue:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) -end - -function LevelValue:deserialize(tlv_buf) - return ConcentrationMeasurementServerAttributesLevelValue:deserialize(tlv_buf) -end - -setmetatable(LevelValue, {__call = LevelValue.new_value, __index = LevelValue.base_type}) -return LevelValue - diff --git a/drivers/SmartThings/matter-thermostat/src/OzoneConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/OzoneConcentrationMeasurement/server/attributes/MeasuredValue.lua deleted file mode 100644 index 763bbaa17b..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/OzoneConcentrationMeasurement/server/attributes/MeasuredValue.lua +++ /dev/null @@ -1,56 +0,0 @@ -local cluster_base = require "st.matter.cluster_base" -local data_types = require "st.matter.data_types" -local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasuredValue = require "ConcentrationMeasurement.server.attributes.MeasuredValue" - - -local MeasuredValue = { - ID = 0x0000, - NAME = "MeasuredValue", - base_type = require "st.matter.data_types.SinglePrecisionFloat", -} - -function MeasuredValue:new_value(...) - return ConcentrationMeasurementServerAttributesMeasuredValue:new_value(...) -end - -function MeasuredValue:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasuredValue:read(device, endpoint_id, self._cluster.ID) -end - -function MeasuredValue:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasuredValue:subscribe(device, endpoint_id, self._cluster.ID) -end - -function MeasuredValue:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function MeasuredValue:build_test_report_data( - device, - endpoint_id, - value, - status -) - local data = data_types.validate_or_build_type(value, self.base_type) - - return cluster_base.build_test_report_data( - device, - endpoint_id, - self._cluster.ID, - self.ID, - data, - status - ) -end - -function MeasuredValue:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - - return data -end - -setmetatable(MeasuredValue, {__call = MeasuredValue.new_value, __index = MeasuredValue.base_type}) -return MeasuredValue - diff --git a/drivers/SmartThings/matter-thermostat/src/OzoneConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/OzoneConcentrationMeasurement/server/attributes/MeasurementUnit.lua deleted file mode 100644 index d18f14b09f..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/OzoneConcentrationMeasurement/server/attributes/MeasurementUnit.lua +++ /dev/null @@ -1,45 +0,0 @@ -local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasurementUnit = require "ConcentrationMeasurement.server.attributes.MeasurementUnit" - - -local MeasurementUnit = { - ID = 0x0008, - NAME = "MeasurementUnit", - base_type = require "ConcentrationMeasurement.types.MeasurementUnitEnum", -} - -function MeasurementUnit:new_value(...) - return ConcentrationMeasurementServerAttributesMeasurementUnit:new_value(...) -end - -function MeasurementUnit:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasurementUnit:read(device, endpoint_id, self._cluster.ID) -end - -function MeasurementUnit:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasurementUnit:subscribe(device, endpoint_id, self._cluster.ID) -end - -function MeasurementUnit:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function MeasurementUnit:build_test_report_data( - device, - endpoint_id, - value, - status -) - return ConcentrationMeasurementServerAttributesMeasurementUnit:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) -end - -function MeasurementUnit:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - self:augment_type(data) - return data -end - -setmetatable(MeasurementUnit, {__call = MeasurementUnit.new_value, __index = MeasurementUnit.base_type}) -return MeasurementUnit - diff --git a/drivers/SmartThings/matter-thermostat/src/Pm10ConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/Pm10ConcentrationMeasurement/server/attributes/LevelValue.lua deleted file mode 100644 index 50127a3537..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/Pm10ConcentrationMeasurement/server/attributes/LevelValue.lua +++ /dev/null @@ -1,41 +0,0 @@ -local ConcentrationMeasurementServerAttributesLevelValue = require "ConcentrationMeasurement.server.attributes.LevelValue" - -local LevelValue = { - ID = 0x000A, - NAME = "LevelValue", - base_type = require "ConcentrationMeasurement.types.LevelValueEnum", -} - -function LevelValue:new_value(...) - ConcentrationMeasurementServerAttributesLevelValue:new_value(...) -end - -function LevelValue:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesLevelValue:read(device, endpoint_id, self._cluster.ID) -end - -function LevelValue:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesLevelValue:subscribe(device, endpoint_id, self._cluster.ID) -end - -function LevelValue:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function LevelValue:build_test_report_data( - device, - endpoint_id, - value, - status -) - return ConcentrationMeasurementServerAttributesLevelValue:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) -end - -function LevelValue:deserialize(tlv_buf) - return ConcentrationMeasurementServerAttributesLevelValue:deserialize(tlv_buf) -end - -setmetatable(LevelValue, {__call = LevelValue.new_value, __index = LevelValue.base_type}) -return LevelValue - diff --git a/drivers/SmartThings/matter-thermostat/src/Pm10ConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/Pm10ConcentrationMeasurement/server/attributes/MeasuredValue.lua deleted file mode 100644 index 763bbaa17b..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/Pm10ConcentrationMeasurement/server/attributes/MeasuredValue.lua +++ /dev/null @@ -1,56 +0,0 @@ -local cluster_base = require "st.matter.cluster_base" -local data_types = require "st.matter.data_types" -local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasuredValue = require "ConcentrationMeasurement.server.attributes.MeasuredValue" - - -local MeasuredValue = { - ID = 0x0000, - NAME = "MeasuredValue", - base_type = require "st.matter.data_types.SinglePrecisionFloat", -} - -function MeasuredValue:new_value(...) - return ConcentrationMeasurementServerAttributesMeasuredValue:new_value(...) -end - -function MeasuredValue:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasuredValue:read(device, endpoint_id, self._cluster.ID) -end - -function MeasuredValue:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasuredValue:subscribe(device, endpoint_id, self._cluster.ID) -end - -function MeasuredValue:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function MeasuredValue:build_test_report_data( - device, - endpoint_id, - value, - status -) - local data = data_types.validate_or_build_type(value, self.base_type) - - return cluster_base.build_test_report_data( - device, - endpoint_id, - self._cluster.ID, - self.ID, - data, - status - ) -end - -function MeasuredValue:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - - return data -end - -setmetatable(MeasuredValue, {__call = MeasuredValue.new_value, __index = MeasuredValue.base_type}) -return MeasuredValue - diff --git a/drivers/SmartThings/matter-thermostat/src/Pm10ConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/Pm10ConcentrationMeasurement/server/attributes/MeasurementUnit.lua deleted file mode 100644 index d18f14b09f..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/Pm10ConcentrationMeasurement/server/attributes/MeasurementUnit.lua +++ /dev/null @@ -1,45 +0,0 @@ -local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasurementUnit = require "ConcentrationMeasurement.server.attributes.MeasurementUnit" - - -local MeasurementUnit = { - ID = 0x0008, - NAME = "MeasurementUnit", - base_type = require "ConcentrationMeasurement.types.MeasurementUnitEnum", -} - -function MeasurementUnit:new_value(...) - return ConcentrationMeasurementServerAttributesMeasurementUnit:new_value(...) -end - -function MeasurementUnit:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasurementUnit:read(device, endpoint_id, self._cluster.ID) -end - -function MeasurementUnit:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasurementUnit:subscribe(device, endpoint_id, self._cluster.ID) -end - -function MeasurementUnit:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function MeasurementUnit:build_test_report_data( - device, - endpoint_id, - value, - status -) - return ConcentrationMeasurementServerAttributesMeasurementUnit:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) -end - -function MeasurementUnit:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - self:augment_type(data) - return data -end - -setmetatable(MeasurementUnit, {__call = MeasurementUnit.new_value, __index = MeasurementUnit.base_type}) -return MeasurementUnit - diff --git a/drivers/SmartThings/matter-thermostat/src/Pm1ConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/Pm1ConcentrationMeasurement/server/attributes/LevelValue.lua deleted file mode 100644 index 50127a3537..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/Pm1ConcentrationMeasurement/server/attributes/LevelValue.lua +++ /dev/null @@ -1,41 +0,0 @@ -local ConcentrationMeasurementServerAttributesLevelValue = require "ConcentrationMeasurement.server.attributes.LevelValue" - -local LevelValue = { - ID = 0x000A, - NAME = "LevelValue", - base_type = require "ConcentrationMeasurement.types.LevelValueEnum", -} - -function LevelValue:new_value(...) - ConcentrationMeasurementServerAttributesLevelValue:new_value(...) -end - -function LevelValue:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesLevelValue:read(device, endpoint_id, self._cluster.ID) -end - -function LevelValue:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesLevelValue:subscribe(device, endpoint_id, self._cluster.ID) -end - -function LevelValue:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function LevelValue:build_test_report_data( - device, - endpoint_id, - value, - status -) - return ConcentrationMeasurementServerAttributesLevelValue:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) -end - -function LevelValue:deserialize(tlv_buf) - return ConcentrationMeasurementServerAttributesLevelValue:deserialize(tlv_buf) -end - -setmetatable(LevelValue, {__call = LevelValue.new_value, __index = LevelValue.base_type}) -return LevelValue - diff --git a/drivers/SmartThings/matter-thermostat/src/Pm1ConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/Pm1ConcentrationMeasurement/server/attributes/MeasuredValue.lua deleted file mode 100644 index 763bbaa17b..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/Pm1ConcentrationMeasurement/server/attributes/MeasuredValue.lua +++ /dev/null @@ -1,56 +0,0 @@ -local cluster_base = require "st.matter.cluster_base" -local data_types = require "st.matter.data_types" -local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasuredValue = require "ConcentrationMeasurement.server.attributes.MeasuredValue" - - -local MeasuredValue = { - ID = 0x0000, - NAME = "MeasuredValue", - base_type = require "st.matter.data_types.SinglePrecisionFloat", -} - -function MeasuredValue:new_value(...) - return ConcentrationMeasurementServerAttributesMeasuredValue:new_value(...) -end - -function MeasuredValue:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasuredValue:read(device, endpoint_id, self._cluster.ID) -end - -function MeasuredValue:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasuredValue:subscribe(device, endpoint_id, self._cluster.ID) -end - -function MeasuredValue:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function MeasuredValue:build_test_report_data( - device, - endpoint_id, - value, - status -) - local data = data_types.validate_or_build_type(value, self.base_type) - - return cluster_base.build_test_report_data( - device, - endpoint_id, - self._cluster.ID, - self.ID, - data, - status - ) -end - -function MeasuredValue:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - - return data -end - -setmetatable(MeasuredValue, {__call = MeasuredValue.new_value, __index = MeasuredValue.base_type}) -return MeasuredValue - diff --git a/drivers/SmartThings/matter-thermostat/src/Pm1ConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/Pm1ConcentrationMeasurement/server/attributes/MeasurementUnit.lua deleted file mode 100644 index d18f14b09f..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/Pm1ConcentrationMeasurement/server/attributes/MeasurementUnit.lua +++ /dev/null @@ -1,45 +0,0 @@ -local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasurementUnit = require "ConcentrationMeasurement.server.attributes.MeasurementUnit" - - -local MeasurementUnit = { - ID = 0x0008, - NAME = "MeasurementUnit", - base_type = require "ConcentrationMeasurement.types.MeasurementUnitEnum", -} - -function MeasurementUnit:new_value(...) - return ConcentrationMeasurementServerAttributesMeasurementUnit:new_value(...) -end - -function MeasurementUnit:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasurementUnit:read(device, endpoint_id, self._cluster.ID) -end - -function MeasurementUnit:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasurementUnit:subscribe(device, endpoint_id, self._cluster.ID) -end - -function MeasurementUnit:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function MeasurementUnit:build_test_report_data( - device, - endpoint_id, - value, - status -) - return ConcentrationMeasurementServerAttributesMeasurementUnit:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) -end - -function MeasurementUnit:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - self:augment_type(data) - return data -end - -setmetatable(MeasurementUnit, {__call = MeasurementUnit.new_value, __index = MeasurementUnit.base_type}) -return MeasurementUnit - diff --git a/drivers/SmartThings/matter-thermostat/src/Pm25ConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/Pm25ConcentrationMeasurement/server/attributes/LevelValue.lua deleted file mode 100644 index 50127a3537..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/Pm25ConcentrationMeasurement/server/attributes/LevelValue.lua +++ /dev/null @@ -1,41 +0,0 @@ -local ConcentrationMeasurementServerAttributesLevelValue = require "ConcentrationMeasurement.server.attributes.LevelValue" - -local LevelValue = { - ID = 0x000A, - NAME = "LevelValue", - base_type = require "ConcentrationMeasurement.types.LevelValueEnum", -} - -function LevelValue:new_value(...) - ConcentrationMeasurementServerAttributesLevelValue:new_value(...) -end - -function LevelValue:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesLevelValue:read(device, endpoint_id, self._cluster.ID) -end - -function LevelValue:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesLevelValue:subscribe(device, endpoint_id, self._cluster.ID) -end - -function LevelValue:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function LevelValue:build_test_report_data( - device, - endpoint_id, - value, - status -) - return ConcentrationMeasurementServerAttributesLevelValue:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) -end - -function LevelValue:deserialize(tlv_buf) - return ConcentrationMeasurementServerAttributesLevelValue:deserialize(tlv_buf) -end - -setmetatable(LevelValue, {__call = LevelValue.new_value, __index = LevelValue.base_type}) -return LevelValue - diff --git a/drivers/SmartThings/matter-thermostat/src/Pm25ConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/Pm25ConcentrationMeasurement/server/attributes/MeasuredValue.lua deleted file mode 100644 index 763bbaa17b..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/Pm25ConcentrationMeasurement/server/attributes/MeasuredValue.lua +++ /dev/null @@ -1,56 +0,0 @@ -local cluster_base = require "st.matter.cluster_base" -local data_types = require "st.matter.data_types" -local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasuredValue = require "ConcentrationMeasurement.server.attributes.MeasuredValue" - - -local MeasuredValue = { - ID = 0x0000, - NAME = "MeasuredValue", - base_type = require "st.matter.data_types.SinglePrecisionFloat", -} - -function MeasuredValue:new_value(...) - return ConcentrationMeasurementServerAttributesMeasuredValue:new_value(...) -end - -function MeasuredValue:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasuredValue:read(device, endpoint_id, self._cluster.ID) -end - -function MeasuredValue:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasuredValue:subscribe(device, endpoint_id, self._cluster.ID) -end - -function MeasuredValue:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function MeasuredValue:build_test_report_data( - device, - endpoint_id, - value, - status -) - local data = data_types.validate_or_build_type(value, self.base_type) - - return cluster_base.build_test_report_data( - device, - endpoint_id, - self._cluster.ID, - self.ID, - data, - status - ) -end - -function MeasuredValue:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - - return data -end - -setmetatable(MeasuredValue, {__call = MeasuredValue.new_value, __index = MeasuredValue.base_type}) -return MeasuredValue - diff --git a/drivers/SmartThings/matter-thermostat/src/Pm25ConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/Pm25ConcentrationMeasurement/server/attributes/MeasurementUnit.lua deleted file mode 100644 index d18f14b09f..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/Pm25ConcentrationMeasurement/server/attributes/MeasurementUnit.lua +++ /dev/null @@ -1,45 +0,0 @@ -local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasurementUnit = require "ConcentrationMeasurement.server.attributes.MeasurementUnit" - - -local MeasurementUnit = { - ID = 0x0008, - NAME = "MeasurementUnit", - base_type = require "ConcentrationMeasurement.types.MeasurementUnitEnum", -} - -function MeasurementUnit:new_value(...) - return ConcentrationMeasurementServerAttributesMeasurementUnit:new_value(...) -end - -function MeasurementUnit:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasurementUnit:read(device, endpoint_id, self._cluster.ID) -end - -function MeasurementUnit:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasurementUnit:subscribe(device, endpoint_id, self._cluster.ID) -end - -function MeasurementUnit:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function MeasurementUnit:build_test_report_data( - device, - endpoint_id, - value, - status -) - return ConcentrationMeasurementServerAttributesMeasurementUnit:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) -end - -function MeasurementUnit:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - self:augment_type(data) - return data -end - -setmetatable(MeasurementUnit, {__call = MeasurementUnit.new_value, __index = MeasurementUnit.base_type}) -return MeasurementUnit - diff --git a/drivers/SmartThings/matter-thermostat/src/RadonConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/RadonConcentrationMeasurement/server/attributes/LevelValue.lua deleted file mode 100644 index 50127a3537..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/RadonConcentrationMeasurement/server/attributes/LevelValue.lua +++ /dev/null @@ -1,41 +0,0 @@ -local ConcentrationMeasurementServerAttributesLevelValue = require "ConcentrationMeasurement.server.attributes.LevelValue" - -local LevelValue = { - ID = 0x000A, - NAME = "LevelValue", - base_type = require "ConcentrationMeasurement.types.LevelValueEnum", -} - -function LevelValue:new_value(...) - ConcentrationMeasurementServerAttributesLevelValue:new_value(...) -end - -function LevelValue:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesLevelValue:read(device, endpoint_id, self._cluster.ID) -end - -function LevelValue:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesLevelValue:subscribe(device, endpoint_id, self._cluster.ID) -end - -function LevelValue:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function LevelValue:build_test_report_data( - device, - endpoint_id, - value, - status -) - return ConcentrationMeasurementServerAttributesLevelValue:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) -end - -function LevelValue:deserialize(tlv_buf) - return ConcentrationMeasurementServerAttributesLevelValue:deserialize(tlv_buf) -end - -setmetatable(LevelValue, {__call = LevelValue.new_value, __index = LevelValue.base_type}) -return LevelValue - diff --git a/drivers/SmartThings/matter-thermostat/src/RadonConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/RadonConcentrationMeasurement/server/attributes/MeasuredValue.lua deleted file mode 100644 index 763bbaa17b..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/RadonConcentrationMeasurement/server/attributes/MeasuredValue.lua +++ /dev/null @@ -1,56 +0,0 @@ -local cluster_base = require "st.matter.cluster_base" -local data_types = require "st.matter.data_types" -local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasuredValue = require "ConcentrationMeasurement.server.attributes.MeasuredValue" - - -local MeasuredValue = { - ID = 0x0000, - NAME = "MeasuredValue", - base_type = require "st.matter.data_types.SinglePrecisionFloat", -} - -function MeasuredValue:new_value(...) - return ConcentrationMeasurementServerAttributesMeasuredValue:new_value(...) -end - -function MeasuredValue:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasuredValue:read(device, endpoint_id, self._cluster.ID) -end - -function MeasuredValue:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasuredValue:subscribe(device, endpoint_id, self._cluster.ID) -end - -function MeasuredValue:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function MeasuredValue:build_test_report_data( - device, - endpoint_id, - value, - status -) - local data = data_types.validate_or_build_type(value, self.base_type) - - return cluster_base.build_test_report_data( - device, - endpoint_id, - self._cluster.ID, - self.ID, - data, - status - ) -end - -function MeasuredValue:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - - return data -end - -setmetatable(MeasuredValue, {__call = MeasuredValue.new_value, __index = MeasuredValue.base_type}) -return MeasuredValue - diff --git a/drivers/SmartThings/matter-thermostat/src/RadonConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/RadonConcentrationMeasurement/server/attributes/MeasurementUnit.lua deleted file mode 100644 index d18f14b09f..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/RadonConcentrationMeasurement/server/attributes/MeasurementUnit.lua +++ /dev/null @@ -1,45 +0,0 @@ -local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasurementUnit = require "ConcentrationMeasurement.server.attributes.MeasurementUnit" - - -local MeasurementUnit = { - ID = 0x0008, - NAME = "MeasurementUnit", - base_type = require "ConcentrationMeasurement.types.MeasurementUnitEnum", -} - -function MeasurementUnit:new_value(...) - return ConcentrationMeasurementServerAttributesMeasurementUnit:new_value(...) -end - -function MeasurementUnit:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasurementUnit:read(device, endpoint_id, self._cluster.ID) -end - -function MeasurementUnit:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasurementUnit:subscribe(device, endpoint_id, self._cluster.ID) -end - -function MeasurementUnit:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function MeasurementUnit:build_test_report_data( - device, - endpoint_id, - value, - status -) - return ConcentrationMeasurementServerAttributesMeasurementUnit:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) -end - -function MeasurementUnit:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - self:augment_type(data) - return data -end - -setmetatable(MeasurementUnit, {__call = MeasurementUnit.new_value, __index = MeasurementUnit.base_type}) -return MeasurementUnit - diff --git a/drivers/SmartThings/matter-thermostat/src/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/LevelValue.lua deleted file mode 100644 index 50127a3537..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/LevelValue.lua +++ /dev/null @@ -1,41 +0,0 @@ -local ConcentrationMeasurementServerAttributesLevelValue = require "ConcentrationMeasurement.server.attributes.LevelValue" - -local LevelValue = { - ID = 0x000A, - NAME = "LevelValue", - base_type = require "ConcentrationMeasurement.types.LevelValueEnum", -} - -function LevelValue:new_value(...) - ConcentrationMeasurementServerAttributesLevelValue:new_value(...) -end - -function LevelValue:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesLevelValue:read(device, endpoint_id, self._cluster.ID) -end - -function LevelValue:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesLevelValue:subscribe(device, endpoint_id, self._cluster.ID) -end - -function LevelValue:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function LevelValue:build_test_report_data( - device, - endpoint_id, - value, - status -) - return ConcentrationMeasurementServerAttributesLevelValue:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) -end - -function LevelValue:deserialize(tlv_buf) - return ConcentrationMeasurementServerAttributesLevelValue:deserialize(tlv_buf) -end - -setmetatable(LevelValue, {__call = LevelValue.new_value, __index = LevelValue.base_type}) -return LevelValue - diff --git a/drivers/SmartThings/matter-thermostat/src/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/MeasuredValue.lua deleted file mode 100644 index 763bbaa17b..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/MeasuredValue.lua +++ /dev/null @@ -1,56 +0,0 @@ -local cluster_base = require "st.matter.cluster_base" -local data_types = require "st.matter.data_types" -local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasuredValue = require "ConcentrationMeasurement.server.attributes.MeasuredValue" - - -local MeasuredValue = { - ID = 0x0000, - NAME = "MeasuredValue", - base_type = require "st.matter.data_types.SinglePrecisionFloat", -} - -function MeasuredValue:new_value(...) - return ConcentrationMeasurementServerAttributesMeasuredValue:new_value(...) -end - -function MeasuredValue:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasuredValue:read(device, endpoint_id, self._cluster.ID) -end - -function MeasuredValue:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasuredValue:subscribe(device, endpoint_id, self._cluster.ID) -end - -function MeasuredValue:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function MeasuredValue:build_test_report_data( - device, - endpoint_id, - value, - status -) - local data = data_types.validate_or_build_type(value, self.base_type) - - return cluster_base.build_test_report_data( - device, - endpoint_id, - self._cluster.ID, - self.ID, - data, - status - ) -end - -function MeasuredValue:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - - return data -end - -setmetatable(MeasuredValue, {__call = MeasuredValue.new_value, __index = MeasuredValue.base_type}) -return MeasuredValue - diff --git a/drivers/SmartThings/matter-thermostat/src/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/MeasurementUnit.lua deleted file mode 100644 index d18f14b09f..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/MeasurementUnit.lua +++ /dev/null @@ -1,45 +0,0 @@ -local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasurementUnit = require "ConcentrationMeasurement.server.attributes.MeasurementUnit" - - -local MeasurementUnit = { - ID = 0x0008, - NAME = "MeasurementUnit", - base_type = require "ConcentrationMeasurement.types.MeasurementUnitEnum", -} - -function MeasurementUnit:new_value(...) - return ConcentrationMeasurementServerAttributesMeasurementUnit:new_value(...) -end - -function MeasurementUnit:read(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasurementUnit:read(device, endpoint_id, self._cluster.ID) -end - -function MeasurementUnit:subscribe(device, endpoint_id) - return ConcentrationMeasurementServerAttributesMeasurementUnit:subscribe(device, endpoint_id, self._cluster.ID) -end - -function MeasurementUnit:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function MeasurementUnit:build_test_report_data( - device, - endpoint_id, - value, - status -) - return ConcentrationMeasurementServerAttributesMeasurementUnit:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) -end - -function MeasurementUnit:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - self:augment_type(data) - return data -end - -setmetatable(MeasurementUnit, {__call = MeasurementUnit.new_value, __index = MeasurementUnit.base_type}) -return MeasurementUnit - diff --git a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/attributes/AcceptedCommandList.lua b/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/attributes/AcceptedCommandList.lua deleted file mode 100644 index 6a8d95df1d..0000000000 --- a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/attributes/AcceptedCommandList.lua +++ /dev/null @@ -1,75 +0,0 @@ -local cluster_base = require "st.matter.cluster_base" -local data_types = require "st.matter.data_types" -local TLVParser = require "st.matter.TLV.TLVParser" - -local AcceptedCommandList = { - ID = 0xFFF9, - NAME = "AcceptedCommandList", - base_type = require "st.matter.data_types.Array", - element_type = require "st.matter.data_types.Uint32", -} - -function AcceptedCommandList:augment_type(data_type_obj) - for i, v in ipairs(data_type_obj.elements) do - data_type_obj.elements[i] = data_types.validate_or_build_type(v, AcceptedCommandList.element_type) - end -end - -function AcceptedCommandList:new_value(...) - local o = self.base_type(table.unpack({...})) - - return o -end - -function AcceptedCommandList:read(device, endpoint_id) - return cluster_base.read( - device, - endpoint_id, - self._cluster.ID, - self.ID, - nil - ) -end - -function AcceptedCommandList:subscribe(device, endpoint_id) - return cluster_base.subscribe( - device, - endpoint_id, - self._cluster.ID, - self.ID, - nil - ) -end - -function AcceptedCommandList:set_parent_cluster(cluster) - self._cluster = cluster - return self -end - -function AcceptedCommandList:build_test_report_data( - device, - endpoint_id, - value, - status -) - local data = data_types.validate_or_build_type(value, self.base_type) - - return cluster_base.build_test_report_data( - device, - endpoint_id, - self._cluster.ID, - self.ID, - data, - status - ) -end - -function AcceptedCommandList:deserialize(tlv_buf) - local data = TLVParser.decode_tlv(tlv_buf) - - return data -end - -setmetatable(AcceptedCommandList, {__call = AcceptedCommandList.new_value, __index = AcceptedCommandList.base_type}) -return AcceptedCommandList - diff --git a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/init.lua similarity index 90% rename from drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/init.lua index 3fa5f37100..7bfdfd7248 100644 --- a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/init.lua @@ -1,7 +1,10 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local ActivatedCarbonFilterMonitoringServerAttributes = require "ActivatedCarbonFilterMonitoring.server.attributes" -local ActivatedCarbonFilterMonitoringServerCommands = require "ActivatedCarbonFilterMonitoring.server.commands" -local ActivatedCarbonFilterMonitoringTypes = require "ActivatedCarbonFilterMonitoring.types" +local ActivatedCarbonFilterMonitoringServerAttributes = require "embedded_clusters.ActivatedCarbonFilterMonitoring.server.attributes" +local ActivatedCarbonFilterMonitoringServerCommands = require "embedded_clusters.ActivatedCarbonFilterMonitoring.server.commands" +local ActivatedCarbonFilterMonitoringTypes = require "embedded_clusters.ActivatedCarbonFilterMonitoring.types" local ActivatedCarbonFilterMonitoring = {} diff --git a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/attributes/ChangeIndication.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/server/attributes/ChangeIndication.lua similarity index 88% rename from drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/attributes/ChangeIndication.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/server/attributes/ChangeIndication.lua index 4980356485..df65143e66 100644 --- a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/attributes/ChangeIndication.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/server/attributes/ChangeIndication.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" @@ -5,7 +8,7 @@ local TLVParser = require "st.matter.TLV.TLVParser" local ChangeIndication = { ID = 0x0002, NAME = "ChangeIndication", - base_type = require "ActivatedCarbonFilterMonitoring.types.ChangeIndicationEnum", + base_type = require "embedded_clusters.ActivatedCarbonFilterMonitoring.types.ChangeIndicationEnum", } function ChangeIndication:new_value(...) diff --git a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/attributes/Condition.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/server/attributes/Condition.lua similarity index 89% rename from drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/attributes/Condition.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/server/attributes/Condition.lua index e668aa4c48..361e423a1c 100644 --- a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/attributes/Condition.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/server/attributes/Condition.lua @@ -1,4 +1,7 @@ -local cluster_base = require "st.matter.cluster_base" +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" diff --git a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/server/attributes/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/server/attributes/init.lua index a02378a50d..1ecdf6a931 100644 --- a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("ActivatedCarbonFilterMonitoring.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.ActivatedCarbonFilterMonitoring.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/commands/ResetCondition.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/server/commands/ResetCondition.lua similarity index 96% rename from drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/commands/ResetCondition.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/server/commands/ResetCondition.lua index 040ce653b4..1ac942f780 100644 --- a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/commands/ResetCondition.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/server/commands/ResetCondition.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" diff --git a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/commands/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/server/commands/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/commands/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/server/commands/init.lua index 7f641481d4..ec343da578 100644 --- a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/commands/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/server/commands/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local command_mt = {} command_mt.__command_cache = {} command_mt.__index = function(self, key) if command_mt.__command_cache[key] == nil then - local req_loc = string.format("ActivatedCarbonFilterMonitoring.server.commands.%s", key) + local req_loc = string.format("embedded_clusters.ActivatedCarbonFilterMonitoring.server.commands.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") command_mt.__command_cache[key] = raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/types/ChangeIndicationEnum.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/types/ChangeIndicationEnum.lua similarity index 92% rename from drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/types/ChangeIndicationEnum.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/types/ChangeIndicationEnum.lua index 438de24c94..6b5ab62494 100644 --- a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/types/ChangeIndicationEnum.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/types/ChangeIndicationEnum.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local UintABC = require "st.matter.data_types.base_defs.UintABC" diff --git a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/types/Feature.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/types/Feature.lua similarity index 96% rename from drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/types/Feature.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/types/Feature.lua index 88474d1b0f..906e769676 100644 --- a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/types/Feature.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/types/Feature.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local UintABC = require "st.matter.data_types.base_defs.UintABC" diff --git a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/types/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/types/init.lua similarity index 62% rename from drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/types/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/types/init.lua index 2ff8e6e89a..7cdc2d1561 100644 --- a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/types/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ActivatedCarbonFilterMonitoring/types/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local types_mt = {} types_mt.__types_cache = {} types_mt.__index = function(self, key) if types_mt.__types_cache[key] == nil then - types_mt.__types_cache[key] = require("ActivatedCarbonFilterMonitoring.types." .. key) + types_mt.__types_cache[key] = require("embedded_clusters.ActivatedCarbonFilterMonitoring.types." .. key) end return types_mt.__types_cache[key] end diff --git a/drivers/SmartThings/matter-thermostat/src/AirQuality/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/init.lua similarity index 86% rename from drivers/SmartThings/matter-thermostat/src/AirQuality/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/init.lua index 78e77fce51..4f6d19cc9f 100644 --- a/drivers/SmartThings/matter-thermostat/src/AirQuality/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/init.lua @@ -1,6 +1,9 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local AirQualityServerAttributes = require "AirQuality.server.attributes" -local AirQualityTypes = require "AirQuality.types" +local AirQualityServerAttributes = require "embedded_clusters.AirQuality.server.attributes" +local AirQualityTypes = require "embedded_clusters.AirQuality.types" local AirQuality = {} diff --git a/drivers/SmartThings/matter-thermostat/src/AirQuality/server/attributes/AcceptedCommandList.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/server/attributes/AcceptedCommandList.lua similarity index 94% rename from drivers/SmartThings/matter-thermostat/src/AirQuality/server/attributes/AcceptedCommandList.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/server/attributes/AcceptedCommandList.lua index 6a8d95df1d..a1e56c6597 100644 --- a/drivers/SmartThings/matter-thermostat/src/AirQuality/server/attributes/AcceptedCommandList.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/server/attributes/AcceptedCommandList.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" diff --git a/drivers/SmartThings/matter-thermostat/src/AirQuality/server/attributes/AirQuality.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/server/attributes/AirQuality.lua similarity index 88% rename from drivers/SmartThings/matter-thermostat/src/AirQuality/server/attributes/AirQuality.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/server/attributes/AirQuality.lua index f92f43543a..1beb6218f9 100644 --- a/drivers/SmartThings/matter-thermostat/src/AirQuality/server/attributes/AirQuality.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/server/attributes/AirQuality.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" @@ -5,7 +8,7 @@ local TLVParser = require "st.matter.TLV.TLVParser" local AirQuality = { ID = 0x0000, NAME = "AirQuality", - base_type = require "AirQuality.types.AirQualityEnum", + base_type = require "embedded_clusters.AirQuality.types.AirQualityEnum", } function AirQuality:new_value(...) diff --git a/drivers/SmartThings/matter-thermostat/src/AirQuality/server/attributes/AttributeList.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/server/attributes/AttributeList.lua similarity index 94% rename from drivers/SmartThings/matter-thermostat/src/AirQuality/server/attributes/AttributeList.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/server/attributes/AttributeList.lua index 93e96817e6..238b50ade3 100644 --- a/drivers/SmartThings/matter-thermostat/src/AirQuality/server/attributes/AttributeList.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/server/attributes/AttributeList.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" diff --git a/drivers/SmartThings/matter-thermostat/src/AirQuality/server/attributes/EventList.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/server/attributes/EventList.lua similarity index 94% rename from drivers/SmartThings/matter-thermostat/src/AirQuality/server/attributes/EventList.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/server/attributes/EventList.lua index 69155cd7ca..719f17a231 100644 --- a/drivers/SmartThings/matter-thermostat/src/AirQuality/server/attributes/EventList.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/server/attributes/EventList.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" diff --git a/drivers/SmartThings/matter-thermostat/src/AirQuality/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/server/attributes/init.lua similarity index 75% rename from drivers/SmartThings/matter-thermostat/src/AirQuality/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/server/attributes/init.lua index aef3b476a9..50295b081a 100644 --- a/drivers/SmartThings/matter-thermostat/src/AirQuality/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("AirQuality.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.AirQuality.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/AirQuality/types/AirQualityEnum.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/types/AirQualityEnum.lua similarity index 94% rename from drivers/SmartThings/matter-thermostat/src/AirQuality/types/AirQualityEnum.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/types/AirQualityEnum.lua index 317a42dc9b..c2c255614a 100644 --- a/drivers/SmartThings/matter-thermostat/src/AirQuality/types/AirQualityEnum.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/types/AirQualityEnum.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local UintABC = require "st.matter.data_types.base_defs.UintABC" diff --git a/drivers/SmartThings/matter-thermostat/src/AirQuality/types/Feature.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/types/Feature.lua similarity index 96% rename from drivers/SmartThings/matter-thermostat/src/AirQuality/types/Feature.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/types/Feature.lua index 86b90ce627..906a09a2bb 100644 --- a/drivers/SmartThings/matter-thermostat/src/AirQuality/types/Feature.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/types/Feature.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local UintABC = require "st.matter.data_types.base_defs.UintABC" diff --git a/drivers/SmartThings/matter-thermostat/src/AirQuality/types/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/types/init.lua similarity index 60% rename from drivers/SmartThings/matter-thermostat/src/AirQuality/types/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/types/init.lua index 88a2b861b7..b77d67de82 100644 --- a/drivers/SmartThings/matter-thermostat/src/AirQuality/types/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/AirQuality/types/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local types_mt = {} types_mt.__types_cache = {} types_mt.__index = function(self, key) if types_mt.__types_cache[key] == nil then - types_mt.__types_cache[key] = require("AirQuality.types." .. key) + types_mt.__types_cache[key] = require("embedded_clusters.AirQuality.types." .. key) end return types_mt.__types_cache[key] end diff --git a/drivers/SmartThings/matter-thermostat/src/CarbonDioxideConcentrationMeasurement/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonDioxideConcentrationMeasurement/init.lua similarity index 88% rename from drivers/SmartThings/matter-thermostat/src/CarbonDioxideConcentrationMeasurement/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonDioxideConcentrationMeasurement/init.lua index f5109f8943..4de97147e4 100644 --- a/drivers/SmartThings/matter-thermostat/src/CarbonDioxideConcentrationMeasurement/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonDioxideConcentrationMeasurement/init.lua @@ -1,6 +1,9 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local CarbonDioxideConcentrationMeasurementServerAttributes = require "CarbonDioxideConcentrationMeasurement.server.attributes" -local ConcentrationMeasurement = require "ConcentrationMeasurement" +local CarbonDioxideConcentrationMeasurementServerAttributes = require "embedded_clusters.CarbonDioxideConcentrationMeasurement.server.attributes" +local ConcentrationMeasurement = require "embedded_clusters.ConcentrationMeasurement" local CarbonDioxideConcentrationMeasurement = {} diff --git a/drivers/SmartThings/matter-thermostat/src/CarbonMonoxideConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonDioxideConcentrationMeasurement/server/attributes/LevelValue.lua similarity index 81% rename from drivers/SmartThings/matter-thermostat/src/CarbonMonoxideConcentrationMeasurement/server/attributes/LevelValue.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonDioxideConcentrationMeasurement/server/attributes/LevelValue.lua index 50127a3537..cc8f7b47eb 100644 --- a/drivers/SmartThings/matter-thermostat/src/CarbonMonoxideConcentrationMeasurement/server/attributes/LevelValue.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonDioxideConcentrationMeasurement/server/attributes/LevelValue.lua @@ -1,9 +1,12 @@ -local ConcentrationMeasurementServerAttributesLevelValue = require "ConcentrationMeasurement.server.attributes.LevelValue" +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local ConcentrationMeasurementServerAttributesLevelValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.LevelValue" local LevelValue = { ID = 0x000A, NAME = "LevelValue", - base_type = require "ConcentrationMeasurement.types.LevelValueEnum", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.LevelValueEnum", } function LevelValue:new_value(...) diff --git a/drivers/SmartThings/matter-thermostat/src/NitrogenDioxideConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonDioxideConcentrationMeasurement/server/attributes/MeasuredValue.lua similarity index 89% rename from drivers/SmartThings/matter-thermostat/src/NitrogenDioxideConcentrationMeasurement/server/attributes/MeasuredValue.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonDioxideConcentrationMeasurement/server/attributes/MeasuredValue.lua index 763bbaa17b..ca0dafb0dc 100644 --- a/drivers/SmartThings/matter-thermostat/src/NitrogenDioxideConcentrationMeasurement/server/attributes/MeasuredValue.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonDioxideConcentrationMeasurement/server/attributes/MeasuredValue.lua @@ -1,7 +1,10 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasuredValue = require "ConcentrationMeasurement.server.attributes.MeasuredValue" +local ConcentrationMeasurementServerAttributesMeasuredValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasuredValue" local MeasuredValue = { diff --git a/drivers/SmartThings/matter-thermostat/src/CarbonDioxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonDioxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua similarity index 82% rename from drivers/SmartThings/matter-thermostat/src/CarbonDioxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonDioxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua index d18f14b09f..9597906d3a 100644 --- a/drivers/SmartThings/matter-thermostat/src/CarbonDioxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonDioxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua @@ -1,11 +1,14 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasurementUnit = require "ConcentrationMeasurement.server.attributes.MeasurementUnit" +local ConcentrationMeasurementServerAttributesMeasurementUnit = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasurementUnit" local MeasurementUnit = { ID = 0x0008, NAME = "MeasurementUnit", - base_type = require "ConcentrationMeasurement.types.MeasurementUnitEnum", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.MeasurementUnitEnum", } function MeasurementUnit:new_value(...) diff --git a/drivers/SmartThings/matter-thermostat/src/CarbonDioxideConcentrationMeasurement/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonDioxideConcentrationMeasurement/server/attributes/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/CarbonDioxideConcentrationMeasurement/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonDioxideConcentrationMeasurement/server/attributes/init.lua index 93583c2080..0206213e6f 100644 --- a/drivers/SmartThings/matter-thermostat/src/CarbonDioxideConcentrationMeasurement/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonDioxideConcentrationMeasurement/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("CarbonDioxideConcentrationMeasurement.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.CarbonDioxideConcentrationMeasurement.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/CarbonMonoxideConcentrationMeasurement/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonMonoxideConcentrationMeasurement/init.lua similarity index 88% rename from drivers/SmartThings/matter-thermostat/src/CarbonMonoxideConcentrationMeasurement/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonMonoxideConcentrationMeasurement/init.lua index e8cdb487f5..a6e1f24d1d 100644 --- a/drivers/SmartThings/matter-thermostat/src/CarbonMonoxideConcentrationMeasurement/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonMonoxideConcentrationMeasurement/init.lua @@ -1,6 +1,9 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local CarbonMonoxideConcentrationMeasurementServerAttributes = require "CarbonMonoxideConcentrationMeasurement.server.attributes" -local ConcentrationMeasurement = require "ConcentrationMeasurement" +local CarbonMonoxideConcentrationMeasurementServerAttributes = require "embedded_clusters.CarbonMonoxideConcentrationMeasurement.server.attributes" +local ConcentrationMeasurement = require "embedded_clusters.ConcentrationMeasurement" local CarbonMonoxideConcentrationMeasurement = {} diff --git a/drivers/SmartThings/matter-thermostat/src/CarbonDioxideConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonMonoxideConcentrationMeasurement/server/attributes/LevelValue.lua similarity index 81% rename from drivers/SmartThings/matter-thermostat/src/CarbonDioxideConcentrationMeasurement/server/attributes/LevelValue.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonMonoxideConcentrationMeasurement/server/attributes/LevelValue.lua index 50127a3537..cc8f7b47eb 100644 --- a/drivers/SmartThings/matter-thermostat/src/CarbonDioxideConcentrationMeasurement/server/attributes/LevelValue.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonMonoxideConcentrationMeasurement/server/attributes/LevelValue.lua @@ -1,9 +1,12 @@ -local ConcentrationMeasurementServerAttributesLevelValue = require "ConcentrationMeasurement.server.attributes.LevelValue" +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local ConcentrationMeasurementServerAttributesLevelValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.LevelValue" local LevelValue = { ID = 0x000A, NAME = "LevelValue", - base_type = require "ConcentrationMeasurement.types.LevelValueEnum", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.LevelValueEnum", } function LevelValue:new_value(...) diff --git a/drivers/SmartThings/matter-thermostat/src/CarbonMonoxideConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonMonoxideConcentrationMeasurement/server/attributes/MeasuredValue.lua similarity index 89% rename from drivers/SmartThings/matter-thermostat/src/CarbonMonoxideConcentrationMeasurement/server/attributes/MeasuredValue.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonMonoxideConcentrationMeasurement/server/attributes/MeasuredValue.lua index 763bbaa17b..ca0dafb0dc 100644 --- a/drivers/SmartThings/matter-thermostat/src/CarbonMonoxideConcentrationMeasurement/server/attributes/MeasuredValue.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonMonoxideConcentrationMeasurement/server/attributes/MeasuredValue.lua @@ -1,7 +1,10 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasuredValue = require "ConcentrationMeasurement.server.attributes.MeasuredValue" +local ConcentrationMeasurementServerAttributesMeasuredValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasuredValue" local MeasuredValue = { diff --git a/drivers/SmartThings/matter-thermostat/src/CarbonMonoxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonMonoxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua similarity index 82% rename from drivers/SmartThings/matter-thermostat/src/CarbonMonoxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonMonoxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua index d18f14b09f..9597906d3a 100644 --- a/drivers/SmartThings/matter-thermostat/src/CarbonMonoxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonMonoxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua @@ -1,11 +1,14 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasurementUnit = require "ConcentrationMeasurement.server.attributes.MeasurementUnit" +local ConcentrationMeasurementServerAttributesMeasurementUnit = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasurementUnit" local MeasurementUnit = { ID = 0x0008, NAME = "MeasurementUnit", - base_type = require "ConcentrationMeasurement.types.MeasurementUnitEnum", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.MeasurementUnitEnum", } function MeasurementUnit:new_value(...) diff --git a/drivers/SmartThings/matter-thermostat/src/CarbonMonoxideConcentrationMeasurement/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonMonoxideConcentrationMeasurement/server/attributes/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/CarbonMonoxideConcentrationMeasurement/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonMonoxideConcentrationMeasurement/server/attributes/init.lua index 2307e8977d..1a7e7b508c 100644 --- a/drivers/SmartThings/matter-thermostat/src/CarbonMonoxideConcentrationMeasurement/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/CarbonMonoxideConcentrationMeasurement/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("CarbonMonoxideConcentrationMeasurement.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.CarbonMonoxideConcentrationMeasurement.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/init.lua similarity index 92% rename from drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/init.lua index 596d1bfe80..cb4cffa2d2 100644 --- a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/init.lua @@ -1,6 +1,9 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local ConcentrationMeasurementServerAttributes = require "ConcentrationMeasurement.server.attributes" -local ConcentrationMeasurementTypes = require "ConcentrationMeasurement.types" +local ConcentrationMeasurementServerAttributes = require "embedded_clusters.ConcentrationMeasurement.server.attributes" +local ConcentrationMeasurementTypes = require "embedded_clusters.ConcentrationMeasurement.types" local ConcentrationMeasurement = {} diff --git a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/server/attributes/LevelValue.lua similarity index 88% rename from drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/server/attributes/LevelValue.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/server/attributes/LevelValue.lua index e7023a336c..e4f88c8491 100644 --- a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/server/attributes/LevelValue.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/server/attributes/LevelValue.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" @@ -5,7 +8,7 @@ local TLVParser = require "st.matter.TLV.TLVParser" local LevelValue = { ID = 0x000A, NAME = "LevelValue", - base_type = require "ConcentrationMeasurement.types.LevelValueEnum", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.LevelValueEnum", } function LevelValue:new_value(...) diff --git a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/server/attributes/MeasuredValue.lua similarity index 93% rename from drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/server/attributes/MeasuredValue.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/server/attributes/MeasuredValue.lua index c658d2d3aa..2ab739841f 100644 --- a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/server/attributes/MeasuredValue.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/server/attributes/MeasuredValue.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" diff --git a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/server/attributes/MeasurementUnit.lua similarity index 88% rename from drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/server/attributes/MeasurementUnit.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/server/attributes/MeasurementUnit.lua index 3d50e8b97b..0fa14745c0 100644 --- a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/server/attributes/MeasurementUnit.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/server/attributes/MeasurementUnit.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" @@ -5,7 +8,7 @@ local TLVParser = require "st.matter.TLV.TLVParser" local MeasurementUnit = { ID = 0x0008, NAME = "MeasurementUnit", - base_type = require "ConcentrationMeasurement.types.MeasurementUnitEnum", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.MeasurementUnitEnum", } function MeasurementUnit:new_value(...) diff --git a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/server/attributes/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/server/attributes/init.lua index a1a0092151..19cde9aa55 100644 --- a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("ConcentrationMeasurement.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.ConcentrationMeasurement.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/types/Feature.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/types/Feature.lua similarity index 98% rename from drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/types/Feature.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/types/Feature.lua index 9aa2413903..0bb19bec62 100644 --- a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/types/Feature.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/types/Feature.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local UintABC = require "st.matter.data_types.base_defs.UintABC" diff --git a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/types/LevelValueEnum.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/types/LevelValueEnum.lua similarity index 93% rename from drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/types/LevelValueEnum.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/types/LevelValueEnum.lua index b1264d72b8..02b4f727df 100644 --- a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/types/LevelValueEnum.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/types/LevelValueEnum.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local UintABC = require "st.matter.data_types.base_defs.UintABC" diff --git a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/types/MeasurementUnitEnum.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/types/MeasurementUnitEnum.lua similarity index 94% rename from drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/types/MeasurementUnitEnum.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/types/MeasurementUnitEnum.lua index c8302c5cc4..6efd90901a 100644 --- a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/types/MeasurementUnitEnum.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/types/MeasurementUnitEnum.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local UintABC = require "st.matter.data_types.base_defs.UintABC" diff --git a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/types/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/types/init.lua similarity index 62% rename from drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/types/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/types/init.lua index f6da1e6b62..c339f17414 100644 --- a/drivers/SmartThings/matter-thermostat/src/ConcentrationMeasurement/types/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ConcentrationMeasurement/types/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local types_mt = {} types_mt.__types_cache = {} types_mt.__index = function(self, key) if types_mt.__types_cache[key] == nil then - types_mt.__types_cache[key] = require("ConcentrationMeasurement.types." .. key) + types_mt.__types_cache[key] = require("embedded_clusters.ConcentrationMeasurement.types." .. key) end return types_mt.__types_cache[key] end diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/init.lua similarity index 61% rename from drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/init.lua index bc5685ae75..0f564673a9 100644 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/init.lua @@ -1,7 +1,9 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local ElectricalEnergyMeasurementServerAttributes = require "ElectricalEnergyMeasurement.server.attributes" -local ElectricalEnergyMeasurementEvents = require "ElectricalEnergyMeasurement.server.events" -local ElectricalEnergyMeasurementTypes = require "ElectricalEnergyMeasurement.types" +local ElectricalEnergyMeasurementServerAttributes = require "embedded_clusters.ElectricalEnergyMeasurement.server.attributes" +local ElectricalEnergyMeasurementTypes = require "embedded_clusters.ElectricalEnergyMeasurement.types" local ElectricalEnergyMeasurement = {} ElectricalEnergyMeasurement.ID = 0x0091 @@ -9,20 +11,12 @@ ElectricalEnergyMeasurement.NAME = "ElectricalEnergyMeasurement" ElectricalEnergyMeasurement.server = {} ElectricalEnergyMeasurement.client = {} ElectricalEnergyMeasurement.server.attributes = ElectricalEnergyMeasurementServerAttributes:set_parent_cluster(ElectricalEnergyMeasurement) -ElectricalEnergyMeasurement.server.events = ElectricalEnergyMeasurementEvents:set_parent_cluster(ElectricalEnergyMeasurement) ElectricalEnergyMeasurement.types = ElectricalEnergyMeasurementTypes function ElectricalEnergyMeasurement:get_attribute_by_id(attr_id) local attr_id_map = { - [0x0000] = "Accuracy", [0x0001] = "CumulativeEnergyImported", - [0x0002] = "CumulativeEnergyExported", [0x0003] = "PeriodicEnergyImported", - [0x0004] = "PeriodicEnergyExported", - [0x0005] = "CumulativeEnergyReset", - [0xFFF9] = "AcceptedCommandList", - [0xFFFA] = "EventList", - [0xFFFB] = "AttributeList", } local attr_name = attr_id_map[attr_id] if attr_name ~= nil then @@ -32,15 +26,8 @@ function ElectricalEnergyMeasurement:get_attribute_by_id(attr_id) end ElectricalEnergyMeasurement.attribute_direction_map = { - ["Accuracy"] = "server", ["CumulativeEnergyImported"] = "server", - ["CumulativeEnergyExported"] = "server", ["PeriodicEnergyImported"] = "server", - ["PeriodicEnergyExported"] = "server", - ["CumulativeEnergyReset"] = "server", - ["AcceptedCommandList"] = "server", - ["EventList"] = "server", - ["AttributeList"] = "server", } ElectricalEnergyMeasurement.FeatureMap = ElectricalEnergyMeasurement.types.Feature @@ -63,13 +50,6 @@ end ElectricalEnergyMeasurement.attributes = {} setmetatable(ElectricalEnergyMeasurement.attributes, attribute_helper_mt) -local event_helper_mt = {} -event_helper_mt.__index = function(self, key) - return ElectricalEnergyMeasurement.server.events[key] -end -ElectricalEnergyMeasurement.events = {} -setmetatable(ElectricalEnergyMeasurement.events, event_helper_mt) - setmetatable(ElectricalEnergyMeasurement, {__index = cluster_base}) return ElectricalEnergyMeasurement diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/attributes/CumulativeEnergyImported.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/server/attributes/CumulativeEnergyImported.lua similarity index 88% rename from drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/attributes/CumulativeEnergyImported.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/server/attributes/CumulativeEnergyImported.lua index 3dc58635e1..2d41790440 100644 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/attributes/CumulativeEnergyImported.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/server/attributes/CumulativeEnergyImported.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" @@ -5,7 +8,7 @@ local TLVParser = require "st.matter.TLV.TLVParser" local CumulativeEnergyImported = { ID = 0x0001, NAME = "CumulativeEnergyImported", - base_type = require "ElectricalEnergyMeasurement.types.EnergyMeasurementStruct", + base_type = require "embedded_clusters.ElectricalEnergyMeasurement.types.EnergyMeasurementStruct", } function CumulativeEnergyImported:new_value(...) diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/attributes/PeriodicEnergyImported.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/server/attributes/PeriodicEnergyImported.lua similarity index 88% rename from drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/attributes/PeriodicEnergyImported.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/server/attributes/PeriodicEnergyImported.lua index 753b91ea2d..5daccf48ab 100644 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/attributes/PeriodicEnergyImported.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/server/attributes/PeriodicEnergyImported.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" @@ -5,7 +8,7 @@ local TLVParser = require "st.matter.TLV.TLVParser" local PeriodicEnergyImported = { ID = 0x0003, NAME = "PeriodicEnergyImported", - base_type = require "ElectricalEnergyMeasurement.types.EnergyMeasurementStruct", + base_type = require "embedded_clusters.ElectricalEnergyMeasurement.types.EnergyMeasurementStruct", } function PeriodicEnergyImported:new_value(...) diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/server/attributes/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/server/attributes/init.lua index adfdf42bbf..57bc0d1f72 100644 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("ElectricalEnergyMeasurement.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.ElectricalEnergyMeasurement.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/types/EnergyMeasurementStruct.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/types/EnergyMeasurementStruct.lua similarity index 97% rename from drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/types/EnergyMeasurementStruct.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/types/EnergyMeasurementStruct.lua index 950b260227..a4c58a3646 100644 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/types/EnergyMeasurementStruct.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/types/EnergyMeasurementStruct.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local StructureABC = require "st.matter.data_types.base_defs.StructureABC" local EnergyMeasurementStruct = {} diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/types/Feature.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/types/Feature.lua new file mode 100644 index 0000000000..e3db76f49d --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/types/Feature.lua @@ -0,0 +1,31 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local data_types = require "st.matter.data_types" +local UintABC = require "st.matter.data_types.base_defs.UintABC" +local Feature = {} +local new_mt = UintABC.new_mt({NAME = "Feature", ID = data_types.name_to_id_map["Uint32"]}, 4) + +Feature.BASE_MASK = 0xFFFF +Feature.IMPORTED_ENERGY = 0x0001 +Feature.EXPORTED_ENERGY = 0x0002 +Feature.CUMULATIVE_ENERGY = 0x0004 +Feature.PERIODIC_ENERGY = 0x0008 + +function Feature.bits_are_valid(feature) + local max = + Feature.IMPORTED_ENERGY | + Feature.EXPORTED_ENERGY | + Feature.CUMULATIVE_ENERGY | + Feature.PERIODIC_ENERGY + if (feature <= max) and (feature >= 1) then + return true + else + return false + end +end + +setmetatable(Feature, new_mt) + +return Feature + diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/types/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/types/init.lua similarity index 62% rename from drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/types/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/types/init.lua index bb0c39fe0e..ec29b53e05 100644 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalEnergyMeasurement/types/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalEnergyMeasurement/types/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local types_mt = {} types_mt.__types_cache = {} types_mt.__index = function(self, key) if types_mt.__types_cache[key] == nil then - types_mt.__types_cache[key] = require("ElectricalEnergyMeasurement.types." .. key) + types_mt.__types_cache[key] = require("embedded_clusters.ElectricalEnergyMeasurement.types." .. key) end return types_mt.__types_cache[key] end diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalPowerMeasurement/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalPowerMeasurement/init.lua new file mode 100644 index 0000000000..c9061e3cc4 --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalPowerMeasurement/init.lua @@ -0,0 +1,44 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local cluster_base = require "st.matter.cluster_base" +local ElectricalPowerMeasurementServerAttributes = require "embedded_clusters.ElectricalPowerMeasurement.server.attributes" + +local ElectricalPowerMeasurement = {} + +ElectricalPowerMeasurement.ID = 0x0090 +ElectricalPowerMeasurement.NAME = "ElectricalPowerMeasurement" +ElectricalPowerMeasurement.server = {} +ElectricalPowerMeasurement.client = {} +ElectricalPowerMeasurement.server.attributes = ElectricalPowerMeasurementServerAttributes:set_parent_cluster(ElectricalPowerMeasurement) + +function ElectricalPowerMeasurement:get_attribute_by_id(attr_id) + local attr_id_map = { + [0x0008] = "ActivePower", + } + local attr_name = attr_id_map[attr_id] + if attr_name ~= nil then + return self.attributes[attr_name] + end + return nil +end + +ElectricalPowerMeasurement.attribute_direction_map = { + ["ActivePower"] = "server", +} + +local attribute_helper_mt = {} +attribute_helper_mt.__index = function(self, key) + local direction = ElectricalPowerMeasurement.attribute_direction_map[key] + if direction == nil then + error(string.format("Referenced unknown attribute %s on cluster %s", key, ElectricalPowerMeasurement.NAME)) + end + return ElectricalPowerMeasurement[direction].attributes[key] +end +ElectricalPowerMeasurement.attributes = {} +setmetatable(ElectricalPowerMeasurement.attributes, attribute_helper_mt) + +setmetatable(ElectricalPowerMeasurement, {__index = cluster_base}) + +return ElectricalPowerMeasurement + diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalPowerMeasurement/server/attributes/ActivePower.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalPowerMeasurement/server/attributes/ActivePower.lua similarity index 93% rename from drivers/SmartThings/matter-thermostat/src/ElectricalPowerMeasurement/server/attributes/ActivePower.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalPowerMeasurement/server/attributes/ActivePower.lua index 6c34abd2f4..f1696509f5 100644 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalPowerMeasurement/server/attributes/ActivePower.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalPowerMeasurement/server/attributes/ActivePower.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" @@ -65,4 +68,3 @@ end setmetatable(ActivePower, {__call = ActivePower.new_value, __index = ActivePower.base_type}) return ActivePower - diff --git a/drivers/SmartThings/matter-thermostat/src/ElectricalPowerMeasurement/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalPowerMeasurement/server/attributes/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/ElectricalPowerMeasurement/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalPowerMeasurement/server/attributes/init.lua index 0c30fa8dd4..6de69b94ff 100644 --- a/drivers/SmartThings/matter-thermostat/src/ElectricalPowerMeasurement/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/ElectricalPowerMeasurement/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("ElectricalPowerMeasurement.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.ElectricalPowerMeasurement.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/FormaldehydeConcentrationMeasurement/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/FormaldehydeConcentrationMeasurement/init.lua similarity index 88% rename from drivers/SmartThings/matter-thermostat/src/FormaldehydeConcentrationMeasurement/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/FormaldehydeConcentrationMeasurement/init.lua index 5920a9dc66..cdfd1d597e 100644 --- a/drivers/SmartThings/matter-thermostat/src/FormaldehydeConcentrationMeasurement/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/FormaldehydeConcentrationMeasurement/init.lua @@ -1,6 +1,9 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local FormaldehydeConcentrationMeasurementServerAttributes = require "FormaldehydeConcentrationMeasurement.server.attributes" -local ConcentrationMeasurement = require "ConcentrationMeasurement" +local FormaldehydeConcentrationMeasurementServerAttributes = require "embedded_clusters.FormaldehydeConcentrationMeasurement.server.attributes" +local ConcentrationMeasurement = require "embedded_clusters.ConcentrationMeasurement" local FormaldehydeConcentrationMeasurement = {} diff --git a/drivers/SmartThings/matter-thermostat/src/NitrogenDioxideConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/FormaldehydeConcentrationMeasurement/server/attributes/LevelValue.lua similarity index 81% rename from drivers/SmartThings/matter-thermostat/src/NitrogenDioxideConcentrationMeasurement/server/attributes/LevelValue.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/FormaldehydeConcentrationMeasurement/server/attributes/LevelValue.lua index 50127a3537..cc8f7b47eb 100644 --- a/drivers/SmartThings/matter-thermostat/src/NitrogenDioxideConcentrationMeasurement/server/attributes/LevelValue.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/FormaldehydeConcentrationMeasurement/server/attributes/LevelValue.lua @@ -1,9 +1,12 @@ -local ConcentrationMeasurementServerAttributesLevelValue = require "ConcentrationMeasurement.server.attributes.LevelValue" +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local ConcentrationMeasurementServerAttributesLevelValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.LevelValue" local LevelValue = { ID = 0x000A, NAME = "LevelValue", - base_type = require "ConcentrationMeasurement.types.LevelValueEnum", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.LevelValueEnum", } function LevelValue:new_value(...) diff --git a/drivers/SmartThings/matter-thermostat/src/CarbonDioxideConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/FormaldehydeConcentrationMeasurement/server/attributes/MeasuredValue.lua similarity index 89% rename from drivers/SmartThings/matter-thermostat/src/CarbonDioxideConcentrationMeasurement/server/attributes/MeasuredValue.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/FormaldehydeConcentrationMeasurement/server/attributes/MeasuredValue.lua index 763bbaa17b..ca0dafb0dc 100644 --- a/drivers/SmartThings/matter-thermostat/src/CarbonDioxideConcentrationMeasurement/server/attributes/MeasuredValue.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/FormaldehydeConcentrationMeasurement/server/attributes/MeasuredValue.lua @@ -1,7 +1,10 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasuredValue = require "ConcentrationMeasurement.server.attributes.MeasuredValue" +local ConcentrationMeasurementServerAttributesMeasuredValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasuredValue" local MeasuredValue = { diff --git a/drivers/SmartThings/matter-thermostat/src/FormaldehydeConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/FormaldehydeConcentrationMeasurement/server/attributes/MeasurementUnit.lua similarity index 82% rename from drivers/SmartThings/matter-thermostat/src/FormaldehydeConcentrationMeasurement/server/attributes/MeasurementUnit.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/FormaldehydeConcentrationMeasurement/server/attributes/MeasurementUnit.lua index d18f14b09f..9597906d3a 100644 --- a/drivers/SmartThings/matter-thermostat/src/FormaldehydeConcentrationMeasurement/server/attributes/MeasurementUnit.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/FormaldehydeConcentrationMeasurement/server/attributes/MeasurementUnit.lua @@ -1,11 +1,14 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasurementUnit = require "ConcentrationMeasurement.server.attributes.MeasurementUnit" +local ConcentrationMeasurementServerAttributesMeasurementUnit = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasurementUnit" local MeasurementUnit = { ID = 0x0008, NAME = "MeasurementUnit", - base_type = require "ConcentrationMeasurement.types.MeasurementUnitEnum", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.MeasurementUnitEnum", } function MeasurementUnit:new_value(...) diff --git a/drivers/SmartThings/matter-thermostat/src/FormaldehydeConcentrationMeasurement/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/FormaldehydeConcentrationMeasurement/server/attributes/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/FormaldehydeConcentrationMeasurement/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/FormaldehydeConcentrationMeasurement/server/attributes/init.lua index 37900b0fb1..3dee13abe3 100644 --- a/drivers/SmartThings/matter-thermostat/src/FormaldehydeConcentrationMeasurement/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/FormaldehydeConcentrationMeasurement/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("FormaldehydeConcentrationMeasurement.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.FormaldehydeConcentrationMeasurement.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/init.lua similarity index 89% rename from drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/init.lua index 84400d7833..7a36d3aa92 100644 --- a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/init.lua @@ -1,7 +1,10 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local HepaFilterMonitoringServerAttributes = require "HepaFilterMonitoring.server.attributes" -local HepaFilterMonitoringServerCommands = require "HepaFilterMonitoring.server.commands" -local HepaFilterMonitoringTypes = require "HepaFilterMonitoring.types" +local HepaFilterMonitoringServerAttributes = require "embedded_clusters.HepaFilterMonitoring.server.attributes" +local HepaFilterMonitoringServerCommands = require "embedded_clusters.HepaFilterMonitoring.server.commands" +local HepaFilterMonitoringTypes = require "embedded_clusters.HepaFilterMonitoring.types" local HepaFilterMonitoring = {} diff --git a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/attributes/ChangeIndication.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/server/attributes/ChangeIndication.lua similarity index 88% rename from drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/attributes/ChangeIndication.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/server/attributes/ChangeIndication.lua index 955b89eb88..9dca020204 100644 --- a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/attributes/ChangeIndication.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/server/attributes/ChangeIndication.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" @@ -5,7 +8,7 @@ local TLVParser = require "st.matter.TLV.TLVParser" local ChangeIndication = { ID = 0x0002, NAME = "ChangeIndication", - base_type = require "HepaFilterMonitoring.types.ChangeIndicationEnum", + base_type = require "embedded_clusters.HepaFilterMonitoring.types.ChangeIndicationEnum", } function ChangeIndication:new_value(...) diff --git a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/attributes/Condition.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/server/attributes/Condition.lua similarity index 93% rename from drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/attributes/Condition.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/server/attributes/Condition.lua index e668aa4c48..76a4fd9760 100644 --- a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/attributes/Condition.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/server/attributes/Condition.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" diff --git a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/server/attributes/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/server/attributes/init.lua index 8d7ffe6c00..2590980846 100644 --- a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("HepaFilterMonitoring.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.HepaFilterMonitoring.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/commands/ResetCondition.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/server/commands/ResetCondition.lua similarity index 96% rename from drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/commands/ResetCondition.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/server/commands/ResetCondition.lua index 040ce653b4..1ac942f780 100644 --- a/drivers/SmartThings/matter-thermostat/src/ActivatedCarbonFilterMonitoring/server/commands/ResetCondition.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/server/commands/ResetCondition.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" diff --git a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/commands/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/server/commands/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/commands/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/server/commands/init.lua index 55a4ea7a30..77ae141f77 100644 --- a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/server/commands/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/server/commands/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local command_mt = {} command_mt.__command_cache = {} command_mt.__index = function(self, key) if command_mt.__command_cache[key] == nil then - local req_loc = string.format("HepaFilterMonitoring.server.commands.%s", key) + local req_loc = string.format("embedded_clusters.HepaFilterMonitoring.server.commands.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") command_mt.__command_cache[key] = raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/types/ChangeIndicationEnum.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/types/ChangeIndicationEnum.lua similarity index 92% rename from drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/types/ChangeIndicationEnum.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/types/ChangeIndicationEnum.lua index 438de24c94..6b5ab62494 100644 --- a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/types/ChangeIndicationEnum.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/types/ChangeIndicationEnum.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local UintABC = require "st.matter.data_types.base_defs.UintABC" diff --git a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/types/Feature.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/types/Feature.lua similarity index 96% rename from drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/types/Feature.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/types/Feature.lua index 88474d1b0f..906e769676 100644 --- a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/types/Feature.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/types/Feature.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local UintABC = require "st.matter.data_types.base_defs.UintABC" diff --git a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/types/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/types/init.lua similarity index 61% rename from drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/types/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/types/init.lua index 77aca088ff..6c36af1aec 100644 --- a/drivers/SmartThings/matter-thermostat/src/HepaFilterMonitoring/types/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/HepaFilterMonitoring/types/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local types_mt = {} types_mt.__types_cache = {} types_mt.__index = function(self, key) if types_mt.__types_cache[key] == nil then - types_mt.__types_cache[key] = require("HepaFilterMonitoring.types." .. key) + types_mt.__types_cache[key] = require("embedded_clusters.HepaFilterMonitoring.types." .. key) end return types_mt.__types_cache[key] end diff --git a/drivers/SmartThings/matter-thermostat/src/NitrogenDioxideConcentrationMeasurement/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/NitrogenDioxideConcentrationMeasurement/init.lua similarity index 88% rename from drivers/SmartThings/matter-thermostat/src/NitrogenDioxideConcentrationMeasurement/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/NitrogenDioxideConcentrationMeasurement/init.lua index b60e71050a..eae9be65f0 100644 --- a/drivers/SmartThings/matter-thermostat/src/NitrogenDioxideConcentrationMeasurement/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/NitrogenDioxideConcentrationMeasurement/init.lua @@ -1,6 +1,9 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local NitrogenDioxideConcentrationMeasurementServerAttributes = require "NitrogenDioxideConcentrationMeasurement.server.attributes" -local ConcentrationMeasurement = require "ConcentrationMeasurement" +local NitrogenDioxideConcentrationMeasurementServerAttributes = require "embedded_clusters.NitrogenDioxideConcentrationMeasurement.server.attributes" +local ConcentrationMeasurement = require "embedded_clusters.ConcentrationMeasurement" local NitrogenDioxideConcentrationMeasurement = {} diff --git a/drivers/SmartThings/matter-thermostat/src/FormaldehydeConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/NitrogenDioxideConcentrationMeasurement/server/attributes/LevelValue.lua similarity index 81% rename from drivers/SmartThings/matter-thermostat/src/FormaldehydeConcentrationMeasurement/server/attributes/LevelValue.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/NitrogenDioxideConcentrationMeasurement/server/attributes/LevelValue.lua index 50127a3537..cc8f7b47eb 100644 --- a/drivers/SmartThings/matter-thermostat/src/FormaldehydeConcentrationMeasurement/server/attributes/LevelValue.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/NitrogenDioxideConcentrationMeasurement/server/attributes/LevelValue.lua @@ -1,9 +1,12 @@ -local ConcentrationMeasurementServerAttributesLevelValue = require "ConcentrationMeasurement.server.attributes.LevelValue" +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local ConcentrationMeasurementServerAttributesLevelValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.LevelValue" local LevelValue = { ID = 0x000A, NAME = "LevelValue", - base_type = require "ConcentrationMeasurement.types.LevelValueEnum", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.LevelValueEnum", } function LevelValue:new_value(...) diff --git a/drivers/SmartThings/matter-thermostat/src/FormaldehydeConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/NitrogenDioxideConcentrationMeasurement/server/attributes/MeasuredValue.lua similarity index 89% rename from drivers/SmartThings/matter-thermostat/src/FormaldehydeConcentrationMeasurement/server/attributes/MeasuredValue.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/NitrogenDioxideConcentrationMeasurement/server/attributes/MeasuredValue.lua index 763bbaa17b..ca0dafb0dc 100644 --- a/drivers/SmartThings/matter-thermostat/src/FormaldehydeConcentrationMeasurement/server/attributes/MeasuredValue.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/NitrogenDioxideConcentrationMeasurement/server/attributes/MeasuredValue.lua @@ -1,7 +1,10 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasuredValue = require "ConcentrationMeasurement.server.attributes.MeasuredValue" +local ConcentrationMeasurementServerAttributesMeasuredValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasuredValue" local MeasuredValue = { diff --git a/drivers/SmartThings/matter-thermostat/src/NitrogenDioxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/NitrogenDioxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua similarity index 82% rename from drivers/SmartThings/matter-thermostat/src/NitrogenDioxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/NitrogenDioxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua index d18f14b09f..9597906d3a 100644 --- a/drivers/SmartThings/matter-thermostat/src/NitrogenDioxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/NitrogenDioxideConcentrationMeasurement/server/attributes/MeasurementUnit.lua @@ -1,11 +1,14 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local TLVParser = require "st.matter.TLV.TLVParser" -local ConcentrationMeasurementServerAttributesMeasurementUnit = require "ConcentrationMeasurement.server.attributes.MeasurementUnit" +local ConcentrationMeasurementServerAttributesMeasurementUnit = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasurementUnit" local MeasurementUnit = { ID = 0x0008, NAME = "MeasurementUnit", - base_type = require "ConcentrationMeasurement.types.MeasurementUnitEnum", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.MeasurementUnitEnum", } function MeasurementUnit:new_value(...) diff --git a/drivers/SmartThings/matter-thermostat/src/NitrogenDioxideConcentrationMeasurement/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/NitrogenDioxideConcentrationMeasurement/server/attributes/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/NitrogenDioxideConcentrationMeasurement/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/NitrogenDioxideConcentrationMeasurement/server/attributes/init.lua index 06c3d6dd55..c82517d362 100644 --- a/drivers/SmartThings/matter-thermostat/src/NitrogenDioxideConcentrationMeasurement/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/NitrogenDioxideConcentrationMeasurement/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("NitrogenDioxideConcentrationMeasurement.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.NitrogenDioxideConcentrationMeasurement.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/OzoneConcentrationMeasurement/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/OzoneConcentrationMeasurement/init.lua similarity index 85% rename from drivers/SmartThings/matter-thermostat/src/OzoneConcentrationMeasurement/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/OzoneConcentrationMeasurement/init.lua index 83fd04857e..c49ea94b39 100644 --- a/drivers/SmartThings/matter-thermostat/src/OzoneConcentrationMeasurement/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/OzoneConcentrationMeasurement/init.lua @@ -1,6 +1,9 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local OzoneConcentrationMeasurementServerAttributes = require "OzoneConcentrationMeasurement.server.attributes" -local ConcentrationMeasurement = require "ConcentrationMeasurement" +local OzoneConcentrationMeasurementServerAttributes = require "embedded_clusters.OzoneConcentrationMeasurement.server.attributes" +local ConcentrationMeasurement = require "embedded_clusters.ConcentrationMeasurement" local OzoneConcentrationMeasurement = {} diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/OzoneConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/OzoneConcentrationMeasurement/server/attributes/LevelValue.lua new file mode 100644 index 0000000000..cc8f7b47eb --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/OzoneConcentrationMeasurement/server/attributes/LevelValue.lua @@ -0,0 +1,44 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local ConcentrationMeasurementServerAttributesLevelValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.LevelValue" + +local LevelValue = { + ID = 0x000A, + NAME = "LevelValue", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.LevelValueEnum", +} + +function LevelValue:new_value(...) + ConcentrationMeasurementServerAttributesLevelValue:new_value(...) +end + +function LevelValue:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesLevelValue:read(device, endpoint_id, self._cluster.ID) +end + +function LevelValue:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesLevelValue:subscribe(device, endpoint_id, self._cluster.ID) +end + +function LevelValue:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function LevelValue:build_test_report_data( + device, + endpoint_id, + value, + status +) + return ConcentrationMeasurementServerAttributesLevelValue:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) +end + +function LevelValue:deserialize(tlv_buf) + return ConcentrationMeasurementServerAttributesLevelValue:deserialize(tlv_buf) +end + +setmetatable(LevelValue, {__call = LevelValue.new_value, __index = LevelValue.base_type}) +return LevelValue + diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/OzoneConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/OzoneConcentrationMeasurement/server/attributes/MeasuredValue.lua new file mode 100644 index 0000000000..ca0dafb0dc --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/OzoneConcentrationMeasurement/server/attributes/MeasuredValue.lua @@ -0,0 +1,59 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local cluster_base = require "st.matter.cluster_base" +local data_types = require "st.matter.data_types" +local TLVParser = require "st.matter.TLV.TLVParser" +local ConcentrationMeasurementServerAttributesMeasuredValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasuredValue" + + +local MeasuredValue = { + ID = 0x0000, + NAME = "MeasuredValue", + base_type = require "st.matter.data_types.SinglePrecisionFloat", +} + +function MeasuredValue:new_value(...) + return ConcentrationMeasurementServerAttributesMeasuredValue:new_value(...) +end + +function MeasuredValue:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasuredValue:read(device, endpoint_id, self._cluster.ID) +end + +function MeasuredValue:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasuredValue:subscribe(device, endpoint_id, self._cluster.ID) +end + +function MeasuredValue:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function MeasuredValue:build_test_report_data( + device, + endpoint_id, + value, + status +) + local data = data_types.validate_or_build_type(value, self.base_type) + + return cluster_base.build_test_report_data( + device, + endpoint_id, + self._cluster.ID, + self.ID, + data, + status + ) +end + +function MeasuredValue:deserialize(tlv_buf) + local data = TLVParser.decode_tlv(tlv_buf) + + return data +end + +setmetatable(MeasuredValue, {__call = MeasuredValue.new_value, __index = MeasuredValue.base_type}) +return MeasuredValue + diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/OzoneConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/OzoneConcentrationMeasurement/server/attributes/MeasurementUnit.lua new file mode 100644 index 0000000000..9597906d3a --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/OzoneConcentrationMeasurement/server/attributes/MeasurementUnit.lua @@ -0,0 +1,48 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local TLVParser = require "st.matter.TLV.TLVParser" +local ConcentrationMeasurementServerAttributesMeasurementUnit = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasurementUnit" + + +local MeasurementUnit = { + ID = 0x0008, + NAME = "MeasurementUnit", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.MeasurementUnitEnum", +} + +function MeasurementUnit:new_value(...) + return ConcentrationMeasurementServerAttributesMeasurementUnit:new_value(...) +end + +function MeasurementUnit:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasurementUnit:read(device, endpoint_id, self._cluster.ID) +end + +function MeasurementUnit:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasurementUnit:subscribe(device, endpoint_id, self._cluster.ID) +end + +function MeasurementUnit:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function MeasurementUnit:build_test_report_data( + device, + endpoint_id, + value, + status +) + return ConcentrationMeasurementServerAttributesMeasurementUnit:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) +end + +function MeasurementUnit:deserialize(tlv_buf) + local data = TLVParser.decode_tlv(tlv_buf) + self:augment_type(data) + return data +end + +setmetatable(MeasurementUnit, {__call = MeasurementUnit.new_value, __index = MeasurementUnit.base_type}) +return MeasurementUnit + diff --git a/drivers/SmartThings/matter-thermostat/src/OzoneConcentrationMeasurement/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/OzoneConcentrationMeasurement/server/attributes/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/OzoneConcentrationMeasurement/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/OzoneConcentrationMeasurement/server/attributes/init.lua index fe0048cd99..918b680495 100644 --- a/drivers/SmartThings/matter-thermostat/src/OzoneConcentrationMeasurement/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/OzoneConcentrationMeasurement/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("OzoneConcentrationMeasurement.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.OzoneConcentrationMeasurement.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/Pm10ConcentrationMeasurement/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm10ConcentrationMeasurement/init.lua similarity index 85% rename from drivers/SmartThings/matter-thermostat/src/Pm10ConcentrationMeasurement/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm10ConcentrationMeasurement/init.lua index 98eebd407e..3b333b5417 100644 --- a/drivers/SmartThings/matter-thermostat/src/Pm10ConcentrationMeasurement/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm10ConcentrationMeasurement/init.lua @@ -1,6 +1,9 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local Pm10ConcentrationMeasurementServerAttributes = require "Pm10ConcentrationMeasurement.server.attributes" -local ConcentrationMeasurement = require "ConcentrationMeasurement" +local Pm10ConcentrationMeasurementServerAttributes = require "embedded_clusters.Pm10ConcentrationMeasurement.server.attributes" +local ConcentrationMeasurement = require "embedded_clusters.ConcentrationMeasurement" local Pm10ConcentrationMeasurement = {} diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm10ConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm10ConcentrationMeasurement/server/attributes/LevelValue.lua new file mode 100644 index 0000000000..cc8f7b47eb --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm10ConcentrationMeasurement/server/attributes/LevelValue.lua @@ -0,0 +1,44 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local ConcentrationMeasurementServerAttributesLevelValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.LevelValue" + +local LevelValue = { + ID = 0x000A, + NAME = "LevelValue", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.LevelValueEnum", +} + +function LevelValue:new_value(...) + ConcentrationMeasurementServerAttributesLevelValue:new_value(...) +end + +function LevelValue:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesLevelValue:read(device, endpoint_id, self._cluster.ID) +end + +function LevelValue:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesLevelValue:subscribe(device, endpoint_id, self._cluster.ID) +end + +function LevelValue:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function LevelValue:build_test_report_data( + device, + endpoint_id, + value, + status +) + return ConcentrationMeasurementServerAttributesLevelValue:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) +end + +function LevelValue:deserialize(tlv_buf) + return ConcentrationMeasurementServerAttributesLevelValue:deserialize(tlv_buf) +end + +setmetatable(LevelValue, {__call = LevelValue.new_value, __index = LevelValue.base_type}) +return LevelValue + diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm10ConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm10ConcentrationMeasurement/server/attributes/MeasuredValue.lua new file mode 100644 index 0000000000..ca0dafb0dc --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm10ConcentrationMeasurement/server/attributes/MeasuredValue.lua @@ -0,0 +1,59 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local cluster_base = require "st.matter.cluster_base" +local data_types = require "st.matter.data_types" +local TLVParser = require "st.matter.TLV.TLVParser" +local ConcentrationMeasurementServerAttributesMeasuredValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasuredValue" + + +local MeasuredValue = { + ID = 0x0000, + NAME = "MeasuredValue", + base_type = require "st.matter.data_types.SinglePrecisionFloat", +} + +function MeasuredValue:new_value(...) + return ConcentrationMeasurementServerAttributesMeasuredValue:new_value(...) +end + +function MeasuredValue:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasuredValue:read(device, endpoint_id, self._cluster.ID) +end + +function MeasuredValue:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasuredValue:subscribe(device, endpoint_id, self._cluster.ID) +end + +function MeasuredValue:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function MeasuredValue:build_test_report_data( + device, + endpoint_id, + value, + status +) + local data = data_types.validate_or_build_type(value, self.base_type) + + return cluster_base.build_test_report_data( + device, + endpoint_id, + self._cluster.ID, + self.ID, + data, + status + ) +end + +function MeasuredValue:deserialize(tlv_buf) + local data = TLVParser.decode_tlv(tlv_buf) + + return data +end + +setmetatable(MeasuredValue, {__call = MeasuredValue.new_value, __index = MeasuredValue.base_type}) +return MeasuredValue + diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm10ConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm10ConcentrationMeasurement/server/attributes/MeasurementUnit.lua new file mode 100644 index 0000000000..9597906d3a --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm10ConcentrationMeasurement/server/attributes/MeasurementUnit.lua @@ -0,0 +1,48 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local TLVParser = require "st.matter.TLV.TLVParser" +local ConcentrationMeasurementServerAttributesMeasurementUnit = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasurementUnit" + + +local MeasurementUnit = { + ID = 0x0008, + NAME = "MeasurementUnit", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.MeasurementUnitEnum", +} + +function MeasurementUnit:new_value(...) + return ConcentrationMeasurementServerAttributesMeasurementUnit:new_value(...) +end + +function MeasurementUnit:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasurementUnit:read(device, endpoint_id, self._cluster.ID) +end + +function MeasurementUnit:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasurementUnit:subscribe(device, endpoint_id, self._cluster.ID) +end + +function MeasurementUnit:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function MeasurementUnit:build_test_report_data( + device, + endpoint_id, + value, + status +) + return ConcentrationMeasurementServerAttributesMeasurementUnit:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) +end + +function MeasurementUnit:deserialize(tlv_buf) + local data = TLVParser.decode_tlv(tlv_buf) + self:augment_type(data) + return data +end + +setmetatable(MeasurementUnit, {__call = MeasurementUnit.new_value, __index = MeasurementUnit.base_type}) +return MeasurementUnit + diff --git a/drivers/SmartThings/matter-thermostat/src/Pm10ConcentrationMeasurement/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm10ConcentrationMeasurement/server/attributes/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/Pm10ConcentrationMeasurement/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm10ConcentrationMeasurement/server/attributes/init.lua index 55f08c7e43..3b1e6617a4 100644 --- a/drivers/SmartThings/matter-thermostat/src/Pm10ConcentrationMeasurement/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm10ConcentrationMeasurement/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("Pm10ConcentrationMeasurement.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.Pm10ConcentrationMeasurement.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/Pm1ConcentrationMeasurement/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm1ConcentrationMeasurement/init.lua similarity index 85% rename from drivers/SmartThings/matter-thermostat/src/Pm1ConcentrationMeasurement/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm1ConcentrationMeasurement/init.lua index 0b3caa3bd2..b2e6656a9a 100644 --- a/drivers/SmartThings/matter-thermostat/src/Pm1ConcentrationMeasurement/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm1ConcentrationMeasurement/init.lua @@ -1,6 +1,9 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local Pm1ConcentrationMeasurementServerAttributes = require "Pm1ConcentrationMeasurement.server.attributes" -local ConcentrationMeasurement = require "ConcentrationMeasurement" +local Pm1ConcentrationMeasurementServerAttributes = require "embedded_clusters.Pm1ConcentrationMeasurement.server.attributes" +local ConcentrationMeasurement = require "embedded_clusters.ConcentrationMeasurement" local Pm1ConcentrationMeasurement = {} diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm1ConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm1ConcentrationMeasurement/server/attributes/LevelValue.lua new file mode 100644 index 0000000000..cc8f7b47eb --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm1ConcentrationMeasurement/server/attributes/LevelValue.lua @@ -0,0 +1,44 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local ConcentrationMeasurementServerAttributesLevelValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.LevelValue" + +local LevelValue = { + ID = 0x000A, + NAME = "LevelValue", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.LevelValueEnum", +} + +function LevelValue:new_value(...) + ConcentrationMeasurementServerAttributesLevelValue:new_value(...) +end + +function LevelValue:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesLevelValue:read(device, endpoint_id, self._cluster.ID) +end + +function LevelValue:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesLevelValue:subscribe(device, endpoint_id, self._cluster.ID) +end + +function LevelValue:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function LevelValue:build_test_report_data( + device, + endpoint_id, + value, + status +) + return ConcentrationMeasurementServerAttributesLevelValue:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) +end + +function LevelValue:deserialize(tlv_buf) + return ConcentrationMeasurementServerAttributesLevelValue:deserialize(tlv_buf) +end + +setmetatable(LevelValue, {__call = LevelValue.new_value, __index = LevelValue.base_type}) +return LevelValue + diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm1ConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm1ConcentrationMeasurement/server/attributes/MeasuredValue.lua new file mode 100644 index 0000000000..ca0dafb0dc --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm1ConcentrationMeasurement/server/attributes/MeasuredValue.lua @@ -0,0 +1,59 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local cluster_base = require "st.matter.cluster_base" +local data_types = require "st.matter.data_types" +local TLVParser = require "st.matter.TLV.TLVParser" +local ConcentrationMeasurementServerAttributesMeasuredValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasuredValue" + + +local MeasuredValue = { + ID = 0x0000, + NAME = "MeasuredValue", + base_type = require "st.matter.data_types.SinglePrecisionFloat", +} + +function MeasuredValue:new_value(...) + return ConcentrationMeasurementServerAttributesMeasuredValue:new_value(...) +end + +function MeasuredValue:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasuredValue:read(device, endpoint_id, self._cluster.ID) +end + +function MeasuredValue:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasuredValue:subscribe(device, endpoint_id, self._cluster.ID) +end + +function MeasuredValue:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function MeasuredValue:build_test_report_data( + device, + endpoint_id, + value, + status +) + local data = data_types.validate_or_build_type(value, self.base_type) + + return cluster_base.build_test_report_data( + device, + endpoint_id, + self._cluster.ID, + self.ID, + data, + status + ) +end + +function MeasuredValue:deserialize(tlv_buf) + local data = TLVParser.decode_tlv(tlv_buf) + + return data +end + +setmetatable(MeasuredValue, {__call = MeasuredValue.new_value, __index = MeasuredValue.base_type}) +return MeasuredValue + diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm1ConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm1ConcentrationMeasurement/server/attributes/MeasurementUnit.lua new file mode 100644 index 0000000000..9597906d3a --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm1ConcentrationMeasurement/server/attributes/MeasurementUnit.lua @@ -0,0 +1,48 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local TLVParser = require "st.matter.TLV.TLVParser" +local ConcentrationMeasurementServerAttributesMeasurementUnit = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasurementUnit" + + +local MeasurementUnit = { + ID = 0x0008, + NAME = "MeasurementUnit", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.MeasurementUnitEnum", +} + +function MeasurementUnit:new_value(...) + return ConcentrationMeasurementServerAttributesMeasurementUnit:new_value(...) +end + +function MeasurementUnit:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasurementUnit:read(device, endpoint_id, self._cluster.ID) +end + +function MeasurementUnit:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasurementUnit:subscribe(device, endpoint_id, self._cluster.ID) +end + +function MeasurementUnit:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function MeasurementUnit:build_test_report_data( + device, + endpoint_id, + value, + status +) + return ConcentrationMeasurementServerAttributesMeasurementUnit:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) +end + +function MeasurementUnit:deserialize(tlv_buf) + local data = TLVParser.decode_tlv(tlv_buf) + self:augment_type(data) + return data +end + +setmetatable(MeasurementUnit, {__call = MeasurementUnit.new_value, __index = MeasurementUnit.base_type}) +return MeasurementUnit + diff --git a/drivers/SmartThings/matter-thermostat/src/Pm1ConcentrationMeasurement/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm1ConcentrationMeasurement/server/attributes/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/Pm1ConcentrationMeasurement/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm1ConcentrationMeasurement/server/attributes/init.lua index f668e41a07..2635da32a6 100644 --- a/drivers/SmartThings/matter-thermostat/src/Pm1ConcentrationMeasurement/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm1ConcentrationMeasurement/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("Pm1ConcentrationMeasurement.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.Pm1ConcentrationMeasurement.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/Pm25ConcentrationMeasurement/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm25ConcentrationMeasurement/init.lua similarity index 85% rename from drivers/SmartThings/matter-thermostat/src/Pm25ConcentrationMeasurement/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm25ConcentrationMeasurement/init.lua index 5234346d60..e6e6144f94 100644 --- a/drivers/SmartThings/matter-thermostat/src/Pm25ConcentrationMeasurement/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm25ConcentrationMeasurement/init.lua @@ -1,6 +1,9 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local Pm25ConcentrationMeasurementServerAttributes = require "Pm25ConcentrationMeasurement.server.attributes" -local ConcentrationMeasurement = require "ConcentrationMeasurement" +local Pm25ConcentrationMeasurementServerAttributes = require "embedded_clusters.Pm25ConcentrationMeasurement.server.attributes" +local ConcentrationMeasurement = require "embedded_clusters.ConcentrationMeasurement" local Pm25ConcentrationMeasurement = {} diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm25ConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm25ConcentrationMeasurement/server/attributes/LevelValue.lua new file mode 100644 index 0000000000..cc8f7b47eb --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm25ConcentrationMeasurement/server/attributes/LevelValue.lua @@ -0,0 +1,44 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local ConcentrationMeasurementServerAttributesLevelValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.LevelValue" + +local LevelValue = { + ID = 0x000A, + NAME = "LevelValue", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.LevelValueEnum", +} + +function LevelValue:new_value(...) + ConcentrationMeasurementServerAttributesLevelValue:new_value(...) +end + +function LevelValue:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesLevelValue:read(device, endpoint_id, self._cluster.ID) +end + +function LevelValue:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesLevelValue:subscribe(device, endpoint_id, self._cluster.ID) +end + +function LevelValue:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function LevelValue:build_test_report_data( + device, + endpoint_id, + value, + status +) + return ConcentrationMeasurementServerAttributesLevelValue:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) +end + +function LevelValue:deserialize(tlv_buf) + return ConcentrationMeasurementServerAttributesLevelValue:deserialize(tlv_buf) +end + +setmetatable(LevelValue, {__call = LevelValue.new_value, __index = LevelValue.base_type}) +return LevelValue + diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm25ConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm25ConcentrationMeasurement/server/attributes/MeasuredValue.lua new file mode 100644 index 0000000000..ca0dafb0dc --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm25ConcentrationMeasurement/server/attributes/MeasuredValue.lua @@ -0,0 +1,59 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local cluster_base = require "st.matter.cluster_base" +local data_types = require "st.matter.data_types" +local TLVParser = require "st.matter.TLV.TLVParser" +local ConcentrationMeasurementServerAttributesMeasuredValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasuredValue" + + +local MeasuredValue = { + ID = 0x0000, + NAME = "MeasuredValue", + base_type = require "st.matter.data_types.SinglePrecisionFloat", +} + +function MeasuredValue:new_value(...) + return ConcentrationMeasurementServerAttributesMeasuredValue:new_value(...) +end + +function MeasuredValue:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasuredValue:read(device, endpoint_id, self._cluster.ID) +end + +function MeasuredValue:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasuredValue:subscribe(device, endpoint_id, self._cluster.ID) +end + +function MeasuredValue:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function MeasuredValue:build_test_report_data( + device, + endpoint_id, + value, + status +) + local data = data_types.validate_or_build_type(value, self.base_type) + + return cluster_base.build_test_report_data( + device, + endpoint_id, + self._cluster.ID, + self.ID, + data, + status + ) +end + +function MeasuredValue:deserialize(tlv_buf) + local data = TLVParser.decode_tlv(tlv_buf) + + return data +end + +setmetatable(MeasuredValue, {__call = MeasuredValue.new_value, __index = MeasuredValue.base_type}) +return MeasuredValue + diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm25ConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm25ConcentrationMeasurement/server/attributes/MeasurementUnit.lua new file mode 100644 index 0000000000..9597906d3a --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm25ConcentrationMeasurement/server/attributes/MeasurementUnit.lua @@ -0,0 +1,48 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local TLVParser = require "st.matter.TLV.TLVParser" +local ConcentrationMeasurementServerAttributesMeasurementUnit = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasurementUnit" + + +local MeasurementUnit = { + ID = 0x0008, + NAME = "MeasurementUnit", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.MeasurementUnitEnum", +} + +function MeasurementUnit:new_value(...) + return ConcentrationMeasurementServerAttributesMeasurementUnit:new_value(...) +end + +function MeasurementUnit:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasurementUnit:read(device, endpoint_id, self._cluster.ID) +end + +function MeasurementUnit:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasurementUnit:subscribe(device, endpoint_id, self._cluster.ID) +end + +function MeasurementUnit:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function MeasurementUnit:build_test_report_data( + device, + endpoint_id, + value, + status +) + return ConcentrationMeasurementServerAttributesMeasurementUnit:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) +end + +function MeasurementUnit:deserialize(tlv_buf) + local data = TLVParser.decode_tlv(tlv_buf) + self:augment_type(data) + return data +end + +setmetatable(MeasurementUnit, {__call = MeasurementUnit.new_value, __index = MeasurementUnit.base_type}) +return MeasurementUnit + diff --git a/drivers/SmartThings/matter-thermostat/src/Pm25ConcentrationMeasurement/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm25ConcentrationMeasurement/server/attributes/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/Pm25ConcentrationMeasurement/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm25ConcentrationMeasurement/server/attributes/init.lua index 2c7d5fce7b..5c432da0ec 100644 --- a/drivers/SmartThings/matter-thermostat/src/Pm25ConcentrationMeasurement/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/Pm25ConcentrationMeasurement/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("Pm25ConcentrationMeasurement.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.Pm25ConcentrationMeasurement.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/RadonConcentrationMeasurement/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/RadonConcentrationMeasurement/init.lua similarity index 85% rename from drivers/SmartThings/matter-thermostat/src/RadonConcentrationMeasurement/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/RadonConcentrationMeasurement/init.lua index 2a4cc04a0d..3d4b21d602 100644 --- a/drivers/SmartThings/matter-thermostat/src/RadonConcentrationMeasurement/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/RadonConcentrationMeasurement/init.lua @@ -1,6 +1,9 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local RadonConcentrationMeasurementServerAttributes = require "RadonConcentrationMeasurement.server.attributes" -local ConcentrationMeasurement = require "ConcentrationMeasurement" +local RadonConcentrationMeasurementServerAttributes = require "embedded_clusters.RadonConcentrationMeasurement.server.attributes" +local ConcentrationMeasurement = require "embedded_clusters.ConcentrationMeasurement" local RadonConcentrationMeasurement = {} diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/RadonConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/RadonConcentrationMeasurement/server/attributes/LevelValue.lua new file mode 100644 index 0000000000..cc8f7b47eb --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/RadonConcentrationMeasurement/server/attributes/LevelValue.lua @@ -0,0 +1,44 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local ConcentrationMeasurementServerAttributesLevelValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.LevelValue" + +local LevelValue = { + ID = 0x000A, + NAME = "LevelValue", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.LevelValueEnum", +} + +function LevelValue:new_value(...) + ConcentrationMeasurementServerAttributesLevelValue:new_value(...) +end + +function LevelValue:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesLevelValue:read(device, endpoint_id, self._cluster.ID) +end + +function LevelValue:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesLevelValue:subscribe(device, endpoint_id, self._cluster.ID) +end + +function LevelValue:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function LevelValue:build_test_report_data( + device, + endpoint_id, + value, + status +) + return ConcentrationMeasurementServerAttributesLevelValue:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) +end + +function LevelValue:deserialize(tlv_buf) + return ConcentrationMeasurementServerAttributesLevelValue:deserialize(tlv_buf) +end + +setmetatable(LevelValue, {__call = LevelValue.new_value, __index = LevelValue.base_type}) +return LevelValue + diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/RadonConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/RadonConcentrationMeasurement/server/attributes/MeasuredValue.lua new file mode 100644 index 0000000000..ca0dafb0dc --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/RadonConcentrationMeasurement/server/attributes/MeasuredValue.lua @@ -0,0 +1,59 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local cluster_base = require "st.matter.cluster_base" +local data_types = require "st.matter.data_types" +local TLVParser = require "st.matter.TLV.TLVParser" +local ConcentrationMeasurementServerAttributesMeasuredValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasuredValue" + + +local MeasuredValue = { + ID = 0x0000, + NAME = "MeasuredValue", + base_type = require "st.matter.data_types.SinglePrecisionFloat", +} + +function MeasuredValue:new_value(...) + return ConcentrationMeasurementServerAttributesMeasuredValue:new_value(...) +end + +function MeasuredValue:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasuredValue:read(device, endpoint_id, self._cluster.ID) +end + +function MeasuredValue:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasuredValue:subscribe(device, endpoint_id, self._cluster.ID) +end + +function MeasuredValue:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function MeasuredValue:build_test_report_data( + device, + endpoint_id, + value, + status +) + local data = data_types.validate_or_build_type(value, self.base_type) + + return cluster_base.build_test_report_data( + device, + endpoint_id, + self._cluster.ID, + self.ID, + data, + status + ) +end + +function MeasuredValue:deserialize(tlv_buf) + local data = TLVParser.decode_tlv(tlv_buf) + + return data +end + +setmetatable(MeasuredValue, {__call = MeasuredValue.new_value, __index = MeasuredValue.base_type}) +return MeasuredValue + diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/RadonConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/RadonConcentrationMeasurement/server/attributes/MeasurementUnit.lua new file mode 100644 index 0000000000..9597906d3a --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/RadonConcentrationMeasurement/server/attributes/MeasurementUnit.lua @@ -0,0 +1,48 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local TLVParser = require "st.matter.TLV.TLVParser" +local ConcentrationMeasurementServerAttributesMeasurementUnit = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasurementUnit" + + +local MeasurementUnit = { + ID = 0x0008, + NAME = "MeasurementUnit", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.MeasurementUnitEnum", +} + +function MeasurementUnit:new_value(...) + return ConcentrationMeasurementServerAttributesMeasurementUnit:new_value(...) +end + +function MeasurementUnit:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasurementUnit:read(device, endpoint_id, self._cluster.ID) +end + +function MeasurementUnit:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasurementUnit:subscribe(device, endpoint_id, self._cluster.ID) +end + +function MeasurementUnit:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function MeasurementUnit:build_test_report_data( + device, + endpoint_id, + value, + status +) + return ConcentrationMeasurementServerAttributesMeasurementUnit:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) +end + +function MeasurementUnit:deserialize(tlv_buf) + local data = TLVParser.decode_tlv(tlv_buf) + self:augment_type(data) + return data +end + +setmetatable(MeasurementUnit, {__call = MeasurementUnit.new_value, __index = MeasurementUnit.base_type}) +return MeasurementUnit + diff --git a/drivers/SmartThings/matter-thermostat/src/RadonConcentrationMeasurement/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/RadonConcentrationMeasurement/server/attributes/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/RadonConcentrationMeasurement/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/RadonConcentrationMeasurement/server/attributes/init.lua index b83ef67bfc..8aa225e510 100644 --- a/drivers/SmartThings/matter-thermostat/src/RadonConcentrationMeasurement/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/RadonConcentrationMeasurement/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("RadonConcentrationMeasurement.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.RadonConcentrationMeasurement.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/TotalVolatileOrganicCompoundsConcentrationMeasurement/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/TotalVolatileOrganicCompoundsConcentrationMeasurement/init.lua similarity index 89% rename from drivers/SmartThings/matter-thermostat/src/TotalVolatileOrganicCompoundsConcentrationMeasurement/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/TotalVolatileOrganicCompoundsConcentrationMeasurement/init.lua index a99c1dea50..cad49a14e1 100644 --- a/drivers/SmartThings/matter-thermostat/src/TotalVolatileOrganicCompoundsConcentrationMeasurement/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/TotalVolatileOrganicCompoundsConcentrationMeasurement/init.lua @@ -1,6 +1,9 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local TotalVolatileOrganicCompoundsConcentrationMeasurementServerAttributes = require "TotalVolatileOrganicCompoundsConcentrationMeasurement.server.attributes" -local ConcentrationMeasurement = require "ConcentrationMeasurement" +local TotalVolatileOrganicCompoundsConcentrationMeasurementServerAttributes = require "embedded_clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement.server.attributes" +local ConcentrationMeasurement = require "embedded_clusters.ConcentrationMeasurement" local TotalVolatileOrganicCompoundsConcentrationMeasurement = {} diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/LevelValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/LevelValue.lua new file mode 100644 index 0000000000..cc8f7b47eb --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/LevelValue.lua @@ -0,0 +1,44 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local ConcentrationMeasurementServerAttributesLevelValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.LevelValue" + +local LevelValue = { + ID = 0x000A, + NAME = "LevelValue", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.LevelValueEnum", +} + +function LevelValue:new_value(...) + ConcentrationMeasurementServerAttributesLevelValue:new_value(...) +end + +function LevelValue:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesLevelValue:read(device, endpoint_id, self._cluster.ID) +end + +function LevelValue:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesLevelValue:subscribe(device, endpoint_id, self._cluster.ID) +end + +function LevelValue:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function LevelValue:build_test_report_data( + device, + endpoint_id, + value, + status +) + return ConcentrationMeasurementServerAttributesLevelValue:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) +end + +function LevelValue:deserialize(tlv_buf) + return ConcentrationMeasurementServerAttributesLevelValue:deserialize(tlv_buf) +end + +setmetatable(LevelValue, {__call = LevelValue.new_value, __index = LevelValue.base_type}) +return LevelValue + diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/MeasuredValue.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/MeasuredValue.lua new file mode 100644 index 0000000000..ca0dafb0dc --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/MeasuredValue.lua @@ -0,0 +1,59 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local cluster_base = require "st.matter.cluster_base" +local data_types = require "st.matter.data_types" +local TLVParser = require "st.matter.TLV.TLVParser" +local ConcentrationMeasurementServerAttributesMeasuredValue = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasuredValue" + + +local MeasuredValue = { + ID = 0x0000, + NAME = "MeasuredValue", + base_type = require "st.matter.data_types.SinglePrecisionFloat", +} + +function MeasuredValue:new_value(...) + return ConcentrationMeasurementServerAttributesMeasuredValue:new_value(...) +end + +function MeasuredValue:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasuredValue:read(device, endpoint_id, self._cluster.ID) +end + +function MeasuredValue:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasuredValue:subscribe(device, endpoint_id, self._cluster.ID) +end + +function MeasuredValue:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function MeasuredValue:build_test_report_data( + device, + endpoint_id, + value, + status +) + local data = data_types.validate_or_build_type(value, self.base_type) + + return cluster_base.build_test_report_data( + device, + endpoint_id, + self._cluster.ID, + self.ID, + data, + status + ) +end + +function MeasuredValue:deserialize(tlv_buf) + local data = TLVParser.decode_tlv(tlv_buf) + + return data +end + +setmetatable(MeasuredValue, {__call = MeasuredValue.new_value, __index = MeasuredValue.base_type}) +return MeasuredValue + diff --git a/drivers/SmartThings/matter-thermostat/src/embedded_clusters/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/MeasurementUnit.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/MeasurementUnit.lua new file mode 100644 index 0000000000..9597906d3a --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/MeasurementUnit.lua @@ -0,0 +1,48 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local TLVParser = require "st.matter.TLV.TLVParser" +local ConcentrationMeasurementServerAttributesMeasurementUnit = require "embedded_clusters.ConcentrationMeasurement.server.attributes.MeasurementUnit" + + +local MeasurementUnit = { + ID = 0x0008, + NAME = "MeasurementUnit", + base_type = require "embedded_clusters.ConcentrationMeasurement.types.MeasurementUnitEnum", +} + +function MeasurementUnit:new_value(...) + return ConcentrationMeasurementServerAttributesMeasurementUnit:new_value(...) +end + +function MeasurementUnit:read(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasurementUnit:read(device, endpoint_id, self._cluster.ID) +end + +function MeasurementUnit:subscribe(device, endpoint_id) + return ConcentrationMeasurementServerAttributesMeasurementUnit:subscribe(device, endpoint_id, self._cluster.ID) +end + +function MeasurementUnit:set_parent_cluster(cluster) + self._cluster = cluster + return self +end + +function MeasurementUnit:build_test_report_data( + device, + endpoint_id, + value, + status +) + return ConcentrationMeasurementServerAttributesMeasurementUnit:build_test_report_data(device, endpoint_id, value, status, self._cluster.ID) +end + +function MeasurementUnit:deserialize(tlv_buf) + local data = TLVParser.decode_tlv(tlv_buf) + self:augment_type(data) + return data +end + +setmetatable(MeasurementUnit, {__call = MeasurementUnit.new_value, __index = MeasurementUnit.base_type}) +return MeasurementUnit + diff --git a/drivers/SmartThings/matter-thermostat/src/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/init.lua index c0c1b65c37..b67cbcd7b6 100644 --- a/drivers/SmartThings/matter-thermostat/src/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/TotalVolatileOrganicCompoundsConcentrationMeasurement/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("TotalVolatileOrganicCompoundsConcentrationMeasurement.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/init.lua similarity index 87% rename from drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/init.lua index 1155cfd636..3a1c2f1bfb 100644 --- a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/init.lua @@ -1,7 +1,10 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" -local WaterHeaterModeServerAttributes = require "WaterHeaterMode.server.attributes" -local WaterHeaterModeServerCommands = require "WaterHeaterMode.server.commands" -local WaterHeaterModeTypes = require "WaterHeaterMode.types" +local WaterHeaterModeServerAttributes = require "embedded_clusters.WaterHeaterMode.server.attributes" +local WaterHeaterModeServerCommands = require "embedded_clusters.WaterHeaterMode.server.commands" +local WaterHeaterModeTypes = require "embedded_clusters.WaterHeaterMode.types" local WaterHeaterMode = {} diff --git a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/attributes/CurrentMode.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/server/attributes/CurrentMode.lua similarity index 93% rename from drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/attributes/CurrentMode.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/server/attributes/CurrentMode.lua index aa20156f74..165b906df5 100644 --- a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/attributes/CurrentMode.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/server/attributes/CurrentMode.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" @@ -24,7 +27,6 @@ function CurrentMode:read(device, endpoint_id) ) end - function CurrentMode:subscribe(device, endpoint_id) return cluster_base.subscribe( device, diff --git a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/attributes/SupportedModes.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/server/attributes/SupportedModes.lua similarity index 90% rename from drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/attributes/SupportedModes.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/server/attributes/SupportedModes.lua index 1f393a17d4..903a374d43 100644 --- a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/attributes/SupportedModes.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/server/attributes/SupportedModes.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local cluster_base = require "st.matter.cluster_base" local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" @@ -6,7 +9,7 @@ local SupportedModes = { ID = 0x0000, NAME = "SupportedModes", base_type = require "st.matter.data_types.Array", - element_type = require "WaterHeaterMode.types.ModeOptionStruct", + element_type = require "embedded_clusters.WaterHeaterMode.types.ModeOptionStruct", } function SupportedModes:augment_type(data_type_obj) diff --git a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/attributes/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/server/attributes/init.lua similarity index 75% rename from drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/attributes/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/server/attributes/init.lua index 020a4125ce..fb7bed9828 100644 --- a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/attributes/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/server/attributes/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local attr_mt = {} attr_mt.__attr_cache = {} attr_mt.__index = function(self, key) if attr_mt.__attr_cache[key] == nil then - local req_loc = string.format("WaterHeaterMode.server.attributes.%s", key) + local req_loc = string.format("embedded_clusters.WaterHeaterMode.server.attributes.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/commands/ChangeToMode.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/server/commands/ChangeToMode.lua similarity index 96% rename from drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/commands/ChangeToMode.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/server/commands/ChangeToMode.lua index 73ddbd1029..726352b448 100644 --- a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/commands/ChangeToMode.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/server/commands/ChangeToMode.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local TLVParser = require "st.matter.TLV.TLVParser" diff --git a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/commands/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/server/commands/init.lua similarity index 76% rename from drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/commands/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/server/commands/init.lua index 9736c4577f..64660ed336 100644 --- a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/server/commands/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/server/commands/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local command_mt = {} command_mt.__command_cache = {} command_mt.__index = function(self, key) if command_mt.__command_cache[key] == nil then - local req_loc = string.format("WaterHeaterMode.server.commands.%s", key) + local req_loc = string.format("embedded_clusters.WaterHeaterMode.server.commands.%s", key) local raw_def = require(req_loc) local cluster = rawget(self, "_cluster") command_mt.__command_cache[key] = raw_def:set_parent_cluster(cluster) diff --git a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/types/Feature.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/types/Feature.lua similarity index 92% rename from drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/types/Feature.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/types/Feature.lua index da49bf4115..c7e987399d 100644 --- a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/types/Feature.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/types/Feature.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local UintABC = require "st.matter.data_types.base_defs.UintABC" diff --git a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/types/ModeOptionStruct.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/types/ModeOptionStruct.lua similarity index 94% rename from drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/types/ModeOptionStruct.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/types/ModeOptionStruct.lua index f770a7916c..1db8c5b634 100644 --- a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/types/ModeOptionStruct.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/types/ModeOptionStruct.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local StructureABC = require "st.matter.data_types.base_defs.StructureABC" @@ -24,7 +27,7 @@ ModeOptionStruct.field_defs = { is_nullable = false, is_optional = false, data_type = require "st.matter.data_types.Array", - element_type = require "WaterHeaterMode.types.ModeTagStruct", + element_type = require "embedded_clusters.WaterHeaterMode.types.ModeTagStruct", }, } diff --git a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/types/ModeTag.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/types/ModeTag.lua similarity index 90% rename from drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/types/ModeTag.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/types/ModeTag.lua index 009e70a40e..7d37d4374d 100644 --- a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/types/ModeTag.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/types/ModeTag.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local UintABC = require "st.matter.data_types.base_defs.UintABC" diff --git a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/types/ModeTagStruct.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/types/ModeTagStruct.lua similarity index 96% rename from drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/types/ModeTagStruct.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/types/ModeTagStruct.lua index 1c41eb320e..17dfd269d6 100644 --- a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/types/ModeTagStruct.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/types/ModeTagStruct.lua @@ -1,3 +1,6 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local data_types = require "st.matter.data_types" local StructureABC = require "st.matter.data_types.base_defs.StructureABC" diff --git a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/types/init.lua b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/types/init.lua similarity index 61% rename from drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/types/init.lua rename to drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/types/init.lua index f0198ff8a0..bae583dfb4 100644 --- a/drivers/SmartThings/matter-thermostat/src/WaterHeaterMode/types/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/embedded_clusters/WaterHeaterMode/types/init.lua @@ -1,8 +1,11 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local types_mt = {} types_mt.__types_cache = {} types_mt.__index = function(self, key) if types_mt.__types_cache[key] == nil then - types_mt.__types_cache[key] = require("WaterHeaterMode.types." .. key) + types_mt.__types_cache[key] = require("embedded_clusters.WaterHeaterMode.types." .. key) end return types_mt.__types_cache[key] end diff --git a/drivers/SmartThings/matter-thermostat/src/init.lua b/drivers/SmartThings/matter-thermostat/src/init.lua index 58f0d75344..6697c80fb6 100644 --- a/drivers/SmartThings/matter-thermostat/src/init.lua +++ b/drivers/SmartThings/matter-thermostat/src/init.lua @@ -1,2263 +1,516 @@ --- 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 -local capabilities = require "st.capabilities" -local log = require "log" -local clusters = require "st.matter.clusters" -local embedded_cluster_utils = require "embedded-cluster-utils" -local im = require "st.matter.interaction_model" - -local MatterDriver = require "st.matter.driver" -local utils = require "st.utils" - -local SUPPORTED_COMPONENT_CAPABILITIES = "__supported_component_capabilities" --- declare match_profile function for use throughout file -local match_profile - --- Include driver-side definitions when lua libs api version is < 10 -local version = require "version" -if version.api < 10 then - clusters.HepaFilterMonitoring = require "HepaFilterMonitoring" - clusters.ActivatedCarbonFilterMonitoring = require "ActivatedCarbonFilterMonitoring" - clusters.AirQuality = require "AirQuality" - clusters.CarbonMonoxideConcentrationMeasurement = require "CarbonMonoxideConcentrationMeasurement" - clusters.CarbonDioxideConcentrationMeasurement = require "CarbonDioxideConcentrationMeasurement" - clusters.FormaldehydeConcentrationMeasurement = require "FormaldehydeConcentrationMeasurement" - clusters.NitrogenDioxideConcentrationMeasurement = require "NitrogenDioxideConcentrationMeasurement" - clusters.OzoneConcentrationMeasurement = require "OzoneConcentrationMeasurement" - clusters.Pm1ConcentrationMeasurement = require "Pm1ConcentrationMeasurement" - clusters.Pm10ConcentrationMeasurement = require "Pm10ConcentrationMeasurement" - clusters.Pm25ConcentrationMeasurement = require "Pm25ConcentrationMeasurement" - clusters.RadonConcentrationMeasurement = require "RadonConcentrationMeasurement" - clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement = require "TotalVolatileOrganicCompoundsConcentrationMeasurement" - -- new modes add in Matter 1.2 - clusters.Thermostat.types.ThermostatSystemMode.DRY = 0x8 - clusters.Thermostat.types.ThermostatSystemMode.SLEEP = 0x9 -end - -local SAVED_SYSTEM_MODE_IB = "__saved_system_mode_ib" -local DISALLOWED_THERMOSTAT_MODES = "__DISALLOWED_CONTROL_OPERATIONS" -local OPTIONAL_THERMOSTAT_MODES_SEEN = "__OPTIONAL_THERMOSTAT_MODES_SEEN" - -if version.api < 11 then - clusters.ElectricalEnergyMeasurement = require "ElectricalEnergyMeasurement" - clusters.ElectricalPowerMeasurement = require "ElectricalPowerMeasurement" -end - -if version.api < 13 then - clusters.WaterHeaterMode = require "WaterHeaterMode" -end - -local THERMOSTAT_MODE_MAP = { - [clusters.Thermostat.types.ThermostatSystemMode.OFF] = capabilities.thermostatMode.thermostatMode.off, - [clusters.Thermostat.types.ThermostatSystemMode.AUTO] = capabilities.thermostatMode.thermostatMode.auto, - [clusters.Thermostat.types.ThermostatSystemMode.COOL] = capabilities.thermostatMode.thermostatMode.cool, - [clusters.Thermostat.types.ThermostatSystemMode.HEAT] = capabilities.thermostatMode.thermostatMode.heat, - [clusters.Thermostat.types.ThermostatSystemMode.EMERGENCY_HEATING] = capabilities.thermostatMode.thermostatMode.emergency_heat, - [clusters.Thermostat.types.ThermostatSystemMode.PRECOOLING] = capabilities.thermostatMode.thermostatMode.precooling, - [clusters.Thermostat.types.ThermostatSystemMode.FAN_ONLY] = capabilities.thermostatMode.thermostatMode.fanonly, - [clusters.Thermostat.types.ThermostatSystemMode.DRY] = capabilities.thermostatMode.thermostatMode.dryair, - [clusters.Thermostat.types.ThermostatSystemMode.SLEEP] = capabilities.thermostatMode.thermostatMode.asleep, -} - -local THERMOSTAT_OPERATING_MODE_MAP = { - [0] = capabilities.thermostatOperatingState.thermostatOperatingState.heating, - [1] = capabilities.thermostatOperatingState.thermostatOperatingState.cooling, - [2] = capabilities.thermostatOperatingState.thermostatOperatingState.fan_only, - [3] = capabilities.thermostatOperatingState.thermostatOperatingState.heating, - [4] = capabilities.thermostatOperatingState.thermostatOperatingState.cooling, - [5] = capabilities.thermostatOperatingState.thermostatOperatingState.fan_only, - [6] = capabilities.thermostatOperatingState.thermostatOperatingState.fan_only, -} - -local WIND_MODE_MAP = { - [0] = capabilities.windMode.windMode.sleepWind, - [1] = capabilities.windMode.windMode.naturalWind -} - -local ROCK_MODE_MAP = { - [0] = capabilities.fanOscillationMode.fanOscillationMode.horizontal, - [1] = capabilities.fanOscillationMode.fanOscillationMode.vertical, - [2] = capabilities.fanOscillationMode.fanOscillationMode.swing -} - -local RAC_DEVICE_TYPE_ID = 0x0072 -local AP_DEVICE_TYPE_ID = 0x002D -local FAN_DEVICE_TYPE_ID = 0x002B -local WATER_HEATER_DEVICE_TYPE_ID = 0x050F -local HEAT_PUMP_DEVICE_TYPE_ID = 0x0309 -local THERMOSTAT_DEVICE_TYPE_ID = 0x0301 -local ELECTRICAL_SENSOR_DEVICE_TYPE_ID = 0x0510 - -local MIN_ALLOWED_PERCENT_VALUE = 0 -local MAX_ALLOWED_PERCENT_VALUE = 100 - -local CUMULATIVE_REPORTS_NOT_SUPPORTED = "__cumulative_reports_not_supported" -local LAST_IMPORTED_REPORT_TIMESTAMP = "__last_imported_report_timestamp" -local MINIMUM_ST_ENERGY_REPORT_INTERVAL = (15 * 60) -- 15 minutes, reported in seconds - -local TOTAL_CUMULATIVE_ENERGY_IMPORTED_MAP = "__total_cumulative_energy_imported_map" -local SUPPORTED_WATER_HEATER_MODES_WITH_IDX = "__supported_water_heater_modes_with_idx" -local COMPONENT_TO_ENDPOINT_MAP = "__component_to_endpoint_map" -local MGM3_PPM_CONVERSION_FACTOR = 24.45 - --- For RPC version >= 6, we can always assume that the values received from temperatureSetpoint --- are in Celsius, but we still limit the setpoint range to somewhat reasonable values. --- For RPC <= 5, this is a work around to handle when units for temperatureSetpoint is changed for the App. --- When units are switched, we will never know the units of the received command value as the arguments don't contain the unit. --- So to handle this we assume the following ranges considering usual thermostat/water-heater temperatures: --- Thermostat: --- 1. if the received setpoint command value is in range 5 ~ 40, it is inferred as *C --- 2. if the received setpoint command value is in range 41 ~ 104, it is inferred as *F -local THERMOSTAT_MAX_TEMP_IN_C = version.rpc >= 6 and 100.0 or 40.0 -local THERMOSTAT_MIN_TEMP_IN_C = version.rpc >= 6 and 0.0 or 5.0 --- Water Heater: --- 1. if the received setpoint command value is in range 30 ~ 80, it is inferred as *C --- 2. if the received setpoint command value is in range 86 ~ 176, it is inferred as *F -local WATER_HEATER_MAX_TEMP_IN_C = version.rpc >= 6 and 100.0 or 80.0 -local WATER_HEATER_MIN_TEMP_IN_C = version.rpc >= 6 and 0.0 or 30.0 - -local setpoint_limit_device_field = { - MIN_SETPOINT_DEADBAND_CHECKED = "MIN_SETPOINT_DEADBAND_CHECKED", - MIN_HEAT = "MIN_HEAT", - MAX_HEAT = "MAX_HEAT", - MIN_COOL = "MIN_COOL", - MAX_COOL = "MAX_COOL", - MIN_DEADBAND = "MIN_DEADBAND", - MIN_TEMP = "MIN_TEMP", - MAX_TEMP = "MAX_TEMP" -} - -local battery_support = { - NO_BATTERY = "NO_BATTERY", - BATTERY_LEVEL = "BATTERY_LEVEL", - BATTERY_PERCENTAGE = "BATTERY_PERCENTAGE" -} - -local profiling_data = { - BATTERY_SUPPORT = "__BATTERY_SUPPORT", - THERMOSTAT_RUNNING_STATE_SUPPORT = "__THERMOSTAT_RUNNING_STATE_SUPPORT" -} - -local subscribed_attributes = { - [capabilities.switch.ID] = { - clusters.OnOff.attributes.OnOff - }, - [capabilities.temperatureMeasurement.ID] = { - clusters.Thermostat.attributes.LocalTemperature, - clusters.TemperatureMeasurement.attributes.MeasuredValue, - clusters.TemperatureMeasurement.attributes.MinMeasuredValue, - clusters.TemperatureMeasurement.attributes.MaxMeasuredValue - }, - [capabilities.relativeHumidityMeasurement.ID] = { - clusters.RelativeHumidityMeasurement.attributes.MeasuredValue - }, - [capabilities.thermostatMode.ID] = { - clusters.Thermostat.attributes.SystemMode, - clusters.Thermostat.attributes.ControlSequenceOfOperation - }, - [capabilities.thermostatOperatingState.ID] = { - clusters.Thermostat.attributes.ThermostatRunningState - }, - [capabilities.thermostatFanMode.ID] = { - clusters.FanControl.attributes.FanModeSequence, - clusters.FanControl.attributes.FanMode - }, - [capabilities.thermostatCoolingSetpoint.ID] = { - clusters.Thermostat.attributes.OccupiedCoolingSetpoint, - clusters.Thermostat.attributes.AbsMinCoolSetpointLimit, - clusters.Thermostat.attributes.AbsMaxCoolSetpointLimit - }, - [capabilities.thermostatHeatingSetpoint.ID] = { - clusters.Thermostat.attributes.OccupiedHeatingSetpoint, - clusters.Thermostat.attributes.AbsMinHeatSetpointLimit, - clusters.Thermostat.attributes.AbsMaxHeatSetpointLimit - }, - [capabilities.airConditionerFanMode.ID] = { - clusters.FanControl.attributes.FanModeSequence, - clusters.FanControl.attributes.FanMode - }, - [capabilities.airPurifierFanMode.ID] = { - clusters.FanControl.attributes.FanModeSequence, - clusters.FanControl.attributes.FanMode - }, - [capabilities.fanMode.ID] = { - clusters.FanControl.attributes.FanModeSequence, - clusters.FanControl.attributes.FanMode - }, - [capabilities.fanSpeedPercent.ID] = { - clusters.FanControl.attributes.PercentCurrent - }, - [capabilities.windMode.ID] = { - clusters.FanControl.attributes.WindSupport, - clusters.FanControl.attributes.WindSetting - }, - [capabilities.fanOscillationMode.ID] = { - clusters.FanControl.attributes.RockSupport, - clusters.FanControl.attributes.RockSetting - }, - [capabilities.battery.ID] = { - clusters.PowerSource.attributes.BatPercentRemaining - }, - [capabilities.batteryLevel.ID] = { - clusters.PowerSource.attributes.BatChargeLevel - }, - [capabilities.filterState.ID] = { - clusters.HepaFilterMonitoring.attributes.Condition, - clusters.ActivatedCarbonFilterMonitoring.attributes.Condition - }, - [capabilities.filterStatus.ID] = { - clusters.HepaFilterMonitoring.attributes.ChangeIndication, - clusters.ActivatedCarbonFilterMonitoring.attributes.ChangeIndication - }, - [capabilities.airQualityHealthConcern.ID] = { - clusters.AirQuality.attributes.AirQuality - }, - [capabilities.carbonMonoxideMeasurement.ID] = { - clusters.CarbonMonoxideConcentrationMeasurement.attributes.MeasuredValue, - clusters.CarbonMonoxideConcentrationMeasurement.attributes.MeasurementUnit, - }, - [capabilities.carbonMonoxideHealthConcern.ID] = { - clusters.CarbonMonoxideConcentrationMeasurement.attributes.LevelValue, - }, - [capabilities.carbonDioxideMeasurement.ID] = { - clusters.CarbonDioxideConcentrationMeasurement.attributes.MeasuredValue, - clusters.CarbonDioxideConcentrationMeasurement.attributes.MeasurementUnit, - }, - [capabilities.carbonDioxideHealthConcern.ID] = { - clusters.CarbonDioxideConcentrationMeasurement.attributes.LevelValue, - }, - [capabilities.nitrogenDioxideMeasurement.ID] = { - clusters.NitrogenDioxideConcentrationMeasurement.attributes.MeasuredValue, - clusters.NitrogenDioxideConcentrationMeasurement.attributes.MeasurementUnit - }, - [capabilities.nitrogenDioxideHealthConcern.ID] = { - clusters.NitrogenDioxideConcentrationMeasurement.attributes.LevelValue, - }, - [capabilities.ozoneMeasurement.ID] = { - clusters.OzoneConcentrationMeasurement.attributes.MeasuredValue, - clusters.OzoneConcentrationMeasurement.attributes.MeasurementUnit - }, - [capabilities.ozoneHealthConcern.ID] = { - clusters.OzoneConcentrationMeasurement.attributes.LevelValue, - }, - [capabilities.formaldehydeMeasurement.ID] = { - clusters.FormaldehydeConcentrationMeasurement.attributes.MeasuredValue, - clusters.FormaldehydeConcentrationMeasurement.attributes.MeasurementUnit, - }, - [capabilities.formaldehydeHealthConcern.ID] = { - clusters.FormaldehydeConcentrationMeasurement.attributes.LevelValue, - }, - [capabilities.veryFineDustSensor.ID] = { - clusters.Pm1ConcentrationMeasurement.attributes.MeasuredValue, - clusters.Pm1ConcentrationMeasurement.attributes.MeasurementUnit, - }, - [capabilities.veryFineDustHealthConcern.ID] = { - clusters.Pm1ConcentrationMeasurement.attributes.LevelValue, - }, - [capabilities.fineDustHealthConcern.ID] = { - clusters.Pm25ConcentrationMeasurement.attributes.LevelValue, - }, - [capabilities.fineDustSensor.ID] = { - clusters.Pm25ConcentrationMeasurement.attributes.MeasuredValue, - clusters.Pm25ConcentrationMeasurement.attributes.MeasurementUnit, - }, - [capabilities.dustSensor.ID] = { - clusters.Pm25ConcentrationMeasurement.attributes.MeasuredValue, - clusters.Pm25ConcentrationMeasurement.attributes.MeasurementUnit, - clusters.Pm10ConcentrationMeasurement.attributes.MeasuredValue, - clusters.Pm10ConcentrationMeasurement.attributes.MeasurementUnit, - }, - [capabilities.dustHealthConcern.ID] = { - clusters.Pm10ConcentrationMeasurement.attributes.LevelValue, - }, - [capabilities.radonMeasurement.ID] = { - clusters.RadonConcentrationMeasurement.attributes.MeasuredValue, - clusters.RadonConcentrationMeasurement.attributes.MeasurementUnit, - }, - [capabilities.radonHealthConcern.ID] = { - clusters.RadonConcentrationMeasurement.attributes.LevelValue, - }, - [capabilities.tvocMeasurement.ID] = { - clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement.attributes.MeasuredValue, - clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement.attributes.MeasurementUnit, - }, - [capabilities.tvocHealthConcern.ID] = { - clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement.attributes.LevelValue - }, - [capabilities.powerMeter.ID] = { - clusters.ElectricalPowerMeasurement.attributes.ActivePower - }, - [capabilities.mode.ID] = { - clusters.WaterHeaterMode.attributes.CurrentMode, - clusters.WaterHeaterMode.attributes.SupportedModes - }, - [capabilities.powerConsumptionReport.ID] = { - clusters.ElectricalEnergyMeasurement.attributes.PeriodicEnergyImported, - clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyImported - }, - [capabilities.energyMeter.ID] = { - clusters.ElectricalEnergyMeasurement.attributes.PeriodicEnergyImported, - clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyImported - }, -} - -local function supports_capability_by_id_modular(device, capability, component) - if not device:get_field(SUPPORTED_COMPONENT_CAPABILITIES) then - device.log.warn_with({hub_logs = true}, "Device has overriden supports_capability_by_id, but does not have supported capabilities set.") - return false - end - for _, component_capabilities in ipairs(device:get_field(SUPPORTED_COMPONENT_CAPABILITIES)) do - local comp_id = component_capabilities[1] - local capability_ids = component_capabilities[2] - if (component == nil) or (component == comp_id) then - for _, cap in ipairs(capability_ids) do - if cap == capability then - return true - end - end - end - end - return false -end - -local function epoch_to_iso8601(time) - return os.date("!%Y-%m-%dT%H:%M:%SZ", time) -end - -local get_total_cumulative_energy_imported = function(device) - local total_cumulative_energy_imported = device:get_field(TOTAL_CUMULATIVE_ENERGY_IMPORTED_MAP) or {} - local total_energy = 0 - for _, energyWh in pairs(total_cumulative_energy_imported) do - total_energy = total_energy + energyWh - end - return total_energy -end - -local function report_power_consumption_to_st_energy(device, latest_total_imported_energy_wh) - local current_time = os.time() - local last_time = device:get_field(LAST_IMPORTED_REPORT_TIMESTAMP) or 0 - - -- Ensure that the previous report was sent at least 15 minutes ago - if MINIMUM_ST_ENERGY_REPORT_INTERVAL >= (current_time - last_time) then - return - end - - device:set_field(LAST_IMPORTED_REPORT_TIMESTAMP, current_time, { persist = true }) - - -- Calculate the energy delta between reports - local energy_delta_wh = 0.0 - local previous_imported_report = device:get_latest_state("main", capabilities.powerConsumptionReport.ID, - capabilities.powerConsumptionReport.powerConsumption.NAME) - if previous_imported_report and previous_imported_report.energy then - energy_delta_wh = math.max(latest_total_imported_energy_wh - previous_imported_report.energy, 0.0) - end - - -- Report the energy consumed during the time interval. The unit of these values should be 'Wh' - device:emit_component_event(device.profile.components["main"], capabilities.powerConsumptionReport.powerConsumption({ - start = epoch_to_iso8601(last_time), - ["end"] = epoch_to_iso8601(current_time - 1), - deltaEnergy = energy_delta_wh, - energy = latest_total_imported_energy_wh - })) -end - -local function device_removed(driver, device) - device.log.info("device removed") -end - -local function tbl_contains(array, value) - for idx, element in ipairs(array) do - if element == value then - return true, idx - end - end - return false, nil -end - -local function get_field_for_endpoint(device, field, endpoint) - return device:get_field(string.format("%s_%d", field, endpoint)) -end - -local function set_field_for_endpoint(device, field, endpoint, value, additional_params) - device:set_field(string.format("%s_%d", field, endpoint), value, additional_params) -end - -local function find_default_endpoint(device, cluster) - local res = device.MATTER_DEFAULT_ENDPOINT - local eps = embedded_cluster_utils.get_endpoints(device, cluster) - table.sort(eps) - for _, v in ipairs(eps) do - if v ~= 0 then --0 is the matter RootNode endpoint - return v - end - end - device.log.warn(string.format("Did not find default endpoint, will use endpoint %d instead", device.MATTER_DEFAULT_ENDPOINT)) - return res -end - -local function component_to_endpoint(device, component_name, cluster_id) - -- Use the find_default_endpoint function to return the first endpoint that - -- supports a given cluster. - local component_to_endpoint_map = device:get_field(COMPONENT_TO_ENDPOINT_MAP) - if component_to_endpoint_map ~= nil and component_to_endpoint_map[component_name] ~= nil then - return component_to_endpoint_map[component_name] - end - if not cluster_id then return device.MATTER_DEFAULT_ENDPOINT end - return find_default_endpoint(device, cluster_id) -end - -local endpoint_to_component = function (device, endpoint_id) - local component_to_endpoint_map = device:get_field(COMPONENT_TO_ENDPOINT_MAP) - if component_to_endpoint_map ~= nil then - for comp, ep in pairs(component_to_endpoint_map) do - if ep == endpoint_id then - return comp - end - end - end - return "main" -end - -local function device_init(driver, device) - if device:get_field(SUPPORTED_COMPONENT_CAPABILITIES) and (version.api < 15 or version.rpc < 9) then - -- assume that device is using a modular profile on 0.57 FW, override supports_capability_by_id - -- library function to utilize optional capabilities - device:extend_device("supports_capability_by_id", supports_capability_by_id_modular) - end - device:subscribe() - device:set_component_to_endpoint_fn(component_to_endpoint) - device:set_endpoint_to_component_fn(endpoint_to_component) - if not device:get_field(setpoint_limit_device_field.MIN_SETPOINT_DEADBAND_CHECKED) then - local auto_eps = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.AUTOMODE}) - --Query min setpoint deadband if needed - if #auto_eps ~= 0 and device:get_field(setpoint_limit_device_field.MIN_DEADBAND) == nil then - local deadband_read = im.InteractionRequest(im.InteractionRequest.RequestType.READ, {}) - deadband_read:merge(clusters.Thermostat.attributes.MinSetpointDeadBand:read()) - device:send(deadband_read) - end - end - - -- device energy reporting must be handled cumulatively, periodically, or by both simulatanously. - -- To ensure a single source of truth, we only handle a device's periodic reporting if cumulative reporting is not supported. - local electrical_energy_measurement_eps = embedded_cluster_utils.get_endpoints(device, clusters.ElectricalEnergyMeasurement.ID) - if #electrical_energy_measurement_eps > 0 then - local cumulative_energy_eps = embedded_cluster_utils.get_endpoints( - device, - clusters.ElectricalEnergyMeasurement.ID, - {feature_bitmap = clusters.ElectricalEnergyMeasurement.types.Feature.CUMULATIVE_ENERGY} - ) - if #cumulative_energy_eps == 0 then device:set_field(CUMULATIVE_REPORTS_NOT_SUPPORTED, true, {persist = false}) end - end -end - -local function info_changed(driver, device, event, args) - if device:get_field(SUPPORTED_COMPONENT_CAPABILITIES) then - -- This indicates the device should be using a modular profile, so - -- re-up subscription with new capabilities using the modular supports_capability override - device:extend_device("supports_capability_by_id", supports_capability_by_id_modular) - end - - if device.profile.id ~= args.old_st_store.profile.id then - device:subscribe() - end -end - -local function get_endpoints_for_dt(device, device_type) - local endpoints = {} - for _, ep in ipairs(device.endpoints) do - for _, dt in ipairs(ep.device_types) do - if dt.device_type_id == device_type then - table.insert(endpoints, ep.endpoint_id) - break - end - end - end - table.sort(endpoints) - return endpoints -end - -local function get_device_type(device) - -- For cases where a device has multiple device types, this list indicates which - -- device type will be the "main" device type for purposes of selecting a profile - -- with an appropriate category. This is done to promote consistency between - -- devices with similar device type compositions that may report their device types - -- listed in different orders - local device_type_priority = { - [HEAT_PUMP_DEVICE_TYPE_ID] = 1, - [RAC_DEVICE_TYPE_ID] = 2, - [AP_DEVICE_TYPE_ID] = 3, - [THERMOSTAT_DEVICE_TYPE_ID] = 4, - [FAN_DEVICE_TYPE_ID] = 5, - [WATER_HEATER_DEVICE_TYPE_ID] = 6, - } - - local main_device_type = false - - for _, ep in ipairs(device.endpoints) do - if ep.device_types ~= nil then - for _, dt in ipairs(ep.device_types) do - if not device_type_priority[main_device_type] or (device_type_priority[dt.device_type_id] and - device_type_priority[dt.device_type_id] < device_type_priority[main_device_type]) then - main_device_type = dt.device_type_id - end - end - end - end - - return main_device_type -end - -local AIR_QUALITY_MAP = { - {capabilities.carbonDioxideMeasurement.ID, "-co2", clusters.CarbonDioxideConcentrationMeasurement}, - {capabilities.carbonDioxideHealthConcern.ID, "-co2", clusters.CarbonDioxideConcentrationMeasurement}, - {capabilities.carbonMonoxideMeasurement.ID, "-co", clusters.CarbonMonoxideConcentrationMeasurement}, - {capabilities.carbonMonoxideHealthConcern.ID, "-co", clusters.CarbonMonoxideConcentrationMeasurement}, - {capabilities.dustSensor.ID, "-pm10", clusters.Pm10ConcentrationMeasurement}, - {capabilities.dustHealthConcern.ID, "-pm10", clusters.Pm10ConcentrationMeasurement}, - {capabilities.fineDustSensor.ID, "-pm25", clusters.Pm25ConcentrationMeasurement}, - {capabilities.fineDustHealthConcern.ID, "-pm25", clusters.Pm25ConcentrationMeasurement}, - {capabilities.formaldehydeMeasurement.ID, "-ch2o", clusters.FormaldehydeConcentrationMeasurement}, - {capabilities.formaldehydeHealthConcern.ID, "-ch2o", clusters.FormaldehydeConcentrationMeasurement}, - {capabilities.nitrogenDioxideHealthConcern.ID, "-no2", clusters.NitrogenDioxideConcentrationMeasurement}, - {capabilities.nitrogenDioxideMeasurement.ID, "-no2", clusters.NitrogenDioxideConcentrationMeasurement}, - {capabilities.ozoneHealthConcern.ID, "-ozone", clusters.OzoneConcentrationMeasurement}, - {capabilities.ozoneMeasurement.ID, "-ozone", clusters.OzoneConcentrationMeasurement}, - {capabilities.radonHealthConcern.ID, "-radon", clusters.RadonConcentrationMeasurement}, - {capabilities.radonMeasurement.ID, "-radon", clusters.RadonConcentrationMeasurement}, - {capabilities.tvocHealthConcern.ID, "-tvoc", clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement}, - {capabilities.tvocMeasurement.ID, "-tvoc", clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement}, - {capabilities.veryFineDustHealthConcern.ID, "-pm1", clusters.Pm1ConcentrationMeasurement}, - {capabilities.veryFineDustSensor.ID, "-pm1", clusters.Pm1ConcentrationMeasurement}, -} - -local function create_level_measurement_profile(device) - local meas_name, level_name = "", "" - for _, details in ipairs(AIR_QUALITY_MAP) do - local cap_id = details[1] - local cluster = details[3] - -- capability describes either a HealthConcern or Measurement/Sensor - if (cap_id:match("HealthConcern$")) then - local attr_eps = embedded_cluster_utils.get_endpoints(device, cluster.ID, { feature_bitmap = cluster.types.Feature.LEVEL_INDICATION }) - if #attr_eps > 0 then - level_name = level_name .. details[2] - end - elseif (cap_id:match("Measurement$") or cap_id:match("Sensor$")) then - local attr_eps = embedded_cluster_utils.get_endpoints(device, cluster.ID, { feature_bitmap = cluster.types.Feature.NUMERIC_MEASUREMENT }) - if #attr_eps > 0 then - meas_name = meas_name .. details[2] - end - end - end - return meas_name, level_name -end - -local function supported_level_measurements(device) - local measurement_caps, level_caps = {}, {} - for _, details in ipairs(AIR_QUALITY_MAP) do - local cap_id = details[1] - local cluster = details[3] - -- capability describes either a HealthConcern or Measurement/Sensor - if (cap_id:match("HealthConcern$")) then - local attr_eps = embedded_cluster_utils.get_endpoints(device, cluster.ID, { feature_bitmap = cluster.types.Feature.LEVEL_INDICATION }) - if #attr_eps > 0 then - table.insert(level_caps, cap_id) - end - elseif (cap_id:match("Measurement$") or cap_id:match("Sensor$")) then - local attr_eps = embedded_cluster_utils.get_endpoints(device, cluster.ID, { feature_bitmap = cluster.types.Feature.NUMERIC_MEASUREMENT }) - if #attr_eps > 0 then - table.insert(measurement_caps, cap_id) - end - end - end - return measurement_caps, level_caps -end - -local function create_air_quality_sensor_profile(device) - local aqs_eps = embedded_cluster_utils.get_endpoints(device, clusters.AirQuality.ID) - local profile_name = "" - if #aqs_eps > 0 then - profile_name = profile_name .. "-aqs" - end - local meas_name, level_name = create_level_measurement_profile(device) - if meas_name ~= "" then - profile_name = profile_name .. meas_name .. "-meas" - end - if level_name ~= "" then - profile_name = profile_name .. level_name .. "-level" - end - return profile_name -end - -local function create_fan_profile(device) - local fan_eps = device:get_endpoints(clusters.FanControl.ID) - local wind_eps = device:get_endpoints(clusters.FanControl.ID, {feature_bitmap = clusters.FanControl.types.FanControlFeature.WIND}) - local rock_eps = device:get_endpoints(clusters.FanControl.ID, {feature_bitmap = clusters.FanControl.types.Feature.ROCKING}) - local profile_name = "" - if #fan_eps > 0 then - profile_name = profile_name .. "-fan" - end - if #rock_eps > 0 then - profile_name = profile_name .. "-rock" - end - if #wind_eps > 0 then - profile_name = profile_name .. "-wind" - end - return profile_name -end - -local function create_air_purifier_profile(device) - local hepa_filter_eps = embedded_cluster_utils.get_endpoints(device, clusters.HepaFilterMonitoring.ID) - local ac_filter_eps = embedded_cluster_utils.get_endpoints(device, clusters.ActivatedCarbonFilterMonitoring.ID) - local fan_eps_seen = false - local profile_name = "air-purifier" - if #hepa_filter_eps > 0 then - profile_name = profile_name .. "-hepa" - end - if #ac_filter_eps > 0 then - profile_name = profile_name .. "-ac" - end - - -- air purifier profiles include -fan later in the name for historical reasons. - -- save this information for use at that point. - local fan_profile = create_fan_profile(device) - if fan_profile ~= "" then - fan_eps_seen = true - end - fan_profile = string.gsub(fan_profile, "-fan", "") - profile_name = profile_name .. fan_profile - - return profile_name, fan_eps_seen -end - -local function create_thermostat_modes_profile(device) - local heat_eps = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.HEATING}) - local cool_eps = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.COOLING}) - - local thermostat_modes = "" - if #heat_eps == 0 and #cool_eps == 0 then - return "No Heating nor Cooling Support" - elseif #heat_eps > 0 and #cool_eps == 0 then - thermostat_modes = thermostat_modes .. "-heating-only" - elseif #cool_eps > 0 and #heat_eps == 0 then - thermostat_modes = thermostat_modes .. "-cooling-only" - end - return thermostat_modes -end - -local function profiling_data_still_required(device) - for _, field in pairs(profiling_data) do - if device:get_field(field) == nil then - return true -- data still required if a field is nil - end - end - return false -end - -local function match_profile_switch(driver, device) - if profiling_data_still_required(device) then return end - - local running_state_supported = device:get_field(profiling_data.THERMOSTAT_RUNNING_STATE_SUPPORT) - local battery_supported = device:get_field(profiling_data.BATTERY_SUPPORT) - - local thermostat_eps = device:get_endpoints(clusters.Thermostat.ID) - local humidity_eps = device:get_endpoints(clusters.RelativeHumidityMeasurement.ID) - local device_type = get_device_type(device) - local profile_name - if device_type == RAC_DEVICE_TYPE_ID then - profile_name = "room-air-conditioner" - - if #humidity_eps > 0 then - profile_name = profile_name .. "-humidity" - end - - -- Room AC does not support the rocking feature of FanControl. - local fan_name = create_fan_profile(device) - fan_name = string.gsub(fan_name, "-rock", "") - profile_name = profile_name .. fan_name - - local thermostat_modes = create_thermostat_modes_profile(device) - if thermostat_modes == "" then - profile_name = profile_name .. "-heating-cooling" - else - device.log.warn_with({hub_logs=true}, "Device does not support both heating and cooling. No matching profile") - return - end - - if profile_name == "room-air-conditioner-humidity-fan-wind-heating-cooling" then - profile_name = "room-air-conditioner" - end - - if not running_state_supported and profile_name == "room-air-conditioner-fan-heating-cooling" then - profile_name = profile_name .. "-nostate" - end - - elseif device_type == FAN_DEVICE_TYPE_ID then - profile_name = create_fan_profile(device) - -- remove leading "-" - profile_name = string.sub(profile_name, 2) - if profile_name == "fan" then - profile_name = "fan-generic" - end - - elseif device_type == AP_DEVICE_TYPE_ID then - local fan_eps_found - profile_name, fan_eps_found = create_air_purifier_profile(device) - if #thermostat_eps > 0 then - profile_name = profile_name .. "-thermostat" - - if #humidity_eps > 0 then - profile_name = profile_name .. "-humidity" - end - - if fan_eps_found then - profile_name = profile_name .. "-fan" - end - - local thermostat_modes = create_thermostat_modes_profile(device) - if thermostat_modes ~= "No Heating nor Cooling Support" then - profile_name = profile_name .. thermostat_modes - end - - if not running_state_supported then - profile_name = profile_name .. "-nostate" - end - - if battery_supported == battery_support.BATTERY_LEVEL then - profile_name = profile_name .. "-batteryLevel" - elseif battery_supported == battery_support.NO_BATTERY then - profile_name = profile_name .. "-nobattery" - end - elseif #device:get_endpoints(clusters.TemperatureMeasurement.ID) > 0 then - profile_name = profile_name .. "-temperature" - - if #humidity_eps > 0 then - profile_name = profile_name .. "-humidity" - end - - if fan_eps_found then - profile_name = profile_name .. "-fan" - end - end - profile_name = profile_name .. create_air_quality_sensor_profile(device) - elseif device_type == WATER_HEATER_DEVICE_TYPE_ID then - -- If a Water Heater is composed of Electrical Sensor device type, it must support both ElectricalEnergyMeasurement and - -- ElectricalPowerMeasurement clusters. - local electrical_sensor_eps = get_endpoints_for_dt(device, ELECTRICAL_SENSOR_DEVICE_TYPE_ID) or {} - if #electrical_sensor_eps > 0 then - profile_name = "water-heater-power-energy-powerConsumption" - end - elseif device_type == HEAT_PUMP_DEVICE_TYPE_ID then - profile_name = "heat-pump" - local MAX_HEAT_PUMP_THERMOSTAT_COMPONENTS = 2 - for i = 1, math.min(MAX_HEAT_PUMP_THERMOSTAT_COMPONENTS, #thermostat_eps) do - profile_name = profile_name .. "-thermostat" - if tbl_contains(humidity_eps, thermostat_eps[i]) then - profile_name = profile_name .. "-humidity" - end - end - elseif #thermostat_eps > 0 then - profile_name = "thermostat" - - if #humidity_eps > 0 then - profile_name = profile_name .. "-humidity" - end - - -- thermostat profiles support neither wind nor rocking FanControl attributes - local fan_name = create_fan_profile(device) - if fan_name ~= "" then - profile_name = profile_name .. "-fan" - end - - local thermostat_modes = create_thermostat_modes_profile(device) - if thermostat_modes == "No Heating nor Cooling Support" then - device.log.warn_with({hub_logs=true}, "Device does not support either heating or cooling. No matching profile") - return - else - profile_name = profile_name .. thermostat_modes - end - - if not running_state_supported then - profile_name = profile_name .. "-nostate" - end - - if battery_supported == battery_support.BATTERY_LEVEL then - profile_name = profile_name .. "-batteryLevel" - elseif battery_supported == battery_support.NO_BATTERY then - profile_name = profile_name .. "-nobattery" - end - else - device.log.warn_with({hub_logs=true}, "Device type is not supported in thermostat driver") - return - end - - if profile_name then - device.log.info_with({hub_logs=true}, string.format("Updating device profile to %s.", profile_name)) - device:try_update_metadata({profile = profile_name}) - end - -- clear all profiling data fields after profiling is complete. - for _, field in pairs(profiling_data) do - device:set_field(field, nil) - end -end - -local function get_thermostat_optional_capabilities(device) - local heat_eps = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.HEATING}) - local cool_eps = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.COOLING}) - local running_state_supported = device:get_field(profiling_data.THERMOSTAT_RUNNING_STATE_SUPPORT) - - local supported_thermostat_capabilities = {} - - if #heat_eps > 0 then - table.insert(supported_thermostat_capabilities, capabilities.thermostatHeatingSetpoint.ID) - end - if #cool_eps > 0 then - table.insert(supported_thermostat_capabilities, capabilities.thermostatCoolingSetpoint.ID) - end - - if running_state_supported then - table.insert(supported_thermostat_capabilities, capabilities.thermostatOperatingState.ID) - end - - return supported_thermostat_capabilities -end - -local function get_air_quality_optional_capabilities(device) - local supported_air_quality_capabilities = {} - - local measurement_caps, level_caps = supported_level_measurements(device) - - for _, cap_id in ipairs(measurement_caps) do - table.insert(supported_air_quality_capabilities, cap_id) - end - - for _, cap_id in ipairs(level_caps) do - table.insert(supported_air_quality_capabilities, cap_id) - end - - return supported_air_quality_capabilities -end - -local function match_modular_profile_air_purifer(driver, device) - local optional_supported_component_capabilities = {} - local main_component_capabilities = {} - local hepa_filter_component_capabilities = {} - local ac_filter_component_capabilties = {} - local profile_name = "air-purifier-modular" - - local MAIN_COMPONENT_IDX = 1 - local CAPABILITIES_LIST_IDX = 2 - - local humidity_eps = device:get_endpoints(clusters.RelativeHumidityMeasurement.ID) - local temp_eps = embedded_cluster_utils.get_endpoints(device, clusters.TemperatureMeasurement.ID) - if #humidity_eps > 0 then - table.insert(main_component_capabilities, capabilities.relativeHumidityMeasurement.ID) - end - if #temp_eps > 0 then - table.insert(main_component_capabilities, capabilities.temperatureMeasurement.ID) - end - - local hepa_filter_eps = embedded_cluster_utils.get_endpoints(device, clusters.HepaFilterMonitoring.ID) - local ac_filter_eps = embedded_cluster_utils.get_endpoints(device, clusters.ActivatedCarbonFilterMonitoring.ID) - - if #hepa_filter_eps > 0 then - local filter_state_eps = embedded_cluster_utils.get_endpoints(device, clusters.HepaFilterMonitoring.ID, {feature_bitmap = clusters.HepaFilterMonitoring.types.Feature.CONDITION}) - if #filter_state_eps > 0 then - table.insert(hepa_filter_component_capabilities, capabilities.filterState.ID) - end - - table.insert(hepa_filter_component_capabilities, capabilities.filterStatus.ID) - end - if #ac_filter_eps > 0 then - local filter_state_eps = embedded_cluster_utils.get_endpoints(device, clusters.ActivatedCarbonFilterMonitoring.ID, {feature_bitmap = clusters.ActivatedCarbonFilterMonitoring.types.Feature.CONDITION}) - if #filter_state_eps > 0 then - table.insert(ac_filter_component_capabilties, capabilities.filterState.ID) - end - - table.insert(ac_filter_component_capabilties, capabilities.filterStatus.ID) - end - - -- determine fan capabilities, note that airPurifierFanMode is already mandatory - local rock_eps = device:get_endpoints(clusters.FanControl.ID, {feature_bitmap = clusters.FanControl.types.Feature.ROCKING}) - local wind_eps = device:get_endpoints(clusters.FanControl.ID, {feature_bitmap = clusters.FanControl.types.FanControlFeature.WIND}) - - if #rock_eps > 0 then - table.insert(main_component_capabilities, capabilities.fanOscillationMode.ID) - end - if #wind_eps > 0 then - table.insert(main_component_capabilities, capabilities.windMode.ID) - end - - local thermostat_eps = device:get_endpoints(clusters.Thermostat.ID) - - if #thermostat_eps > 0 then - -- thermostatMode and temperatureMeasurement should be expected if thermostat is present - table.insert(main_component_capabilities, capabilities.thermostatMode.ID) - - -- only add temperatureMeasurement if it is not already added via TemperatureMeasurement cluster support - if #temp_eps == 0 then - table.insert(main_component_capabilities, capabilities.temperatureMeasurement.ID) - end - local thermostat_capabilities = get_thermostat_optional_capabilities(device) - for _, capability_id in pairs(thermostat_capabilities) do - table.insert(main_component_capabilities, capability_id) - end - end - - local aqs_eps = embedded_cluster_utils.get_endpoints(device, clusters.AirQuality.ID) - if #aqs_eps > 0 then - table.insert(main_component_capabilities, capabilities.airQualityHealthConcern.ID) - end - - local supported_air_quality_capabilities = get_air_quality_optional_capabilities(device) - for _, capability_id in pairs(supported_air_quality_capabilities) do - table.insert(main_component_capabilities, capability_id) - end - - table.insert(optional_supported_component_capabilities, {"main", main_component_capabilities}) - if #ac_filter_component_capabilties > 0 then - table.insert(optional_supported_component_capabilities, {"activatedCarbonFilter", ac_filter_component_capabilties}) - end - if #hepa_filter_component_capabilities > 0 then - table.insert(optional_supported_component_capabilities, {"hepaFilter", hepa_filter_component_capabilities}) - end - - device:try_update_metadata({profile = profile_name, optional_component_capabilities = optional_supported_component_capabilities}) - - -- earlier modular profile gating (min api v14, rpc 8) ensures we are running >= 0.57 FW. - -- This gating specifies a workaround required only for 0.57 FW, which is not needed for 0.58 and higher. - if version.api < 15 or version.rpc < 9 then - -- add mandatory capabilities for subscription - local total_supported_capabilities = optional_supported_component_capabilities - table.insert(total_supported_capabilities[MAIN_COMPONENT_IDX][CAPABILITIES_LIST_IDX], capabilities.airPurifierFanMode.ID) - table.insert(total_supported_capabilities[MAIN_COMPONENT_IDX][CAPABILITIES_LIST_IDX], capabilities.fanSpeedPercent.ID) - table.insert(total_supported_capabilities[MAIN_COMPONENT_IDX][CAPABILITIES_LIST_IDX], capabilities.refresh.ID) - table.insert(total_supported_capabilities[MAIN_COMPONENT_IDX][CAPABILITIES_LIST_IDX], capabilities.firmwareUpdate.ID) - - device:set_field(SUPPORTED_COMPONENT_CAPABILITIES, total_supported_capabilities, { persist = true }) - end -end - -local function match_modular_profile_thermostat(driver, device) - local optional_supported_component_capabilities = {} - local main_component_capabilities = {} - local profile_name = "thermostat-modular" - - local humidity_eps = device:get_endpoints(clusters.RelativeHumidityMeasurement.ID) - if #humidity_eps > 0 then - table.insert(main_component_capabilities, capabilities.relativeHumidityMeasurement.ID) - end - - -- determine fan capabilities - local fan_eps = device:get_endpoints(clusters.FanControl.ID) - local rock_eps = device:get_endpoints(clusters.FanControl.ID, {feature_bitmap = clusters.FanControl.types.Feature.ROCKING}) - local wind_eps = device:get_endpoints(clusters.FanControl.ID, {feature_bitmap = clusters.FanControl.types.FanControlFeature.WIND}) - - if #fan_eps > 0 then - table.insert(main_component_capabilities, capabilities.fanMode.ID) - end - if #rock_eps > 0 then - table.insert(main_component_capabilities, capabilities.fanOscillationMode.ID) - end - if #wind_eps > 0 then - table.insert(main_component_capabilities, capabilities.windMode.ID) - end - - local thermostat_capabilities = get_thermostat_optional_capabilities(device) - for _, capability_id in pairs(thermostat_capabilities) do - table.insert(main_component_capabilities, capability_id) - end - - local battery_supported = device:get_field(profiling_data.BATTERY_SUPPORT) - if battery_supported == battery_support.BATTERY_LEVEL then - table.insert(main_component_capabilities, capabilities.batteryLevel.ID) - elseif battery_supported == battery_support.BATTERY_PERCENTAGE then - table.insert(main_component_capabilities, capabilities.battery.ID) - end - - table.insert(optional_supported_component_capabilities, {"main", main_component_capabilities}) - device:try_update_metadata({profile = profile_name, optional_component_capabilities = optional_supported_component_capabilities}) - - -- earlier modular profile gating (min api v14, rpc 8) ensures we are running >= 0.57 FW. - -- This gating specifies a workaround required only for 0.57 FW, which is not needed for 0.58 and higher. - if version.api < 15 or version.rpc < 9 then - -- add mandatory capabilities for subscription - local total_supported_capabilities = optional_supported_component_capabilities - table.insert(main_component_capabilities, capabilities.thermostatMode.ID) - table.insert(main_component_capabilities, capabilities.temperatureMeasurement.ID) - table.insert(main_component_capabilities, capabilities.refresh.ID) - table.insert(main_component_capabilities, capabilities.firmwareUpdate.ID) - - device:set_field(SUPPORTED_COMPONENT_CAPABILITIES, total_supported_capabilities, { persist = true }) - end -end - -local function match_modular_profile_room_ac(driver, device) - local running_state_supported = device:get_field(profiling_data.THERMOSTAT_RUNNING_STATE_SUPPORT) - local humidity_eps = device:get_endpoints(clusters.RelativeHumidityMeasurement.ID) - local optional_supported_component_capabilities = {} - local main_component_capabilities = {} - local profile_name = "room-air-conditioner-modular" - - if #humidity_eps > 0 then - table.insert(main_component_capabilities, capabilities.relativeHumidityMeasurement.ID) - end - - -- determine fan capabilities - local fan_eps = device:get_endpoints(clusters.FanControl.ID) - local wind_eps = device:get_endpoints(clusters.FanControl.ID, {feature_bitmap = clusters.FanControl.types.FanControlFeature.WIND}) - -- Note: Room AC does not support the rocking feature of FanControl. - - if #fan_eps > 0 then - table.insert(main_component_capabilities, capabilities.airConditionerFanMode.ID) - table.insert(main_component_capabilities, capabilities.fanSpeedPercent.ID) - end - if #wind_eps > 0 then - table.insert(main_component_capabilities, capabilities.windMode.ID) - end - - local heat_eps = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.HEATING}) - local cool_eps = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.COOLING}) - - if #heat_eps > 0 then - table.insert(main_component_capabilities, capabilities.thermostatHeatingSetpoint.ID) - end - if #cool_eps > 0 then - table.insert(main_component_capabilities, capabilities.thermostatCoolingSetpoint.ID) - end - - if running_state_supported then - table.insert(main_component_capabilities, capabilities.thermostatOperatingState.ID) - end - - table.insert(optional_supported_component_capabilities, {"main", main_component_capabilities}) - device:try_update_metadata({profile = profile_name, optional_component_capabilities = optional_supported_component_capabilities}) - - -- earlier modular profile gating (min api v14, rpc 8) ensures we are running >= 0.57 FW. - -- This gating specifies a workaround required only for 0.57 FW, which is not needed for 0.58 and higher. - if version.api < 15 or version.rpc < 9 then - -- add mandatory capabilities for subscription - local total_supported_capabilities = optional_supported_component_capabilities - table.insert(main_component_capabilities, capabilities.switch.ID) - table.insert(main_component_capabilities, capabilities.temperatureMeasurement.ID) - table.insert(main_component_capabilities, capabilities.thermostatMode.ID) - table.insert(main_component_capabilities, capabilities.refresh.ID) - table.insert(main_component_capabilities, capabilities.firmwareUpdate.ID) - - device:set_field(SUPPORTED_COMPONENT_CAPABILITIES, total_supported_capabilities, { persist = true }) - end -end - -local function match_modular_profile(driver, device, device_type) - if profiling_data_still_required(device) then return end - - if device_type == AP_DEVICE_TYPE_ID then - match_modular_profile_air_purifer(driver, device) - elseif device_type == RAC_DEVICE_TYPE_ID then - match_modular_profile_room_ac(driver, device) - elseif device_type == THERMOSTAT_DEVICE_TYPE_ID then - match_modular_profile_thermostat(driver, device) - else - device.log.warn_with({hub_logs=true}, "Device type is not supported by modular profile in thermostat driver, trying profile switch instead") - match_profile_switch(driver, device) - return - end - - -- clear all profiling data fields after profiling is complete. - for _, field in pairs(profiling_data) do - device:set_field(field, nil) - end -end - -local function supports_modular_profile(device) - local supported_modular_device_types = { - AP_DEVICE_TYPE_ID, - RAC_DEVICE_TYPE_ID, - THERMOSTAT_DEVICE_TYPE_ID, - } - local device_type = get_device_type(device) - if not tbl_contains(supported_modular_device_types, device_type) then - device_type = false - end - return version.api >= 14 and version.rpc >= 8 and device_type -end - -function match_profile(driver, device) - local modular_device_type = supports_modular_profile(device) - if modular_device_type then - match_modular_profile(driver, device, modular_device_type) - else - match_profile_switch(driver, device) - end -end - -local function do_configure(driver, device) - match_profile(driver, device) -end - -local function driver_switched(driver, device) - match_profile(driver, device) -end - -local function device_added(driver, device) - local req = im.InteractionRequest(im.InteractionRequest.RequestType.READ, {}) - req:merge(clusters.Thermostat.attributes.ControlSequenceOfOperation:read(device)) - req:merge(clusters.FanControl.attributes.FanModeSequence:read(device)) - req:merge(clusters.FanControl.attributes.WindSupport:read(device)) - req:merge(clusters.FanControl.attributes.RockSupport:read(device)) - - local thermostat_eps = device:get_endpoints(clusters.Thermostat.ID) - if #thermostat_eps > 0 then - req:merge(clusters.Thermostat.attributes.AttributeList:read(device)) - else - device:set_field(profiling_data.THERMOSTAT_RUNNING_STATE_SUPPORT, false) - end - local battery_feature_eps = device:get_endpoints(clusters.PowerSource.ID, {feature_bitmap = clusters.PowerSource.types.PowerSourceFeature.BATTERY}) - if #battery_feature_eps > 0 then - req:merge(clusters.PowerSource.attributes.AttributeList:read(device)) - else - device:set_field(profiling_data.BATTERY_SUPPORT, battery_support.NO_BATTERY) - end - device:send(req) - local heat_pump_eps = get_endpoints_for_dt(device, HEAT_PUMP_DEVICE_TYPE_ID) or {} - if #heat_pump_eps > 0 then - local thermostat_eps = get_endpoints_for_dt(device, THERMOSTAT_DEVICE_TYPE_ID) or {} - local component_to_endpoint_map = { - ["thermostatOne"] = thermostat_eps[1], - ["thermostatTwo"] = thermostat_eps[2], - } - device:set_field(COMPONENT_TO_ENDPOINT_MAP, component_to_endpoint_map, {persist = true}) - end -end - -local function store_unit_factory(capability_name) - return function(driver, device, ib, response) - device:set_field(capability_name.."_unit", ib.data.value, {persist = true}) - end -end - -local units = { - PPM = 0, - PPB = 1, - PPT = 2, - MGM3 = 3, - UGM3 = 4, - NGM3 = 5, - PM3 = 6, - BQM3 = 7, - PCIL = 0xFF -- not in matter spec -} - -local unit_strings = { - [units.PPM] = "ppm", - [units.PPB] = "ppb", - [units.PPT] = "ppt", - [units.MGM3] = "mg/m^3", - [units.NGM3] = "ng/m^3", - [units.UGM3] = "μg/m^3", - [units.BQM3] = "Bq/m^3", - [units.PCIL] = "pCi/L" -} - -local unit_default = { - [capabilities.carbonMonoxideMeasurement.NAME] = units.PPM, - [capabilities.carbonDioxideMeasurement.NAME] = units.PPM, - [capabilities.nitrogenDioxideMeasurement.NAME] = units.PPM, - [capabilities.ozoneMeasurement.NAME] = units.PPM, - [capabilities.formaldehydeMeasurement.NAME] = units.PPM, - [capabilities.veryFineDustSensor.NAME] = units.UGM3, - [capabilities.fineDustSensor.NAME] = units.UGM3, - [capabilities.dustSensor.NAME] = units.UGM3, - [capabilities.radonMeasurement.NAME] = units.BQM3, - [capabilities.tvocMeasurement.NAME] = units.PPB -- TVOC is typically within the range of 0-5500 ppb, with good to moderate values being < 660 ppb -} - --- All ConcentrationMesurement clusters inherit from the same base cluster definitions, --- so CarbonMonoxideConcentratinMeasurement is used below but the same enum types exist --- in all ConcentrationMeasurement clusters -local level_strings = { - [clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.UNKNOWN] = "unknown", - [clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.LOW] = "good", - [clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.MEDIUM] = "moderate", - [clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.HIGH] = "unhealthy", - [clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.CRITICAL] = "hazardous", -} - --- measured in g/mol -local molecular_weights = { - [capabilities.carbonDioxideMeasurement.NAME] = 44.010, - [capabilities.nitrogenDioxideMeasurement.NAME] = 28.014, - [capabilities.ozoneMeasurement.NAME] = 48.0, - [capabilities.formaldehydeMeasurement.NAME] = 30.031, - [capabilities.veryFineDustSensor.NAME] = "N/A", - [capabilities.fineDustSensor.NAME] = "N/A", - [capabilities.dustSensor.NAME] = "N/A", - [capabilities.radonMeasurement.NAME] = 222.018, - [capabilities.tvocMeasurement.NAME] = "N/A", -} - -local conversion_tables = { - [units.PPM] = { - [units.PPM] = function(value) return utils.round(value) end, - [units.PPB] = function(value) return utils.round(value * (10^3)) end, - [units.UGM3] = function(value, molecular_weight) return utils.round((value * molecular_weight * 10^3) / MGM3_PPM_CONVERSION_FACTOR) end, - [units.MGM3] = function(value, molecular_weight) return utils.round((value * molecular_weight) / MGM3_PPM_CONVERSION_FACTOR) end, - }, - [units.PPB] = { - [units.PPM] = function(value) return utils.round(value/(10^3)) end, - [units.PPB] = function(value) return utils.round(value) end, - }, - [units.PPT] = { - [units.PPM] = function(value) return utils.round(value/(10^6)) end - }, - [units.MGM3] = { - [units.UGM3] = function(value) return utils.round(value * (10^3)) end, - [units.PPM] = function(value, molecular_weight) return utils.round((value * MGM3_PPM_CONVERSION_FACTOR) / molecular_weight) end, - }, - [units.UGM3] = { - [units.UGM3] = function(value) return utils.round(value) end, - [units.PPM] = function(value, molecular_weight) return utils.round((value * MGM3_PPM_CONVERSION_FACTOR) / (molecular_weight * 10^3)) end, - }, - [units.NGM3] = { - [units.UGM3] = function(value) return utils.round(value/(10^3)) end - }, - [units.BQM3] = { - [units.PCIL] = function(value) return utils.round(value/37) end - }, -} - -local function unit_conversion(value, from_unit, to_unit, capability_name) - local conversion_function = conversion_tables[from_unit][to_unit] - if not conversion_function then - log.info_with( {hub_logs = true} , string.format("Unsupported unit conversion from %s to %s", unit_strings[from_unit], unit_strings[to_unit])) - return - end - - if not value then - log.info_with( {hub_logs = true} , "unit conversion value is nil") - return - end - - return conversion_function(value, molecular_weights[capability_name]) -end - -local function measurementHandlerFactory(capability_name, attribute, target_unit) - return function(driver, device, ib, response) - local reporting_unit = device:get_field(capability_name.."_unit") - - if not reporting_unit then - reporting_unit = unit_default[capability_name] - device:set_field(capability_name.."_unit", reporting_unit, {persist = true}) - end - - local value = nil - if reporting_unit then - value = unit_conversion(ib.data.value, reporting_unit, target_unit, capability_name) - end - - if value then - device:emit_event_for_endpoint(ib.endpoint_id, attribute({value = value, unit = unit_strings[target_unit]})) - -- handle case where device profile supports both fineDustLevel and dustLevel - if capability_name == capabilities.fineDustSensor.NAME and device:supports_capability(capabilities.dustSensor) then - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.dustSensor.fineDustLevel({value = value, unit = unit_strings[target_unit]})) - end - end - end -end - -local function levelHandlerFactory(attribute) - return function(driver, device, ib, response) - device:emit_event_for_endpoint(ib.endpoint_id, attribute(level_strings[ib.data.value])) - end -end - --- handlers -local function air_quality_attr_handler(driver, device, ib, response) - local state = ib.data.value - if state == 0 then -- Unknown - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.airQualityHealthConcern.airQualityHealthConcern.unknown()) - elseif state == 1 then -- Good - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.airQualityHealthConcern.airQualityHealthConcern.good()) - elseif state == 2 then -- Fair - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.airQualityHealthConcern.airQualityHealthConcern.moderate()) - elseif state == 3 then -- Moderate - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.airQualityHealthConcern.airQualityHealthConcern.slightlyUnhealthy()) - elseif state == 4 then -- Poor - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.airQualityHealthConcern.airQualityHealthConcern.unhealthy()) - elseif state == 5 then -- VeryPoor - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.airQualityHealthConcern.airQualityHealthConcern.veryUnhealthy()) - elseif state == 6 then -- ExtremelyPoor - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.airQualityHealthConcern.airQualityHealthConcern.hazardous()) - end -end - -local function on_off_attr_handler(driver, device, ib, response) - if ib.data.value then - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.switch.switch.on()) - else - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.switch.switch.off()) - end -end - -local function temp_event_handler(attribute) - return function(driver, device, ib, response) - if ib.data.value == nil then - return - end - local unit = "C" - - -- Only emit the capability for RPC version >= 5, since unit conversion for - -- range capabilities is only supported in that case. - if version.rpc >= 5 then - local event - if attribute == capabilities.thermostatCoolingSetpoint.coolingSetpoint then - local range = { - minimum = device:get_field(setpoint_limit_device_field.MIN_COOL) or THERMOSTAT_MIN_TEMP_IN_C, - maximum = device:get_field(setpoint_limit_device_field.MAX_COOL) or THERMOSTAT_MAX_TEMP_IN_C, - step = 0.1 - } - event = capabilities.thermostatCoolingSetpoint.coolingSetpointRange({value = range, unit = unit}) - device:emit_event_for_endpoint(ib.endpoint_id, event) - elseif attribute == capabilities.thermostatHeatingSetpoint.heatingSetpoint then - local MAX_TEMP_IN_C = THERMOSTAT_MAX_TEMP_IN_C - local MIN_TEMP_IN_C = THERMOSTAT_MIN_TEMP_IN_C - local is_water_heater_device = get_device_type(device) == WATER_HEATER_DEVICE_TYPE_ID - if is_water_heater_device then - MAX_TEMP_IN_C = WATER_HEATER_MAX_TEMP_IN_C - MIN_TEMP_IN_C = WATER_HEATER_MIN_TEMP_IN_C - end - - local range = { - minimum = device:get_field(setpoint_limit_device_field.MIN_HEAT) or MIN_TEMP_IN_C, - maximum = device:get_field(setpoint_limit_device_field.MAX_HEAT) or MAX_TEMP_IN_C, - step = 0.1 - } - event = capabilities.thermostatHeatingSetpoint.heatingSetpointRange({value = range, unit = unit}) - device:emit_event_for_endpoint(ib.endpoint_id, event) - end - end - - local temp = ib.data.value / 100.0 - device:emit_event_for_endpoint(ib.endpoint_id, attribute({value = temp, unit = unit})) - end -end - -local temp_attr_handler_factory = function(minOrMax) - return function(driver, device, ib, response) - if ib.data.value == nil then - return - end - local temp = ib.data.value / 100.0 - local unit = "C" - temp = utils.clamp_value(temp, THERMOSTAT_MIN_TEMP_IN_C, THERMOSTAT_MAX_TEMP_IN_C) - set_field_for_endpoint(device, minOrMax, ib.endpoint_id, temp) - local min = get_field_for_endpoint(device, setpoint_limit_device_field.MIN_TEMP, ib.endpoint_id) - local max = get_field_for_endpoint(device, setpoint_limit_device_field.MAX_TEMP, ib.endpoint_id) - if min ~= nil and max ~= nil then - if min < max then - -- Only emit the capability for RPC version >= 5 (unit conversion for - -- temperature range capability is only supported for RPC >= 5) - if version.rpc >= 5 then - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.temperatureMeasurement.temperatureRange({ value = { minimum = min, maximum = max }, unit = unit })) - end - set_field_for_endpoint(device, setpoint_limit_device_field.MIN_TEMP, ib.endpoint_id, nil) - set_field_for_endpoint(device, setpoint_limit_device_field.MAX_TEMP, ib.endpoint_id, nil) - else - device.log.warn_with({hub_logs = true}, string.format("Device reported a min temperature %d that is not lower than the reported max temperature %d", min, max)) - end - end - end -end - -local function humidity_attr_handler(driver, device, ib, response) - local humidity = math.floor(ib.data.value / 100.0) - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.relativeHumidityMeasurement.humidity(humidity)) -end - -local function system_mode_handler(driver, device, ib, response) - if device:get_field(OPTIONAL_THERMOSTAT_MODES_SEEN) == nil then -- this being nil means the sequence_of_operation_handler hasn't run. - device.log.info_with({hub_logs = true}, "In the SystemMode handler: ControlSequenceOfOperation has not run yet. Exiting early.") - device:set_field(SAVED_SYSTEM_MODE_IB, ib) - return - end - - local supported_modes = device:get_latest_state(device:endpoint_to_component(ib.endpoint_id), capabilities.thermostatMode.ID, capabilities.thermostatMode.supportedThermostatModes.NAME) or {} - -- check that the given mode was in the supported modes list - if tbl_contains(supported_modes, THERMOSTAT_MODE_MAP[ib.data.value].NAME) then - device:emit_event_for_endpoint(ib.endpoint_id, THERMOSTAT_MODE_MAP[ib.data.value]()) - return - end - -- if the value is not found in the supported modes list, check if it's disallowed and early return if so. - local disallowed_thermostat_modes = device:get_field(DISALLOWED_THERMOSTAT_MODES) or {} - if tbl_contains(disallowed_thermostat_modes, THERMOSTAT_MODE_MAP[ib.data.value].NAME) then - return - end - -- if we get here, then the reported mode is allowed and not in our mode map - -- add the mode to the OPTIONAL_THERMOSTAT_MODES_SEEN and supportedThermostatModes tables - local optional_modes_seen = utils.deep_copy(device:get_field(OPTIONAL_THERMOSTAT_MODES_SEEN)) or {} - table.insert(optional_modes_seen, THERMOSTAT_MODE_MAP[ib.data.value].NAME) - device:set_field(OPTIONAL_THERMOSTAT_MODES_SEEN, optional_modes_seen, {persist=true}) - local sm_copy = utils.deep_copy(supported_modes) - table.insert(sm_copy, THERMOSTAT_MODE_MAP[ib.data.value].NAME) - local supported_modes_event = capabilities.thermostatMode.supportedThermostatModes(sm_copy, {visibility = {displayed = false}}) - device:emit_event_for_endpoint(ib.endpoint_id, supported_modes_event) - device:emit_event_for_endpoint(ib.endpoint_id, THERMOSTAT_MODE_MAP[ib.data.value]()) -end - -local function running_state_handler(driver, device, ib, response) - for mode, operating_state in pairs(THERMOSTAT_OPERATING_MODE_MAP) do - if ((ib.data.value >> mode) & 1) > 0 then - device:emit_event_for_endpoint(ib.endpoint_id, operating_state()) - return - end - end - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.thermostatOperatingState.thermostatOperatingState.idle()) -end - -local function sequence_of_operation_handler(driver, device, ib, response) - -- The ControlSequenceOfOperation attribute only directly specifies what can't be operated by the operating environment, not what can. - -- However, we assert here that a Cooling enum value implies that SystemMode supports cooling, and the same for a Heating enum. - -- We also assert that Off is supported, though per spec this is optional. - if device:get_field(OPTIONAL_THERMOSTAT_MODES_SEEN) == nil then - device:set_field(OPTIONAL_THERMOSTAT_MODES_SEEN, {capabilities.thermostatMode.thermostatMode.off.NAME}, {persist=true}) - end - local supported_modes = utils.deep_copy(device:get_field(OPTIONAL_THERMOSTAT_MODES_SEEN)) - local disallowed_mode_operations = {} - - local modes_for_inclusion = {} - if ib.data.value <= clusters.Thermostat.attributes.ControlSequenceOfOperation.COOLING_WITH_REHEAT then - local _, found_idx = tbl_contains(supported_modes, capabilities.thermostatMode.thermostatMode.emergency_heat.NAME) - if found_idx then - table.remove(supported_modes, found_idx) -- if seen before, remove now - end - table.insert(supported_modes, capabilities.thermostatMode.thermostatMode.cool.NAME) - table.insert(disallowed_mode_operations, capabilities.thermostatMode.thermostatMode.heat.NAME) - table.insert(disallowed_mode_operations, capabilities.thermostatMode.thermostatMode.emergency_heat.NAME) - elseif ib.data.value <= clusters.Thermostat.attributes.ControlSequenceOfOperation.HEATING_WITH_REHEAT then - local _, found_idx = tbl_contains(supported_modes, capabilities.thermostatMode.thermostatMode.precooling.NAME) - if found_idx then - table.remove(supported_modes, found_idx) -- if seen before, remove now - end - table.insert(supported_modes, capabilities.thermostatMode.thermostatMode.heat.NAME) - table.insert(disallowed_mode_operations, capabilities.thermostatMode.thermostatMode.cool.NAME) - table.insert(disallowed_mode_operations, capabilities.thermostatMode.thermostatMode.precooling.NAME) - elseif ib.data.value <= clusters.Thermostat.attributes.ControlSequenceOfOperation.COOLING_AND_HEATING_WITH_REHEAT then - table.insert(modes_for_inclusion, capabilities.thermostatMode.thermostatMode.cool.NAME) - table.insert(modes_for_inclusion, capabilities.thermostatMode.thermostatMode.heat.NAME) - end - - -- check whether the Auto Mode should be supported in SystemMode, though this is unrelated to ControlSequenceOfOperation - local auto = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.AUTOMODE}) - if #auto > 0 then - table.insert(modes_for_inclusion, capabilities.thermostatMode.thermostatMode.auto.NAME) - else - table.insert(disallowed_mode_operations, capabilities.thermostatMode.thermostatMode.auto.NAME) - end - - -- if a disallowed value was once allowed and added, it should be removed now. - for index, mode in pairs(supported_modes) do - if tbl_contains(disallowed_mode_operations, mode) then - table.remove(supported_modes, index) - end - end - -- do not include any values twice - for _, mode in pairs(modes_for_inclusion) do - if not tbl_contains(supported_modes, mode) then - table.insert(supported_modes, mode) - end - end - device:set_field(DISALLOWED_THERMOSTAT_MODES, disallowed_mode_operations) - local event = capabilities.thermostatMode.supportedThermostatModes(supported_modes, {visibility = {displayed = false}}) - device:emit_event_for_endpoint(ib.endpoint_id, event) - - -- will be set by the SystemMode handler if this handler hasn't run yet. - if device:get_field(SAVED_SYSTEM_MODE_IB) then - system_mode_handler(driver, device, device:get_field(SAVED_SYSTEM_MODE_IB), response) - device:set_field(SAVED_SYSTEM_MODE_IB, nil) - end -end - -local function min_deadband_limit_handler(driver, device, ib, response) - local val = ib.data.value / 10.0 - log.info("Setting " .. setpoint_limit_device_field.MIN_DEADBAND .. " to " .. string.format("%s", val)) - device:set_field(setpoint_limit_device_field.MIN_DEADBAND, val, { persist = true }) - device:set_field(setpoint_limit_device_field.MIN_SETPOINT_DEADBAND_CHECKED, true, {persist = true}) -end - -local function fan_mode_handler(driver, device, ib, response) - local fan_mode_event = { - [clusters.FanControl.attributes.FanMode.OFF] = { capabilities.fanMode.fanMode.off(), - capabilities.airConditionerFanMode.fanMode("off"), - capabilities.airPurifierFanMode.airPurifierFanMode.off(), - nil }, -- 'OFF' is not supported by thermostatFanMode - [clusters.FanControl.attributes.FanMode.LOW] = { capabilities.fanMode.fanMode.low(), - capabilities.airConditionerFanMode.fanMode("low"), - capabilities.airPurifierFanMode.airPurifierFanMode.low(), - capabilities.thermostatFanMode.thermostatFanMode.on() }, - [clusters.FanControl.attributes.FanMode.MEDIUM] = { capabilities.fanMode.fanMode.medium(), - capabilities.airConditionerFanMode.fanMode("medium"), - capabilities.airPurifierFanMode.airPurifierFanMode.medium(), - capabilities.thermostatFanMode.thermostatFanMode.on() }, - [clusters.FanControl.attributes.FanMode.HIGH] = { capabilities.fanMode.fanMode.high(), - capabilities.airConditionerFanMode.fanMode("high"), - capabilities.airPurifierFanMode.airPurifierFanMode.high(), - capabilities.thermostatFanMode.thermostatFanMode.on() }, - [clusters.FanControl.attributes.FanMode.ON] = { capabilities.fanMode.fanMode.auto(), - capabilities.airConditionerFanMode.fanMode("auto"), - capabilities.airPurifierFanMode.airPurifierFanMode.auto(), - capabilities.thermostatFanMode.thermostatFanMode.on() }, - [clusters.FanControl.attributes.FanMode.AUTO] = { capabilities.fanMode.fanMode.auto(), - capabilities.airConditionerFanMode.fanMode("auto"), - capabilities.airPurifierFanMode.airPurifierFanMode.auto(), - capabilities.thermostatFanMode.thermostatFanMode.auto() }, - [clusters.FanControl.attributes.FanMode.SMART] = { capabilities.fanMode.fanMode.auto(), - capabilities.airConditionerFanMode.fanMode("auto"), - capabilities.airPurifierFanMode.airPurifierFanMode.auto(), - capabilities.thermostatFanMode.thermostatFanMode.auto() } - } - local fan_mode_idx = device:supports_capability_by_id(capabilities.fanMode.ID) and 1 or - device:supports_capability_by_id(capabilities.airConditionerFanMode.ID) and 2 or - device:supports_capability_by_id(capabilities.airPurifierFanMode.ID) and 3 or - device:supports_capability_by_id(capabilities.thermostatFanMode.ID) and 4 - if fan_mode_idx ~= false and fan_mode_event[ib.data.value][fan_mode_idx] then - device:emit_event_for_endpoint(ib.endpoint_id, fan_mode_event[ib.data.value][fan_mode_idx]) - else - log.warn(string.format("Invalid Fan Mode (%s)", ib.data.value)) - end -end - -local function fan_mode_sequence_handler(driver, device, ib, response) - local supportedFanModes, supported_fan_modes_attribute - if ib.data.value == clusters.FanControl.attributes.FanModeSequence.OFF_LOW_MED_HIGH then - supportedFanModes = { "off", "low", "medium", "high" } - elseif ib.data.value == clusters.FanControl.attributes.FanModeSequence.OFF_LOW_HIGH then - supportedFanModes = { "off", "low", "high" } - elseif ib.data.value == clusters.FanControl.attributes.FanModeSequence.OFF_LOW_MED_HIGH_AUTO then - supportedFanModes = { "off", "low", "medium", "high", "auto" } - elseif ib.data.value == clusters.FanControl.attributes.FanModeSequence.OFF_LOW_HIGH_AUTO then - supportedFanModes = { "off", "low", "high", "auto" } - elseif ib.data.value == clusters.FanControl.attributes.FanModeSequence.OFF_HIGH_AUTO then - supportedFanModes = { "off", "high", "auto" } - else - supportedFanModes = { "off", "high" } - end - - if device:supports_capability_by_id(capabilities.airPurifierFanMode.ID) then - supported_fan_modes_attribute = capabilities.airPurifierFanMode.supportedAirPurifierFanModes - elseif device:supports_capability_by_id(capabilities.airConditionerFanMode.ID) then - supported_fan_modes_attribute = capabilities.airConditionerFanMode.supportedAcFanModes - elseif device:supports_capability_by_id(capabilities.thermostatFanMode.ID) then - supported_fan_modes_attribute = capabilities.thermostatFanMode.supportedThermostatFanModes - -- Our thermostat fan mode control is not granular enough to handle all of the supported modes - if ib.data.value >= clusters.FanControl.attributes.FanModeSequence.OFF_LOW_MED_HIGH_AUTO and - ib.data.value <= clusters.FanControl.attributes.FanModeSequence.OFF_ON_AUTO then - supportedFanModes = { "auto", "on" } - else - supportedFanModes = { "on" } - end - else - supported_fan_modes_attribute = capabilities.fanMode.supportedFanModes - end - - local event = supported_fan_modes_attribute(supportedFanModes, {visibility = {displayed = false}}) - device:emit_event_for_endpoint(ib.endpoint_id, event) -end - -local function fan_speed_percent_attr_handler(driver, device, ib, response) - local speed = 0 - if ib.data.value ~= nil then - speed = utils.clamp_value(ib.data.value, MIN_ALLOWED_PERCENT_VALUE, MAX_ALLOWED_PERCENT_VALUE) - end - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.fanSpeedPercent.percent(speed)) -end - -local function wind_support_handler(driver, device, ib, response) - local supported_wind_modes = {capabilities.windMode.windMode.noWind.NAME} - for mode, wind_mode in pairs(WIND_MODE_MAP) do - if ((ib.data.value >> mode) & 1) > 0 then - table.insert(supported_wind_modes, wind_mode.NAME) - end - end - local event = capabilities.windMode.supportedWindModes(supported_wind_modes, {visibility = {displayed = false}}) - device:emit_event_for_endpoint(ib.endpoint_id, event) -end - -local function wind_setting_handler(driver, device, ib, response) - for index, wind_mode in pairs(WIND_MODE_MAP) do - if ((ib.data.value >> index) & 1) > 0 then - device:emit_event_for_endpoint(ib.endpoint_id, wind_mode()) - return - end - end - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.windMode.windMode.noWind()) -end - -local function rock_support_handler(driver, device, ib, response) - local supported_rock_modes = {capabilities.fanOscillationMode.fanOscillationMode.off.NAME} - for mode, rock_mode in pairs(ROCK_MODE_MAP) do - if ((ib.data.value >> mode) & 1) > 0 then - table.insert(supported_rock_modes, rock_mode.NAME) - end - end - local event = capabilities.fanOscillationMode.supportedFanOscillationModes(supported_rock_modes, {visibility = {displayed = false}}) - device:emit_event_for_endpoint(ib.endpoint_id, event) -end - -local function rock_setting_handler(driver, device, ib, response) - for index, rock_mode in pairs(ROCK_MODE_MAP) do - if ((ib.data.value >> index) & 1) > 0 then - device:emit_event_for_endpoint(ib.endpoint_id, rock_mode()) - return - end - end - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.fanOscillationMode.fanOscillationMode.off()) -end - -local function hepa_filter_condition_handler(driver, device, ib, response) - local component = device.profile.components["hepaFilter"] - local condition = ib.data.value - device:emit_component_event(component, capabilities.filterState.filterLifeRemaining(condition)) -end - -local function hepa_filter_change_indication_handler(driver, device, ib, response) - local component = device.profile.components["hepaFilter"] - if ib.data.value == clusters.HepaFilterMonitoring.attributes.ChangeIndication.OK then - device:emit_component_event(component, capabilities.filterStatus.filterStatus.normal()) - elseif ib.data.value == clusters.HepaFilterMonitoring.attributes.ChangeIndication.WARNING then - device:emit_component_event(component, capabilities.filterStatus.filterStatus.normal()) - elseif ib.data.value == clusters.HepaFilterMonitoring.attributes.ChangeIndication.CRITICAL then - device:emit_component_event(component, capabilities.filterStatus.filterStatus.replace()) - end -end - -local function activated_carbon_filter_condition_handler(driver, device, ib, response) - local component = device.profile.components["activatedCarbonFilter"] - local condition = ib.data.value - device:emit_component_event(component, capabilities.filterState.filterLifeRemaining(condition)) -end - -local function activated_carbon_filter_change_indication_handler(driver, device, ib, response) - local component = device.profile.components["activatedCarbonFilter"] - if ib.data.value == clusters.ActivatedCarbonFilterMonitoring.attributes.ChangeIndication.OK then - device:emit_component_event(component, capabilities.filterStatus.filterStatus.normal()) - elseif ib.data.value == clusters.ActivatedCarbonFilterMonitoring.attributes.ChangeIndication.WARNING then - device:emit_component_event(component, capabilities.filterStatus.filterStatus.normal()) - elseif ib.data.value == clusters.ActivatedCarbonFilterMonitoring.attributes.ChangeIndication.CRITICAL then - device:emit_component_event(component, capabilities.filterStatus.filterStatus.replace()) - end -end - -local function handle_switch_on(driver, device, cmd) - local endpoint_id = component_to_endpoint(device, cmd.component, clusters.OnOff.ID) - local req = clusters.OnOff.server.commands.On(device, endpoint_id) - device:send(req) -end - -local function handle_switch_off(driver, device, cmd) - local endpoint_id = component_to_endpoint(device, cmd.component, clusters.OnOff.ID) - local req = clusters.OnOff.server.commands.Off(device, endpoint_id) - device:send(req) -end - -local function set_thermostat_mode(driver, device, cmd) - local mode_id = nil - for value, mode in pairs(THERMOSTAT_MODE_MAP) do - if mode.NAME == cmd.args.mode then - mode_id = value - break - end - end - if mode_id then - device:send(clusters.Thermostat.attributes.SystemMode:write(device, component_to_endpoint(device, cmd.component, clusters.Thermostat.ID), mode_id)) - end -end - -local thermostat_mode_setter = function(mode_name) - return function(driver, device, cmd) - return set_thermostat_mode(driver, device, {component = cmd.component, args = {mode = mode_name}}) - end -end - -local function set_setpoint(setpoint) - return function(driver, device, cmd) - local endpoint_id = component_to_endpoint(device, cmd.component, clusters.Thermostat.ID) - local MAX_TEMP_IN_C = THERMOSTAT_MAX_TEMP_IN_C - local MIN_TEMP_IN_C = THERMOSTAT_MIN_TEMP_IN_C - local is_water_heater_device = get_device_type(device) == WATER_HEATER_DEVICE_TYPE_ID - if is_water_heater_device then - MAX_TEMP_IN_C = WATER_HEATER_MAX_TEMP_IN_C - MIN_TEMP_IN_C = WATER_HEATER_MIN_TEMP_IN_C - end - local value = cmd.args.setpoint - if version.rpc <= 5 and value > MAX_TEMP_IN_C then - value = utils.f_to_c(value) - end - - -- Gather cached setpoint values when considering setpoint limits - -- Note: cached values should always exist, but defaults are chosen just in case to prevent - -- nil operation errors, and deadband logic from triggering. - local cached_cooling_val, cooling_setpoint = device:get_latest_state( - cmd.component, capabilities.thermostatCoolingSetpoint.ID, - capabilities.thermostatCoolingSetpoint.coolingSetpoint.NAME, - MAX_TEMP_IN_C, { value = MAX_TEMP_IN_C, unit = "C" } - ) - if cooling_setpoint and cooling_setpoint.unit == "F" then - cached_cooling_val = utils.f_to_c(cached_cooling_val) - end - local cached_heating_val, heating_setpoint = device:get_latest_state( - cmd.component, capabilities.thermostatHeatingSetpoint.ID, - capabilities.thermostatHeatingSetpoint.heatingSetpoint.NAME, - MIN_TEMP_IN_C, { value = MIN_TEMP_IN_C, unit = "C" } - ) - if heating_setpoint and heating_setpoint.unit == "F" then - cached_heating_val = utils.f_to_c(cached_heating_val) - end - local is_auto_capable = #device:get_endpoints( - clusters.Thermostat.ID, - {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.AUTOMODE} - ) > 0 - - --Check setpoint limits for the device - local setpoint_type = string.match(setpoint.NAME, "Heat") or "Cool" - local deadband = device:get_field(setpoint_limit_device_field.MIN_DEADBAND) or 2.5 --spec default - if setpoint_type == "Heat" then - local min = device:get_field(setpoint_limit_device_field.MIN_HEAT) or MIN_TEMP_IN_C - local max = device:get_field(setpoint_limit_device_field.MAX_HEAT) or MAX_TEMP_IN_C - if value < min or value > max then - log.warn(string.format( - "Invalid setpoint (%s) outside the min (%s) and the max (%s)", - value, min, max - )) - device:emit_event_for_endpoint(endpoint_id, capabilities.thermostatHeatingSetpoint.heatingSetpoint(heating_setpoint, {state_change = true})) - return - end - if is_auto_capable and value > (cached_cooling_val - deadband) then - log.warn(string.format( - "Invalid setpoint (%s) is greater than the cooling setpoint (%s) with the deadband (%s)", - value, cooling_setpoint, deadband - )) - device:emit_event_for_endpoint(endpoint_id, capabilities.thermostatHeatingSetpoint.heatingSetpoint(heating_setpoint, {state_change = true})) - return - end - else - local min = device:get_field(setpoint_limit_device_field.MIN_COOL) or MIN_TEMP_IN_C - local max = device:get_field(setpoint_limit_device_field.MAX_COOL) or MAX_TEMP_IN_C - if value < min or value > max then - log.warn(string.format( - "Invalid setpoint (%s) outside the min (%s) and the max (%s)", - value, min, max - )) - device:emit_event_for_endpoint(endpoint_id, capabilities.thermostatCoolingSetpoint.coolingSetpoint(cooling_setpoint, {state_change = true})) - return - end - if is_auto_capable and value < (cached_heating_val + deadband) then - log.warn(string.format( - "Invalid setpoint (%s) is less than the heating setpoint (%s) with the deadband (%s)", - value, heating_setpoint, deadband - )) - device:emit_event_for_endpoint(endpoint_id, capabilities.thermostatCoolingSetpoint.coolingSetpoint(cooling_setpoint, {state_change = true})) - return - end - end - device:send(setpoint:write(device, component_to_endpoint(device, cmd.component, clusters.Thermostat.ID), utils.round(value * 100.0))) - end -end - -local heating_setpoint_limit_handler_factory = function(minOrMax) - return function(driver, device, ib, response) - if ib.data.value == nil then - return - end - local MAX_TEMP_IN_C = THERMOSTAT_MAX_TEMP_IN_C - local MIN_TEMP_IN_C = THERMOSTAT_MIN_TEMP_IN_C - local is_water_heater_device = (get_device_type(device) == WATER_HEATER_DEVICE_TYPE_ID) - if is_water_heater_device then - MAX_TEMP_IN_C = WATER_HEATER_MAX_TEMP_IN_C - MIN_TEMP_IN_C = WATER_HEATER_MIN_TEMP_IN_C - end - local val = ib.data.value / 100.0 - val = utils.clamp_value(val, MIN_TEMP_IN_C, MAX_TEMP_IN_C) - device:set_field(minOrMax, val) - local min = device:get_field(setpoint_limit_device_field.MIN_HEAT) - local max = device:get_field(setpoint_limit_device_field.MAX_HEAT) - if min ~= nil and max ~= nil then - if min < max then - -- Only emit the capability for RPC version >= 5 (unit conversion for - -- heating setpoint range capability is only supported for RPC >= 5) - if version.rpc >= 5 then - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.thermostatHeatingSetpoint.heatingSetpointRange({ value = { minimum = min, maximum = max, step = 0.1 }, unit = "C" })) - end - else - device.log.warn_with({hub_logs = true}, string.format("Device reported a min heating setpoint %d that is not lower than the reported max %d", min, max)) - end - end - end -end - -local cooling_setpoint_limit_handler_factory = function(minOrMax) - return function(driver, device, ib, response) - if ib.data.value == nil then - return - end - local val = ib.data.value / 100.0 - val = utils.clamp_value(val, THERMOSTAT_MIN_TEMP_IN_C, THERMOSTAT_MAX_TEMP_IN_C) - device:set_field(minOrMax, val) - local min = device:get_field(setpoint_limit_device_field.MIN_COOL) - local max = device:get_field(setpoint_limit_device_field.MAX_COOL) - if min ~= nil and max ~= nil then - if min < max then - -- Only emit the capability for RPC version >= 5 (unit conversion for - -- cooling setpoint range capability is only supported for RPC >= 5) - if version.rpc >= 5 then - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.thermostatCoolingSetpoint.coolingSetpointRange({ value = { minimum = min, maximum = max, step = 0.1 }, unit = "C" })) - end - else - device.log.warn_with({hub_logs = true}, string.format("Device reported a min cooling setpoint %d that is not lower than the reported max %d", min, max)) - end - end - end -end - -local function set_fan_mode(device, cmd, fan_mode_capability) - local command_argument = cmd.args.fanMode - if fan_mode_capability == capabilities.airPurifierFanMode then - command_argument = cmd.args.airPurifierFanMode - elseif fan_mode_capability == capabilities.thermostatFanMode then - command_argument = cmd.args.mode - end - local fan_mode_id - if command_argument == "off" then - fan_mode_id = clusters.FanControl.attributes.FanMode.OFF - elseif command_argument == "on" then - fan_mode_id = clusters.FanControl.attributes.FanMode.ON - elseif command_argument == "auto" then - fan_mode_id = clusters.FanControl.attributes.FanMode.AUTO - elseif command_argument == "high" then - fan_mode_id = clusters.FanControl.attributes.FanMode.HIGH - elseif command_argument == "medium" then - fan_mode_id = clusters.FanControl.attributes.FanMode.MEDIUM - elseif tbl_contains({ "low", "sleep", "quiet", "windFree" }, command_argument) then - fan_mode_id = clusters.FanControl.attributes.FanMode.LOW - else - device.log.warn(string.format("Invalid Fan Mode (%s) received from capability command", command_argument)) - return - end - device:send(clusters.FanControl.attributes.FanMode:write(device, component_to_endpoint(device, cmd.component, clusters.FanControl.ID), fan_mode_id)) -end - -local set_fan_mode_factory = function(fan_mode_capability) - return function(driver, device, cmd) - set_fan_mode(device, cmd, fan_mode_capability) - end -end +local log = require "log" +local version = require "version" +local MatterDriver = require "st.matter.driver" +local capabilities = require "st.capabilities" +local clusters = require "st.matter.clusters" +local im = require "st.matter.interaction_model" +local attribute_handlers = require "thermostat_handlers.attribute_handlers" +local capability_handlers = require "thermostat_handlers.capability_handlers" +local fields = require "thermostat_utils.fields" +local thermostat_utils = require "thermostat_utils.utils" +local embedded_cluster_utils = require "thermostat_utils.embedded_cluster_utils" -local function thermostat_fan_mode_setter(mode_name) - return function(driver, device, cmd) - set_fan_mode(device, {component = cmd.component, args = {mode = mode_name}}, capabilities.thermostatFanMode) - end +if version.api < 10 then + clusters.HepaFilterMonitoring = require "embedded_clusters.HepaFilterMonitoring" + clusters.ActivatedCarbonFilterMonitoring = require "embedded_clusters.ActivatedCarbonFilterMonitoring" + clusters.AirQuality = require "embedded_clusters.AirQuality" + clusters.CarbonMonoxideConcentrationMeasurement = require "embedded_clusters.CarbonMonoxideConcentrationMeasurement" + clusters.CarbonDioxideConcentrationMeasurement = require "embedded_clusters.CarbonDioxideConcentrationMeasurement" + clusters.FormaldehydeConcentrationMeasurement = require "embedded_clusters.FormaldehydeConcentrationMeasurement" + clusters.NitrogenDioxideConcentrationMeasurement = require "embedded_clusters.NitrogenDioxideConcentrationMeasurement" + clusters.OzoneConcentrationMeasurement = require "embedded_clusters.OzoneConcentrationMeasurement" + clusters.Pm1ConcentrationMeasurement = require "embedded_clusters.Pm1ConcentrationMeasurement" + clusters.Pm10ConcentrationMeasurement = require "embedded_clusters.Pm10ConcentrationMeasurement" + clusters.Pm25ConcentrationMeasurement = require "embedded_clusters.Pm25ConcentrationMeasurement" + clusters.RadonConcentrationMeasurement = require "embedded_clusters.RadonConcentrationMeasurement" + clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement = require "embedded_clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement" end -local function set_fan_speed_percent(driver, device, cmd) - local speed = math.floor(cmd.args.percent) - device:send(clusters.FanControl.attributes.PercentSetting:write(device, component_to_endpoint(device, cmd.component, clusters.FanControl.ID), speed)) +if version.api < 11 then + clusters.ElectricalEnergyMeasurement = require "embedded_clusters.ElectricalEnergyMeasurement" + clusters.ElectricalPowerMeasurement = require "embedded_clusters.ElectricalPowerMeasurement" end -local function set_wind_mode(driver, device, cmd) - local wind_mode = 0 - if cmd.args.windMode == capabilities.windMode.windMode.sleepWind.NAME then - wind_mode = clusters.FanControl.types.WindSupportMask.SLEEP_WIND - elseif cmd.args.windMode == capabilities.windMode.windMode.naturalWind.NAME then - wind_mode = clusters.FanControl.types.WindSupportMask.NATURAL_WIND - end - device:send(clusters.FanControl.attributes.WindSetting:write(device, component_to_endpoint(device, cmd.component, clusters.FanControl.ID), wind_mode)) +if version.api < 13 then + clusters.WaterHeaterMode = require "embedded_clusters.WaterHeaterMode" end -local function set_rock_mode(driver, device, cmd) - local rock_mode = 0 - if cmd.args.fanOscillationMode == capabilities.fanOscillationMode.fanOscillationMode.horizontal.NAME then - rock_mode = clusters.FanControl.types.RockSupportMask.ROCK_LEFT_RIGHT - elseif cmd.args.fanOscillationMode == capabilities.fanOscillationMode.fanOscillationMode.vertical.NAME then - rock_mode = clusters.FanControl.types.RockSupportMask.ROCK_UP_DOWN - elseif cmd.args.fanOscillationMode == capabilities.fanOscillationMode.fanOscillationMode.swing.NAME then - rock_mode = clusters.FanControl.types.RockSupportMask.ROCK_ROUND - end - device:send(clusters.FanControl.attributes.RockSetting:write(device, component_to_endpoint(device, cmd.component, clusters.FanControl.ID), rock_mode)) -end +local ThermostatLifecycleHandlers = {} -local function set_water_heater_mode(driver, device, cmd) - device.log.info(string.format("set_water_heater_mode mode: %s", cmd.args.mode)) - local endpoint_id = component_to_endpoint(device, cmd.component, clusters.Thermostat.ID) - local supportedWaterHeaterModesWithIdx = device:get_field(SUPPORTED_WATER_HEATER_MODES_WITH_IDX) or {} - for i, mode in ipairs(supportedWaterHeaterModesWithIdx) do - if cmd.args.mode == mode[2] then - device:send(clusters.WaterHeaterMode.commands.ChangeToMode(device, endpoint_id, mode[1])) - return - end - end -end +function ThermostatLifecycleHandlers.device_added(driver, device) + local req = im.InteractionRequest(im.InteractionRequest.RequestType.READ, {}) + req:merge(clusters.Thermostat.attributes.ControlSequenceOfOperation:read(device)) + req:merge(clusters.FanControl.attributes.FanModeSequence:read(device)) + req:merge(clusters.FanControl.attributes.WindSupport:read(device)) + req:merge(clusters.FanControl.attributes.RockSupport:read(device)) -local function reset_filter_state(driver, device, cmd) - local endpoint_id = device:component_to_endpoint(cmd.component) - if cmd.component == "hepaFilter" then - device:send(clusters.HepaFilterMonitoring.server.commands.ResetCondition(device, endpoint_id)) + local thermostat_eps = device:get_endpoints(clusters.Thermostat.ID) + if #thermostat_eps > 0 then + req:merge(clusters.Thermostat.attributes.AttributeList:read(device)) else - device:send(clusters.ActivatedCarbonFilterMonitoring.server.commands.ResetCondition(device, endpoint_id)) + device:set_field(fields.profiling_data.THERMOSTAT_RUNNING_STATE_SUPPORT, false) end -end - -local function battery_percent_remaining_attr_handler(driver, device, ib, response) - if ib.data.value then - device:emit_event(capabilities.battery.battery(math.floor(ib.data.value / 2.0 + 0.5))) + local battery_feature_eps = device:get_endpoints(clusters.PowerSource.ID, {feature_bitmap = clusters.PowerSource.types.PowerSourceFeature.BATTERY}) + if #battery_feature_eps > 0 then + req:merge(clusters.PowerSource.attributes.AttributeList:read(device)) + else + device:set_field(fields.profiling_data.BATTERY_SUPPORT, fields.battery_support.NO_BATTERY) end -end - -local function active_power_handler(driver, device, ib, response) - if ib.data.value then - local watt_value = ib.data.value / 1000 - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.powerMeter.power({ value = watt_value, unit = "W" })) - if type(device.register_native_capability_attr_handler) == "function" then - device:register_native_capability_attr_handler("powerMeter","power") - end + device:send(req) + local heat_pump_eps = thermostat_utils.get_endpoints_by_device_type(device, fields.HEAT_PUMP_DEVICE_TYPE_ID) or {} + if #heat_pump_eps > 0 then + local thermostat_eps = thermostat_utils.get_endpoints_by_device_type(device, fields.THERMOSTAT_DEVICE_TYPE_ID) or {} + local component_to_endpoint_map = { + ["thermostatOne"] = thermostat_eps[1], + ["thermostatTwo"] = thermostat_eps[2], + } + device:set_field(fields.COMPONENT_TO_ENDPOINT_MAP, component_to_endpoint_map, {persist = true}) end end -local function periodic_energy_imported_handler(driver, device, ib, response) - if ib.data then - if version.api < 11 then - clusters.ElectricalEnergyMeasurement.server.attributes.PeriodicEnergyImported:augment_type(ib.data) - end - local endpoint_id = string.format(ib.endpoint_id) - local energy_imported_Wh = utils.round(ib.data.elements.energy.value / 1000) --convert mWh to Wh - local cumulative_energy_imported = device:get_field(TOTAL_CUMULATIVE_ENERGY_IMPORTED_MAP) or {} - cumulative_energy_imported[endpoint_id] = cumulative_energy_imported[endpoint_id] or 0 - cumulative_energy_imported[endpoint_id] = cumulative_energy_imported[endpoint_id] + energy_imported_Wh - device:set_field(TOTAL_CUMULATIVE_ENERGY_IMPORTED_MAP, cumulative_energy_imported, { persist = true }) - local total_cumulative_energy_imported = get_total_cumulative_energy_imported(device) - device:emit_component_event(device.profile.components["main"], ib.endpoint_id, capabilities.energyMeter.energy({value = total_cumulative_energy_imported, unit = "Wh"})) - report_power_consumption_to_st_energy(device, total_cumulative_energy_imported) - end +function ThermostatLifecycleHandlers.do_configure(driver, device) + local device_cfg = require "thermostat_utils.device_configuration" + device_cfg.match_profile(device) end -local function cumulative_energy_imported_handler(driver, device, ib, response) - if ib.data then - if version.api < 11 then - clusters.ElectricalEnergyMeasurement.server.attributes.CumulativeEnergyImported:augment_type(ib.data) - end - local endpoint_id = string.format(ib.endpoint_id) - local cumulative_energy_imported = device:get_field(TOTAL_CUMULATIVE_ENERGY_IMPORTED_MAP) or {} - local cumulative_energy_imported_Wh = utils.round( ib.data.elements.energy.value / 1000) -- convert mWh to Wh - cumulative_energy_imported[endpoint_id] = cumulative_energy_imported_Wh - device:set_field(TOTAL_CUMULATIVE_ENERGY_IMPORTED_MAP, cumulative_energy_imported, { persist = true }) - local total_cumulative_energy_imported = get_total_cumulative_energy_imported(device) - device:emit_component_event(device.profile.components["main"], capabilities.energyMeter.energy({ value = total_cumulative_energy_imported, unit = "Wh" })) - report_power_consumption_to_st_energy(device, total_cumulative_energy_imported) - end +function ThermostatLifecycleHandlers.driver_switched(driver, device) + local device_cfg = require "thermostat_utils.device_configuration" + device_cfg.match_profile(device) end -local function energy_report_handler_factory(is_cumulative_report) - return function(driver, device, ib, response) - if is_cumulative_report then - cumulative_energy_imported_handler(driver, device, ib, response) - elseif device:get_field(CUMULATIVE_REPORTS_NOT_SUPPORTED) then - periodic_energy_imported_handler(driver, device, ib, response) - end +function ThermostatLifecycleHandlers.device_init(driver, device) + if device:get_field(fields.SUPPORTED_COMPONENT_CAPABILITIES) and (version.api < 15 or version.rpc < 9) then + -- assume that device is using a modular profile on 0.57 FW, override supports_capability_by_id + -- library function to utilize optional capabilities + device:extend_device("supports_capability_by_id", thermostat_utils.supports_capability_by_id_modular) end -end - -local function water_heater_supported_modes_attr_handler(driver, device, ib, response) - local supportWaterHeaterModes = {} - local supportWaterHeaterModesWithIdx = {} - for _, mode in ipairs(ib.data.elements) do - if version.api < 13 then - clusters.WaterHeaterMode.types.ModeOptionStruct:augment_type(mode) + device:subscribe() + device:set_component_to_endpoint_fn(thermostat_utils.component_to_endpoint) + device:set_endpoint_to_component_fn(thermostat_utils.endpoint_to_component) + if not device:get_field(fields.setpoint_limit_device_field.MIN_SETPOINT_DEADBAND_CHECKED) then + local auto_eps = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.AUTOMODE}) + --Query min setpoint deadband if needed + if #auto_eps ~= 0 and device:get_field(fields.setpoint_limit_device_field.MIN_DEADBAND) == nil then + device:send(clusters.Thermostat.attributes.MinSetpointDeadBand:read()) end - table.insert(supportWaterHeaterModes, mode.elements.label.value) - table.insert(supportWaterHeaterModesWithIdx, {mode.elements.mode.value, mode.elements.label.value}) end - device:set_field(SUPPORTED_WATER_HEATER_MODES_WITH_IDX, supportWaterHeaterModesWithIdx, { persist = true }) - local event = capabilities.mode.supportedModes(supportWaterHeaterModes, { visibility = { displayed = false } }) - device:emit_event_for_endpoint(ib.endpoint_id, event) - event = capabilities.mode.supportedArguments(supportWaterHeaterModes, { visibility = { displayed = false } }) - device:emit_event_for_endpoint(ib.endpoint_id, event) -end -local function water_heater_mode_handler(driver, device, ib, response) - device.log.info(string.format("water_heater_mode_handler mode: %s", ib.data.value)) - local supportWaterHeaterModesWithIdx = device:get_field(SUPPORTED_WATER_HEATER_MODES_WITH_IDX) or {} - local currentMode = ib.data.value - for i, mode in ipairs(supportWaterHeaterModesWithIdx) do - if mode[1] == currentMode then - device:emit_event_for_endpoint(ib.endpoint_id, capabilities.mode.mode(mode[2])) - break - end + -- device energy reporting must be handled cumulatively, periodically, or by both simulatanously. + -- To ensure a single source of truth, we only handle a device's periodic reporting if cumulative reporting is not supported. + local electrical_energy_measurement_eps = embedded_cluster_utils.get_endpoints(device, clusters.ElectricalEnergyMeasurement.ID) + if #electrical_energy_measurement_eps > 0 then + local cumulative_energy_eps = embedded_cluster_utils.get_endpoints( + device, + clusters.ElectricalEnergyMeasurement.ID, + {feature_bitmap = clusters.ElectricalEnergyMeasurement.types.Feature.CUMULATIVE_ENERGY} + ) + if #cumulative_energy_eps == 0 then device:set_field(fields.CUMULATIVE_REPORTS_NOT_SUPPORTED, true, {persist = false}) end end end -local function battery_charge_level_attr_handler(driver, device, ib, response) - if ib.data.value == clusters.PowerSource.types.BatChargeLevelEnum.OK then - device:emit_event(capabilities.batteryLevel.battery.normal()) - elseif ib.data.value == clusters.PowerSource.types.BatChargeLevelEnum.WARNING then - device:emit_event(capabilities.batteryLevel.battery.warning()) - elseif ib.data.value == clusters.PowerSource.types.BatChargeLevelEnum.CRITICAL then - device:emit_event(capabilities.batteryLevel.battery.critical()) +function ThermostatLifecycleHandlers.info_changed(driver, device, event, args) + if device:get_field(fields.SUPPORTED_COMPONENT_CAPABILITIES) then + -- This indicates the device should be using a modular profile, so + -- re-up subscription with new capabilities using the modular supports_capability override + device:extend_device("supports_capability_by_id", thermostat_utils.supports_capability_by_id_modular) end -end -local function power_source_attribute_list_handler(driver, device, ib, response) - for _, attr in ipairs(ib.data.elements) do - -- mark if the device if BatPercentRemaining (Attribute ID 0x0C) or - -- BatChargeLevel (Attribute ID 0x0E) is present and try profiling. - if attr.value == 0x0C then - device:set_field(profiling_data.BATTERY_SUPPORT, battery_support.BATTERY_PERCENTAGE) - match_profile(driver, device) - return - elseif attr.value == 0x0E then - device:set_field(profiling_data.BATTERY_SUPPORT, battery_support.BATTERY_LEVEL) - match_profile(driver, device) - return - end + if device.profile.id ~= args.old_st_store.profile.id then + device:subscribe() end end -local function thermostat_attribute_list_handler(driver, device, ib, response) - for _, attr in ipairs(ib.data.elements) do - -- mark whether the optional attribute ThermostatRunningState (0x029) is present and try profiling - if attr.value == 0x029 then - device:set_field(profiling_data.THERMOSTAT_RUNNING_STATE_SUPPORT, true) - match_profile(driver, device) - return - end - end - device:set_field(profiling_data.THERMOSTAT_RUNNING_STATE_SUPPORT, false) - match_profile(driver, device) +function ThermostatLifecycleHandlers.device_removed(driver, device) + device.log.info("device removed") end local matter_driver_template = { lifecycle_handlers = { - init = device_init, - added = device_added, - doConfigure = do_configure, - infoChanged = info_changed, - removed = device_removed, - driverSwitched = driver_switched + added = ThermostatLifecycleHandlers.device_added, + doConfigure = ThermostatLifecycleHandlers.do_configure, + driverSwitched = ThermostatLifecycleHandlers.driver_switched, + infoChanged = ThermostatLifecycleHandlers.info_changed, + init = ThermostatLifecycleHandlers.device_init, + removed = ThermostatLifecycleHandlers.device_removed, }, matter_handlers = { attr = { - [clusters.OnOff.ID] = { - [clusters.OnOff.attributes.OnOff.ID] = on_off_attr_handler, + [clusters.ActivatedCarbonFilterMonitoring.ID] = { + [clusters.ActivatedCarbonFilterMonitoring.attributes.ChangeIndication.ID] = attribute_handlers.activated_carbon_filter_change_indication_handler, + [clusters.ActivatedCarbonFilterMonitoring.attributes.Condition.ID] = attribute_handlers.activated_carbon_filter_condition_handler, }, - [clusters.Thermostat.ID] = { - [clusters.Thermostat.attributes.LocalTemperature.ID] = temp_event_handler(capabilities.temperatureMeasurement.temperature), - [clusters.Thermostat.attributes.OccupiedCoolingSetpoint.ID] = temp_event_handler(capabilities.thermostatCoolingSetpoint.coolingSetpoint), - [clusters.Thermostat.attributes.OccupiedHeatingSetpoint.ID] = temp_event_handler(capabilities.thermostatHeatingSetpoint.heatingSetpoint), - [clusters.Thermostat.attributes.SystemMode.ID] = system_mode_handler, - [clusters.Thermostat.attributes.ThermostatRunningState.ID] = running_state_handler, - [clusters.Thermostat.attributes.ControlSequenceOfOperation.ID] = sequence_of_operation_handler, - [clusters.Thermostat.attributes.AbsMinHeatSetpointLimit.ID] = heating_setpoint_limit_handler_factory(setpoint_limit_device_field.MIN_HEAT), - [clusters.Thermostat.attributes.AbsMaxHeatSetpointLimit.ID] = heating_setpoint_limit_handler_factory(setpoint_limit_device_field.MAX_HEAT), - [clusters.Thermostat.attributes.AbsMinCoolSetpointLimit.ID] = cooling_setpoint_limit_handler_factory(setpoint_limit_device_field.MIN_COOL), - [clusters.Thermostat.attributes.AbsMaxCoolSetpointLimit.ID] = cooling_setpoint_limit_handler_factory(setpoint_limit_device_field.MAX_COOL), - [clusters.Thermostat.attributes.MinSetpointDeadBand.ID] = min_deadband_limit_handler, - [clusters.Thermostat.attributes.AttributeList.ID] = thermostat_attribute_list_handler, + [clusters.AirQuality.ID] = { + [clusters.AirQuality.attributes.AirQuality.ID] = attribute_handlers.air_quality_handler, + }, + [clusters.ElectricalEnergyMeasurement.ID] = { + [clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyImported.ID] = attribute_handlers.energy_imported_factory(true), + [clusters.ElectricalEnergyMeasurement.attributes.PeriodicEnergyImported.ID] = attribute_handlers.energy_imported_factory(false), + }, + [clusters.ElectricalPowerMeasurement.ID] = { + [clusters.ElectricalPowerMeasurement.attributes.ActivePower.ID] = attribute_handlers.active_power_handler }, [clusters.FanControl.ID] = { - [clusters.FanControl.attributes.FanModeSequence.ID] = fan_mode_sequence_handler, - [clusters.FanControl.attributes.FanMode.ID] = fan_mode_handler, - [clusters.FanControl.attributes.PercentCurrent.ID] = fan_speed_percent_attr_handler, - [clusters.FanControl.attributes.WindSupport.ID] = wind_support_handler, - [clusters.FanControl.attributes.WindSetting.ID] = wind_setting_handler, - [clusters.FanControl.attributes.RockSupport.ID] = rock_support_handler, - [clusters.FanControl.attributes.RockSetting.ID] = rock_setting_handler, + [clusters.FanControl.attributes.FanMode.ID] = attribute_handlers.fan_mode_handler, + [clusters.FanControl.attributes.FanModeSequence.ID] = attribute_handlers.fan_mode_sequence_handler, + [clusters.FanControl.attributes.PercentCurrent.ID] = attribute_handlers.percent_current_handler, + [clusters.FanControl.attributes.RockSetting.ID] = attribute_handlers.rock_setting_handler, + [clusters.FanControl.attributes.RockSupport.ID] = attribute_handlers.rock_support_handler, + [clusters.FanControl.attributes.WindSetting.ID] = attribute_handlers.wind_setting_handler, + [clusters.FanControl.attributes.WindSupport.ID] = attribute_handlers.wind_support_handler, }, - [clusters.TemperatureMeasurement.ID] = { - [clusters.TemperatureMeasurement.attributes.MeasuredValue.ID] = temp_event_handler(capabilities.temperatureMeasurement.temperature), - [clusters.TemperatureMeasurement.attributes.MinMeasuredValue.ID] = temp_attr_handler_factory(setpoint_limit_device_field.MIN_TEMP), - [clusters.TemperatureMeasurement.attributes.MaxMeasuredValue.ID] = temp_attr_handler_factory(setpoint_limit_device_field.MAX_TEMP), + [clusters.HepaFilterMonitoring.ID] = { + [clusters.HepaFilterMonitoring.attributes.ChangeIndication.ID] = attribute_handlers.hepa_filter_change_indication_handler, + [clusters.HepaFilterMonitoring.attributes.Condition.ID] = attribute_handlers.hepa_filter_condition_handler, }, - [clusters.RelativeHumidityMeasurement.ID] = { - [clusters.RelativeHumidityMeasurement.attributes.MeasuredValue.ID] = humidity_attr_handler + [clusters.OnOff.ID] = { + [clusters.OnOff.attributes.OnOff.ID] = attribute_handlers.on_off_handler, }, [clusters.PowerSource.ID] = { - [clusters.PowerSource.attributes.AttributeList.ID] = power_source_attribute_list_handler, - [clusters.PowerSource.attributes.BatChargeLevel.ID] = battery_charge_level_attr_handler, - [clusters.PowerSource.attributes.BatPercentRemaining.ID] = battery_percent_remaining_attr_handler, + [clusters.PowerSource.attributes.AttributeList.ID] = attribute_handlers.power_source_attribute_list_handler, + [clusters.PowerSource.attributes.BatChargeLevel.ID] = attribute_handlers.bat_charge_level_handler, + [clusters.PowerSource.attributes.BatPercentRemaining.ID] = attribute_handlers.bat_percent_remaining_handler, }, - [clusters.HepaFilterMonitoring.ID] = { - [clusters.HepaFilterMonitoring.attributes.Condition.ID] = hepa_filter_condition_handler, - [clusters.HepaFilterMonitoring.attributes.ChangeIndication.ID] = hepa_filter_change_indication_handler + [clusters.RelativeHumidityMeasurement.ID] = { + [clusters.RelativeHumidityMeasurement.attributes.MeasuredValue.ID] = attribute_handlers.relative_humidity_measured_value_handler }, - [clusters.ActivatedCarbonFilterMonitoring.ID] = { - [clusters.ActivatedCarbonFilterMonitoring.attributes.Condition.ID] = activated_carbon_filter_condition_handler, - [clusters.ActivatedCarbonFilterMonitoring.attributes.ChangeIndication.ID] = activated_carbon_filter_change_indication_handler + [clusters.TemperatureMeasurement.ID] = { + [clusters.TemperatureMeasurement.attributes.MaxMeasuredValue.ID] = attribute_handlers.temperature_measured_value_bounds_factory(fields.setpoint_limit_device_field.MAX_TEMP), + [clusters.TemperatureMeasurement.attributes.MeasuredValue.ID] = attribute_handlers.temperature_handler_factory(capabilities.temperatureMeasurement.temperature), + [clusters.TemperatureMeasurement.attributes.MinMeasuredValue.ID] = attribute_handlers.temperature_measured_value_bounds_factory(fields.setpoint_limit_device_field.MIN_TEMP), }, - [clusters.AirQuality.ID] = { - [clusters.AirQuality.attributes.AirQuality.ID] = air_quality_attr_handler, + [clusters.Thermostat.ID] = { + [clusters.Thermostat.attributes.AttributeList.ID] = attribute_handlers.thermostat_attribute_list_handler, + [clusters.Thermostat.attributes.AbsMaxCoolSetpointLimit.ID] = attribute_handlers.abs_cool_setpoint_limit_factory(fields.setpoint_limit_device_field.MAX_COOL), + [clusters.Thermostat.attributes.AbsMaxHeatSetpointLimit.ID] = attribute_handlers.abs_heat_setpoint_limit_factory(fields.setpoint_limit_device_field.MAX_HEAT), + [clusters.Thermostat.attributes.AbsMinCoolSetpointLimit.ID] = attribute_handlers.abs_cool_setpoint_limit_factory(fields.setpoint_limit_device_field.MIN_COOL), + [clusters.Thermostat.attributes.AbsMinHeatSetpointLimit.ID] = attribute_handlers.abs_heat_setpoint_limit_factory(fields.setpoint_limit_device_field.MIN_HEAT), + [clusters.Thermostat.attributes.ControlSequenceOfOperation.ID] = attribute_handlers.control_sequence_of_operation_handler, + [clusters.Thermostat.attributes.LocalTemperature.ID] = attribute_handlers.temperature_handler_factory(capabilities.temperatureMeasurement.temperature), + [clusters.Thermostat.attributes.MinSetpointDeadBand.ID] = attribute_handlers.min_setpoint_deadband_handler, + [clusters.Thermostat.attributes.OccupiedCoolingSetpoint.ID] = attribute_handlers.temperature_handler_factory(capabilities.thermostatCoolingSetpoint.coolingSetpoint), + [clusters.Thermostat.attributes.OccupiedHeatingSetpoint.ID] = attribute_handlers.temperature_handler_factory(capabilities.thermostatHeatingSetpoint.heatingSetpoint), + [clusters.Thermostat.attributes.SystemMode.ID] = attribute_handlers.system_mode_handler, + [clusters.Thermostat.attributes.ThermostatRunningState.ID] = attribute_handlers.thermostat_running_state_handler, }, - [clusters.CarbonMonoxideConcentrationMeasurement.ID] = { - [clusters.CarbonMonoxideConcentrationMeasurement.attributes.MeasuredValue.ID] = measurementHandlerFactory(capabilities.carbonMonoxideMeasurement.NAME, capabilities.carbonMonoxideMeasurement.carbonMonoxideLevel, units.PPM), - [clusters.CarbonMonoxideConcentrationMeasurement.attributes.MeasurementUnit.ID] = store_unit_factory(capabilities.carbonMonoxideMeasurement.NAME), - [clusters.CarbonMonoxideConcentrationMeasurement.attributes.LevelValue.ID] = levelHandlerFactory(capabilities.carbonMonoxideHealthConcern.carbonMonoxideHealthConcern), + [clusters.WaterHeaterMode.ID] = { + [clusters.WaterHeaterMode.attributes.CurrentMode.ID] = attribute_handlers.water_heater_current_mode_handler, + [clusters.WaterHeaterMode.attributes.SupportedModes.ID] = attribute_handlers.water_heater_supported_modes_handler }, + -- CONCENTRATION MEASUREMENT CLUSTERS -- [clusters.CarbonDioxideConcentrationMeasurement.ID] = { - [clusters.CarbonDioxideConcentrationMeasurement.attributes.MeasuredValue.ID] = measurementHandlerFactory(capabilities.carbonDioxideMeasurement.NAME, capabilities.carbonDioxideMeasurement.carbonDioxide, units.PPM), - [clusters.CarbonDioxideConcentrationMeasurement.attributes.MeasurementUnit.ID] = store_unit_factory(capabilities.carbonDioxideMeasurement.NAME), - [clusters.CarbonDioxideConcentrationMeasurement.attributes.LevelValue.ID] = levelHandlerFactory(capabilities.carbonDioxideHealthConcern.carbonDioxideHealthConcern), + [clusters.CarbonDioxideConcentrationMeasurement.attributes.LevelValue.ID] = attribute_handlers.concentration_level_value_factory(capabilities.carbonDioxideHealthConcern.carbonDioxideHealthConcern), + [clusters.CarbonDioxideConcentrationMeasurement.attributes.MeasuredValue.ID] = attribute_handlers.concentration_measured_value_factory(capabilities.carbonDioxideMeasurement.NAME, capabilities.carbonDioxideMeasurement.carbonDioxide, fields.units.PPM), + [clusters.CarbonDioxideConcentrationMeasurement.attributes.MeasurementUnit.ID] = attribute_handlers.concentration_measurement_unit_factory(capabilities.carbonDioxideMeasurement.NAME), + }, + [clusters.CarbonMonoxideConcentrationMeasurement.ID] = { + [clusters.CarbonMonoxideConcentrationMeasurement.attributes.LevelValue.ID] = attribute_handlers.concentration_level_value_factory(capabilities.carbonMonoxideHealthConcern.carbonMonoxideHealthConcern), + [clusters.CarbonMonoxideConcentrationMeasurement.attributes.MeasuredValue.ID] = attribute_handlers.concentration_measured_value_factory(capabilities.carbonMonoxideMeasurement.NAME, capabilities.carbonMonoxideMeasurement.carbonMonoxideLevel, fields.units.PPM), + [clusters.CarbonMonoxideConcentrationMeasurement.attributes.MeasurementUnit.ID] = attribute_handlers.concentration_measurement_unit_factory(capabilities.carbonMonoxideMeasurement.NAME), + }, + [clusters.FormaldehydeConcentrationMeasurement.ID] = { + [clusters.FormaldehydeConcentrationMeasurement.attributes.LevelValue.ID] = attribute_handlers.concentration_level_value_factory(capabilities.formaldehydeHealthConcern.formaldehydeHealthConcern), + [clusters.FormaldehydeConcentrationMeasurement.attributes.MeasuredValue.ID] = attribute_handlers.concentration_measured_value_factory(capabilities.formaldehydeMeasurement.NAME, capabilities.formaldehydeMeasurement.formaldehydeLevel, fields.units.PPM), + [clusters.FormaldehydeConcentrationMeasurement.attributes.MeasurementUnit.ID] = attribute_handlers.concentration_measurement_unit_factory(capabilities.formaldehydeMeasurement.NAME), }, [clusters.NitrogenDioxideConcentrationMeasurement.ID] = { - [clusters.NitrogenDioxideConcentrationMeasurement.attributes.MeasuredValue.ID] = measurementHandlerFactory(capabilities.nitrogenDioxideMeasurement.NAME, capabilities.nitrogenDioxideMeasurement.nitrogenDioxide, units.PPM), - [clusters.NitrogenDioxideConcentrationMeasurement.attributes.MeasurementUnit.ID] = store_unit_factory(capabilities.nitrogenDioxideMeasurement.NAME), - [clusters.NitrogenDioxideConcentrationMeasurement.attributes.LevelValue.ID] = levelHandlerFactory(capabilities.nitrogenDioxideHealthConcern.nitrogenDioxideHealthConcern) + [clusters.NitrogenDioxideConcentrationMeasurement.attributes.LevelValue.ID] = attribute_handlers.concentration_level_value_factory(capabilities.nitrogenDioxideHealthConcern.nitrogenDioxideHealthConcern), + [clusters.NitrogenDioxideConcentrationMeasurement.attributes.MeasuredValue.ID] = attribute_handlers.concentration_measured_value_factory(capabilities.nitrogenDioxideMeasurement.NAME, capabilities.nitrogenDioxideMeasurement.nitrogenDioxide, fields.units.PPM), + [clusters.NitrogenDioxideConcentrationMeasurement.attributes.MeasurementUnit.ID] = attribute_handlers.concentration_measurement_unit_factory(capabilities.nitrogenDioxideMeasurement.NAME), }, [clusters.OzoneConcentrationMeasurement.ID] = { - [clusters.OzoneConcentrationMeasurement.attributes.MeasuredValue.ID] = measurementHandlerFactory(capabilities.ozoneMeasurement.NAME, capabilities.ozoneMeasurement.ozone, units.PPM), - [clusters.OzoneConcentrationMeasurement.attributes.MeasurementUnit.ID] = store_unit_factory(capabilities.ozoneMeasurement.NAME), - [clusters.OzoneConcentrationMeasurement.attributes.LevelValue.ID] = levelHandlerFactory(capabilities.ozoneHealthConcern.ozoneHealthConcern) - }, - [clusters.FormaldehydeConcentrationMeasurement.ID] = { - [clusters.FormaldehydeConcentrationMeasurement.attributes.MeasuredValue.ID] = measurementHandlerFactory(capabilities.formaldehydeMeasurement.NAME, capabilities.formaldehydeMeasurement.formaldehydeLevel, units.PPM), - [clusters.FormaldehydeConcentrationMeasurement.attributes.MeasurementUnit.ID] = store_unit_factory(capabilities.formaldehydeMeasurement.NAME), - [clusters.FormaldehydeConcentrationMeasurement.attributes.LevelValue.ID] = levelHandlerFactory(capabilities.formaldehydeHealthConcern.formaldehydeHealthConcern), + [clusters.OzoneConcentrationMeasurement.attributes.LevelValue.ID] = attribute_handlers.concentration_level_value_factory(capabilities.ozoneHealthConcern.ozoneHealthConcern), + [clusters.OzoneConcentrationMeasurement.attributes.MeasuredValue.ID] = attribute_handlers.concentration_measured_value_factory(capabilities.ozoneMeasurement.NAME, capabilities.ozoneMeasurement.ozone, fields.units.PPM), + [clusters.OzoneConcentrationMeasurement.attributes.MeasurementUnit.ID] = attribute_handlers.concentration_measurement_unit_factory(capabilities.ozoneMeasurement.NAME), }, [clusters.Pm1ConcentrationMeasurement.ID] = { - [clusters.Pm1ConcentrationMeasurement.attributes.MeasuredValue.ID] = measurementHandlerFactory(capabilities.veryFineDustSensor.NAME, capabilities.veryFineDustSensor.veryFineDustLevel, units.UGM3), - [clusters.Pm1ConcentrationMeasurement.attributes.MeasurementUnit.ID] = store_unit_factory(capabilities.veryFineDustSensor.NAME), - [clusters.Pm1ConcentrationMeasurement.attributes.LevelValue.ID] = levelHandlerFactory(capabilities.veryFineDustHealthConcern.veryFineDustHealthConcern), - }, - [clusters.Pm25ConcentrationMeasurement.ID] = { - [clusters.Pm25ConcentrationMeasurement.attributes.MeasuredValue.ID] = measurementHandlerFactory(capabilities.fineDustSensor.NAME, capabilities.fineDustSensor.fineDustLevel, units.UGM3), - [clusters.Pm25ConcentrationMeasurement.attributes.MeasurementUnit.ID] = store_unit_factory(capabilities.fineDustSensor.NAME), - [clusters.Pm25ConcentrationMeasurement.attributes.LevelValue.ID] = levelHandlerFactory(capabilities.fineDustHealthConcern.fineDustHealthConcern), + [clusters.Pm1ConcentrationMeasurement.attributes.LevelValue.ID] = attribute_handlers.concentration_level_value_factory(capabilities.veryFineDustHealthConcern.veryFineDustHealthConcern), + [clusters.Pm1ConcentrationMeasurement.attributes.MeasuredValue.ID] = attribute_handlers.concentration_measured_value_factory(capabilities.veryFineDustSensor.NAME, capabilities.veryFineDustSensor.veryFineDustLevel, fields.units.UGM3), + [clusters.Pm1ConcentrationMeasurement.attributes.MeasurementUnit.ID] = attribute_handlers.concentration_measurement_unit_factory(capabilities.veryFineDustSensor.NAME), }, [clusters.Pm10ConcentrationMeasurement.ID] = { - [clusters.Pm10ConcentrationMeasurement.attributes.MeasuredValue.ID] = measurementHandlerFactory(capabilities.dustSensor.NAME, capabilities.dustSensor.dustLevel, units.UGM3), - [clusters.Pm10ConcentrationMeasurement.attributes.MeasurementUnit.ID] = store_unit_factory(capabilities.dustSensor.NAME), - [clusters.Pm10ConcentrationMeasurement.attributes.LevelValue.ID] = levelHandlerFactory(capabilities.dustHealthConcern.dustHealthConcern), + [clusters.Pm10ConcentrationMeasurement.attributes.LevelValue.ID] = attribute_handlers.concentration_level_value_factory(capabilities.dustHealthConcern.dustHealthConcern), + [clusters.Pm10ConcentrationMeasurement.attributes.MeasuredValue.ID] = attribute_handlers.concentration_measured_value_factory(capabilities.dustSensor.NAME, capabilities.dustSensor.dustLevel, fields.units.UGM3), + [clusters.Pm10ConcentrationMeasurement.attributes.MeasurementUnit.ID] = attribute_handlers.concentration_measurement_unit_factory(capabilities.dustSensor.NAME), + }, + [clusters.Pm25ConcentrationMeasurement.ID] = { + [clusters.Pm25ConcentrationMeasurement.attributes.LevelValue.ID] = attribute_handlers.concentration_level_value_factory(capabilities.fineDustHealthConcern.fineDustHealthConcern), + [clusters.Pm25ConcentrationMeasurement.attributes.MeasuredValue.ID] = attribute_handlers.concentration_measured_value_factory(capabilities.fineDustSensor.NAME, capabilities.fineDustSensor.fineDustLevel, fields.units.UGM3), + [clusters.Pm25ConcentrationMeasurement.attributes.MeasurementUnit.ID] = attribute_handlers.concentration_measurement_unit_factory(capabilities.fineDustSensor.NAME), }, [clusters.RadonConcentrationMeasurement.ID] = { - [clusters.RadonConcentrationMeasurement.attributes.MeasuredValue.ID] = measurementHandlerFactory(capabilities.radonMeasurement.NAME, capabilities.radonMeasurement.radonLevel, units.PCIL), - [clusters.RadonConcentrationMeasurement.attributes.MeasurementUnit.ID] = store_unit_factory(capabilities.radonMeasurement.NAME), - [clusters.RadonConcentrationMeasurement.attributes.LevelValue.ID] = levelHandlerFactory(capabilities.radonHealthConcern.radonHealthConcern) + [clusters.RadonConcentrationMeasurement.attributes.LevelValue.ID] = attribute_handlers.concentration_level_value_factory(capabilities.radonHealthConcern.radonHealthConcern), + [clusters.RadonConcentrationMeasurement.attributes.MeasuredValue.ID] = attribute_handlers.concentration_measured_value_factory(capabilities.radonMeasurement.NAME, capabilities.radonMeasurement.radonLevel, fields.units.PCIL), + [clusters.RadonConcentrationMeasurement.attributes.MeasurementUnit.ID] = attribute_handlers.concentration_measurement_unit_factory(capabilities.radonMeasurement.NAME), }, [clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement.ID] = { - [clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement.attributes.MeasuredValue.ID] = measurementHandlerFactory(capabilities.tvocMeasurement.NAME, capabilities.tvocMeasurement.tvocLevel, units.PPB), - [clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement.attributes.MeasurementUnit.ID] = store_unit_factory(capabilities.tvocMeasurement.NAME), - [clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement.attributes.LevelValue.ID] = levelHandlerFactory(capabilities.tvocHealthConcern.tvocHealthConcern) - }, - [clusters.ElectricalPowerMeasurement.ID] = { - [clusters.ElectricalPowerMeasurement.attributes.ActivePower.ID] = active_power_handler - }, - [clusters.ElectricalEnergyMeasurement.ID] = { - [clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyImported.ID] = energy_report_handler_factory(true), - [clusters.ElectricalEnergyMeasurement.attributes.PeriodicEnergyImported.ID] = energy_report_handler_factory(false), - }, - [clusters.WaterHeaterMode.ID] = { - [clusters.WaterHeaterMode.attributes.CurrentMode.ID] = water_heater_mode_handler, - [clusters.WaterHeaterMode.attributes.SupportedModes.ID] = water_heater_supported_modes_attr_handler + [clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement.attributes.LevelValue.ID] = attribute_handlers.concentration_level_value_factory(capabilities.tvocHealthConcern.tvocHealthConcern), + [clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement.attributes.MeasuredValue.ID] = attribute_handlers.concentration_measured_value_factory(capabilities.tvocMeasurement.NAME, capabilities.tvocMeasurement.tvocLevel, fields.units.PPB), + [clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement.attributes.MeasurementUnit.ID] = attribute_handlers.concentration_measurement_unit_factory(capabilities.tvocMeasurement.NAME), }, }, }, - subscribed_attributes = subscribed_attributes, capability_handlers = { - [capabilities.switch.ID] = { - [capabilities.switch.commands.on.NAME] = handle_switch_on, - [capabilities.switch.commands.off.NAME] = handle_switch_off, + [capabilities.airConditionerFanMode.ID] = { + [capabilities.airConditionerFanMode.commands.setFanMode.NAME] = capability_handlers.fan_mode_command_factory(capabilities.airConditionerFanMode) }, - [capabilities.thermostatMode.ID] = { - [capabilities.thermostatMode.commands.setThermostatMode.NAME] = set_thermostat_mode, - [capabilities.thermostatMode.commands.auto.NAME] = thermostat_mode_setter(capabilities.thermostatMode.thermostatMode.auto.NAME), - [capabilities.thermostatMode.commands.off.NAME] = thermostat_mode_setter(capabilities.thermostatMode.thermostatMode.off.NAME), - [capabilities.thermostatMode.commands.cool.NAME] = thermostat_mode_setter(capabilities.thermostatMode.thermostatMode.cool.NAME), - [capabilities.thermostatMode.commands.heat.NAME] = thermostat_mode_setter(capabilities.thermostatMode.thermostatMode.heat.NAME), - [capabilities.thermostatMode.commands.emergencyHeat.NAME] = thermostat_mode_setter(capabilities.thermostatMode.thermostatMode.emergency_heat.NAME) + [capabilities.airPurifierFanMode.ID] = { + [capabilities.airPurifierFanMode.commands.setAirPurifierFanMode.NAME] = capability_handlers.fan_mode_command_factory(capabilities.airPurifierFanMode) }, - [capabilities.thermostatFanMode.ID] = { - [capabilities.thermostatFanMode.commands.setThermostatFanMode.NAME] = set_fan_mode_factory(capabilities.thermostatFanMode), - [capabilities.thermostatFanMode.commands.fanAuto.NAME] = thermostat_fan_mode_setter(capabilities.thermostatFanMode.thermostatFanMode.auto.NAME), - [capabilities.thermostatFanMode.commands.fanOn.NAME] = thermostat_fan_mode_setter(capabilities.thermostatFanMode.thermostatFanMode.on.NAME) + [capabilities.fanMode.ID] = { + [capabilities.fanMode.commands.setFanMode.NAME] = capability_handlers.fan_mode_command_factory(capabilities.fanMode) + }, + [capabilities.fanOscillationMode.ID] = { + [capabilities.fanOscillationMode.commands.setFanOscillationMode.NAME] = capability_handlers.handle_set_fan_oscillation_mode, + }, + [capabilities.fanSpeedPercent.ID] = { + [capabilities.fanSpeedPercent.commands.setPercent.NAME] = capability_handlers.handle_fan_speed_set_percent, + }, + [capabilities.filterState.ID] = { + [capabilities.filterState.commands.resetFilter.NAME] = capability_handlers.handle_filter_state_reset_filter, + }, + [capabilities.mode.ID] = { + [capabilities.mode.commands.setMode.NAME] = capability_handlers.handle_set_mode, + }, + [capabilities.switch.ID] = { + [capabilities.switch.commands.off.NAME] = capability_handlers.handle_switch_off, + [capabilities.switch.commands.on.NAME] = capability_handlers.handle_switch_on, }, [capabilities.thermostatCoolingSetpoint.ID] = { - [capabilities.thermostatCoolingSetpoint.commands.setCoolingSetpoint.NAME] = set_setpoint(clusters.Thermostat.attributes.OccupiedCoolingSetpoint) + [capabilities.thermostatCoolingSetpoint.commands.setCoolingSetpoint.NAME] = capability_handlers.thermostat_set_setpoint_factory(clusters.Thermostat.attributes.OccupiedCoolingSetpoint) }, [capabilities.thermostatHeatingSetpoint.ID] = { - [capabilities.thermostatHeatingSetpoint.commands.setHeatingSetpoint.NAME] = set_setpoint(clusters.Thermostat.attributes.OccupiedHeatingSetpoint) + [capabilities.thermostatHeatingSetpoint.commands.setHeatingSetpoint.NAME] = capability_handlers.thermostat_set_setpoint_factory(clusters.Thermostat.attributes.OccupiedHeatingSetpoint) + }, + [capabilities.thermostatFanMode.ID] = { + [capabilities.thermostatFanMode.commands.fanAuto.NAME] = capability_handlers.thermostat_fan_mode_command_factory(capabilities.thermostatFanMode.thermostatFanMode.auto.NAME), + [capabilities.thermostatFanMode.commands.fanOn.NAME] = capability_handlers.thermostat_fan_mode_command_factory(capabilities.thermostatFanMode.thermostatFanMode.on.NAME), + [capabilities.thermostatFanMode.commands.setThermostatFanMode.NAME] = capability_handlers.fan_mode_command_factory(capabilities.thermostatFanMode), + }, + [capabilities.thermostatMode.ID] = { + [capabilities.thermostatMode.commands.auto.NAME] = capability_handlers.thermostat_mode_command_factory(capabilities.thermostatMode.thermostatMode.auto.NAME), + [capabilities.thermostatMode.commands.cool.NAME] = capability_handlers.thermostat_mode_command_factory(capabilities.thermostatMode.thermostatMode.cool.NAME), + [capabilities.thermostatMode.commands.emergencyHeat.NAME] = capability_handlers.thermostat_mode_command_factory(capabilities.thermostatMode.thermostatMode.emergency_heat.NAME), + [capabilities.thermostatMode.commands.heat.NAME] = capability_handlers.thermostat_mode_command_factory(capabilities.thermostatMode.thermostatMode.heat.NAME), + [capabilities.thermostatMode.commands.off.NAME] = capability_handlers.thermostat_mode_command_factory(capabilities.thermostatMode.thermostatMode.off.NAME), + [capabilities.thermostatMode.commands.setThermostatMode.NAME] = capability_handlers.handle_set_thermostat_mode, + }, + [capabilities.windMode.ID] = { + [capabilities.windMode.commands.setWindMode.NAME] = capability_handlers.handle_set_wind_mode, }, + }, + subscribed_attributes = { [capabilities.airConditionerFanMode.ID] = { - [capabilities.airConditionerFanMode.commands.setFanMode.NAME] = set_fan_mode_factory(capabilities.airConditionerFanMode) + clusters.FanControl.attributes.FanModeSequence, + clusters.FanControl.attributes.FanMode }, [capabilities.airPurifierFanMode.ID] = { - [capabilities.airPurifierFanMode.commands.setAirPurifierFanMode.NAME] = set_fan_mode_factory(capabilities.airPurifierFanMode) + clusters.FanControl.attributes.FanModeSequence, + clusters.FanControl.attributes.FanMode + }, + [capabilities.battery.ID] = { + clusters.PowerSource.attributes.BatPercentRemaining + }, + [capabilities.batteryLevel.ID] = { + clusters.PowerSource.attributes.BatChargeLevel + }, + [capabilities.energyMeter.ID] = { + clusters.ElectricalEnergyMeasurement.attributes.PeriodicEnergyImported, + clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyImported }, [capabilities.fanMode.ID] = { - [capabilities.fanMode.commands.setFanMode.NAME] = set_fan_mode_factory(capabilities.fanMode) + clusters.FanControl.attributes.FanModeSequence, + clusters.FanControl.attributes.FanMode + }, + [capabilities.fanOscillationMode.ID] = { + clusters.FanControl.attributes.RockSupport, + clusters.FanControl.attributes.RockSetting }, [capabilities.fanSpeedPercent.ID] = { - [capabilities.fanSpeedPercent.commands.setPercent.NAME] = set_fan_speed_percent, + clusters.FanControl.attributes.PercentCurrent }, - [capabilities.windMode.ID] = { - [capabilities.windMode.commands.setWindMode.NAME] = set_wind_mode, + [capabilities.filterState.ID] = { + clusters.HepaFilterMonitoring.attributes.Condition, + clusters.ActivatedCarbonFilterMonitoring.attributes.Condition }, - [capabilities.fanOscillationMode.ID] = { - [capabilities.fanOscillationMode.commands.setFanOscillationMode.NAME] = set_rock_mode, + [capabilities.filterStatus.ID] = { + clusters.HepaFilterMonitoring.attributes.ChangeIndication, + clusters.ActivatedCarbonFilterMonitoring.attributes.ChangeIndication }, [capabilities.mode.ID] = { - [capabilities.mode.commands.setMode.NAME] = set_water_heater_mode, + clusters.WaterHeaterMode.attributes.CurrentMode, + clusters.WaterHeaterMode.attributes.SupportedModes + }, + [capabilities.powerConsumptionReport.ID] = { + clusters.ElectricalEnergyMeasurement.attributes.PeriodicEnergyImported, + clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyImported + }, + [capabilities.powerMeter.ID] = { + clusters.ElectricalPowerMeasurement.attributes.ActivePower + }, + [capabilities.relativeHumidityMeasurement.ID] = { + clusters.RelativeHumidityMeasurement.attributes.MeasuredValue + }, + [capabilities.switch.ID] = { + clusters.OnOff.attributes.OnOff + }, + [capabilities.temperatureMeasurement.ID] = { + clusters.Thermostat.attributes.LocalTemperature, + clusters.TemperatureMeasurement.attributes.MeasuredValue, + clusters.TemperatureMeasurement.attributes.MinMeasuredValue, + clusters.TemperatureMeasurement.attributes.MaxMeasuredValue + }, + [capabilities.thermostatCoolingSetpoint.ID] = { + clusters.Thermostat.attributes.OccupiedCoolingSetpoint, + clusters.Thermostat.attributes.AbsMinCoolSetpointLimit, + clusters.Thermostat.attributes.AbsMaxCoolSetpointLimit + }, + [capabilities.thermostatFanMode.ID] = { + clusters.FanControl.attributes.FanModeSequence, + clusters.FanControl.attributes.FanMode + }, + [capabilities.thermostatHeatingSetpoint.ID] = { + clusters.Thermostat.attributes.OccupiedHeatingSetpoint, + clusters.Thermostat.attributes.AbsMinHeatSetpointLimit, + clusters.Thermostat.attributes.AbsMaxHeatSetpointLimit + }, + [capabilities.thermostatMode.ID] = { + clusters.Thermostat.attributes.SystemMode, + clusters.Thermostat.attributes.ControlSequenceOfOperation + }, + [capabilities.thermostatOperatingState.ID] = { + clusters.Thermostat.attributes.ThermostatRunningState + }, + [capabilities.windMode.ID] = { + clusters.FanControl.attributes.WindSupport, + clusters.FanControl.attributes.WindSetting + }, + -- AIR QUALITY SENSOR DEVICE TYPE SPECIFIC CAPABILITIES -- + [capabilities.airQualityHealthConcern.ID] = { + clusters.AirQuality.attributes.AirQuality + }, + [capabilities.atmosphericPressureMeasurement.ID] = { + clusters.PressureMeasurement.attributes.MeasuredValue + }, + [capabilities.carbonDioxideHealthConcern.ID] = { + clusters.CarbonDioxideConcentrationMeasurement.attributes.LevelValue, + }, + [capabilities.carbonDioxideMeasurement.ID] = { + clusters.CarbonDioxideConcentrationMeasurement.attributes.MeasuredValue, + clusters.CarbonDioxideConcentrationMeasurement.attributes.MeasurementUnit, + }, + [capabilities.carbonMonoxideHealthConcern.ID] = { + clusters.CarbonMonoxideConcentrationMeasurement.attributes.LevelValue, + }, + [capabilities.carbonMonoxideMeasurement.ID] = { + clusters.CarbonMonoxideConcentrationMeasurement.attributes.MeasuredValue, + clusters.CarbonMonoxideConcentrationMeasurement.attributes.MeasurementUnit, + }, + [capabilities.dustHealthConcern.ID] = { + clusters.Pm10ConcentrationMeasurement.attributes.LevelValue, + }, + [capabilities.dustSensor.ID] = { + clusters.Pm25ConcentrationMeasurement.attributes.MeasuredValue, + clusters.Pm25ConcentrationMeasurement.attributes.MeasurementUnit, + clusters.Pm10ConcentrationMeasurement.attributes.MeasuredValue, + clusters.Pm10ConcentrationMeasurement.attributes.MeasurementUnit, + }, + [capabilities.fineDustHealthConcern.ID] = { + clusters.Pm25ConcentrationMeasurement.attributes.LevelValue, + }, + [capabilities.fineDustSensor.ID] = { + clusters.Pm25ConcentrationMeasurement.attributes.MeasuredValue, + clusters.Pm25ConcentrationMeasurement.attributes.MeasurementUnit, + }, + [capabilities.formaldehydeHealthConcern.ID] = { + clusters.FormaldehydeConcentrationMeasurement.attributes.LevelValue, + }, + [capabilities.formaldehydeMeasurement.ID] = { + clusters.FormaldehydeConcentrationMeasurement.attributes.MeasuredValue, + clusters.FormaldehydeConcentrationMeasurement.attributes.MeasurementUnit, + }, + [capabilities.nitrogenDioxideHealthConcern.ID] = { + clusters.NitrogenDioxideConcentrationMeasurement.attributes.LevelValue, + }, + [capabilities.nitrogenDioxideMeasurement.ID] = { + clusters.NitrogenDioxideConcentrationMeasurement.attributes.MeasuredValue, + clusters.NitrogenDioxideConcentrationMeasurement.attributes.MeasurementUnit + }, + [capabilities.ozoneHealthConcern.ID] = { + clusters.OzoneConcentrationMeasurement.attributes.LevelValue, + }, + [capabilities.ozoneMeasurement.ID] = { + clusters.OzoneConcentrationMeasurement.attributes.MeasuredValue, + clusters.OzoneConcentrationMeasurement.attributes.MeasurementUnit + }, + [capabilities.radonHealthConcern.ID] = { + clusters.RadonConcentrationMeasurement.attributes.LevelValue, + }, + [capabilities.radonMeasurement.ID] = { + clusters.RadonConcentrationMeasurement.attributes.MeasuredValue, + clusters.RadonConcentrationMeasurement.attributes.MeasurementUnit, + }, + [capabilities.relativeHumidityMeasurement.ID] = { + clusters.RelativeHumidityMeasurement.attributes.MeasuredValue + }, + [capabilities.tvocHealthConcern.ID] = { + clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement.attributes.LevelValue + }, + [capabilities.tvocMeasurement.ID] = { + clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement.attributes.MeasuredValue, + clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement.attributes.MeasurementUnit, + }, + [capabilities.veryFineDustHealthConcern.ID] = { + clusters.Pm1ConcentrationMeasurement.attributes.LevelValue, + }, + [capabilities.veryFineDustSensor.ID] = { + clusters.Pm1ConcentrationMeasurement.attributes.MeasuredValue, + clusters.Pm1ConcentrationMeasurement.attributes.MeasurementUnit, }, - [capabilities.filterState.ID] = { - [capabilities.filterState.commands.resetFilter.NAME] = reset_filter_state, - } }, supported_capabilities = { - capabilities.thermostatMode, - capabilities.thermostatHeatingSetpoint, - capabilities.thermostatCoolingSetpoint, - capabilities.thermostatFanMode, - capabilities.thermostatOperatingState, capabilities.airConditionerFanMode, - capabilities.fanMode, - capabilities.fanSpeedPercent, + capabilities.airQualityHealthConcern, capabilities.airPurifierFanMode, - capabilities.windMode, - capabilities.fanOscillationMode, capabilities.battery, capabilities.batteryLevel, - capabilities.filterState, - capabilities.filterStatus, - capabilities.airQualityHealthConcern, capabilities.carbonDioxideHealthConcern, capabilities.carbonDioxideMeasurement, capabilities.carbonMonoxideHealthConcern, capabilities.carbonMonoxideMeasurement, + capabilities.dustHealthConcern, + capabilities.dustSensor, + capabilities.energyMeter, + capabilities.fanMode, + capabilities.fanOscillationMode, + capabilities.fanSpeedPercent, + capabilities.filterState, + capabilities.filterStatus, + capabilities.fineDustHealthConcern, + capabilities.fineDustSensor, + capabilities.formaldehydeHealthConcern, + capabilities.formaldehydeMeasurement, + capabilities.mode, capabilities.nitrogenDioxideHealthConcern, capabilities.nitrogenDioxideMeasurement, capabilities.ozoneHealthConcern, capabilities.ozoneMeasurement, - capabilities.formaldehydeHealthConcern, - capabilities.formaldehydeMeasurement, - capabilities.veryFineDustHealthConcern, - capabilities.veryFineDustSensor, - capabilities.fineDustHealthConcern, - capabilities.fineDustSensor, - capabilities.dustSensor, - capabilities.dustHealthConcern, + capabilities.powerConsumptionReport, + capabilities.powerMeter, capabilities.radonHealthConcern, capabilities.radonMeasurement, + capabilities.thermostatCoolingSetpoint, + capabilities.thermostatFanMode, + capabilities.thermostatHeatingSetpoint, + capabilities.thermostatMode, + capabilities.thermostatOperatingState, capabilities.tvocHealthConcern, capabilities.tvocMeasurement, - capabilities.powerMeter, - capabilities.energyMeter, - capabilities.powerConsumptionReport, - capabilities.mode + capabilities.veryFineDustHealthConcern, + capabilities.veryFineDustSensor, + capabilities.windMode, }, } diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_air_purifier.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_air_purifier.lua index d9cb8c6309..ca7fddcdd9 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_air_purifier.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_air_purifier.lua @@ -1,16 +1,6 @@ --- Copyright 2025 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local test = require "integration_test" test.set_rpc_version(0) local capabilities = require "st.capabilities" @@ -20,19 +10,19 @@ local clusters = require "st.matter.clusters" local version = require "version" if version.api < 10 then - clusters.HepaFilterMonitoring = require "HepaFilterMonitoring" - clusters.ActivatedCarbonFilterMonitoring = require "ActivatedCarbonFilterMonitoring" - clusters.AirQuality = require "AirQuality" - clusters.CarbonMonoxideConcentrationMeasurement = require "CarbonMonoxideConcentrationMeasurement" - clusters.CarbonDioxideConcentrationMeasurement = require "CarbonDioxideConcentrationMeasurement" - clusters.FormaldehydeConcentrationMeasurement = require "FormaldehydeConcentrationMeasurement" - clusters.NitrogenDioxideConcentrationMeasurement = require "NitrogenDioxideConcentrationMeasurement" - clusters.OzoneConcentrationMeasurement = require "OzoneConcentrationMeasurement" - clusters.Pm1ConcentrationMeasurement = require "Pm1ConcentrationMeasurement" - clusters.Pm10ConcentrationMeasurement = require "Pm10ConcentrationMeasurement" - clusters.Pm25ConcentrationMeasurement = require "Pm25ConcentrationMeasurement" - clusters.RadonConcentrationMeasurement = require "RadonConcentrationMeasurement" - clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement = require "TotalVolatileOrganicCompoundsConcentrationMeasurement" + clusters.HepaFilterMonitoring = require "embedded_clusters.HepaFilterMonitoring" + clusters.ActivatedCarbonFilterMonitoring = require "embedded_clusters.ActivatedCarbonFilterMonitoring" + clusters.AirQuality = require "embedded_clusters.AirQuality" + clusters.CarbonMonoxideConcentrationMeasurement = require "embedded_clusters.CarbonMonoxideConcentrationMeasurement" + clusters.CarbonDioxideConcentrationMeasurement = require "embedded_clusters.CarbonDioxideConcentrationMeasurement" + clusters.FormaldehydeConcentrationMeasurement = require "embedded_clusters.FormaldehydeConcentrationMeasurement" + clusters.NitrogenDioxideConcentrationMeasurement = require "embedded_clusters.NitrogenDioxideConcentrationMeasurement" + clusters.OzoneConcentrationMeasurement = require "embedded_clusters.OzoneConcentrationMeasurement" + clusters.Pm1ConcentrationMeasurement = require "embedded_clusters.Pm1ConcentrationMeasurement" + clusters.Pm10ConcentrationMeasurement = require "embedded_clusters.Pm10ConcentrationMeasurement" + clusters.Pm25ConcentrationMeasurement = require "embedded_clusters.Pm25ConcentrationMeasurement" + clusters.RadonConcentrationMeasurement = require "embedded_clusters.RadonConcentrationMeasurement" + clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement = require "embedded_clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement" end local mock_device = test.mock_device.build_test_matter_device({ diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_air_purifier_api9.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_air_purifier_api9.lua index 871693d5a6..fd68c958d9 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_air_purifier_api9.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_air_purifier_api9.lua @@ -1,16 +1,6 @@ --- Copyright 2025 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local test = require "integration_test" local capabilities = require "st.capabilities" local clusters = require "st.matter.clusters" @@ -21,19 +11,19 @@ local version = require "version" version.api = 9 -- include driver-side cluster definitions to test embedded clusters on lower api versions -clusters.HepaFilterMonitoring = require "HepaFilterMonitoring" -clusters.ActivatedCarbonFilterMonitoring = require "ActivatedCarbonFilterMonitoring" -clusters.AirQuality = require "AirQuality" -clusters.CarbonMonoxideConcentrationMeasurement = require "CarbonMonoxideConcentrationMeasurement" -clusters.CarbonDioxideConcentrationMeasurement = require "CarbonDioxideConcentrationMeasurement" -clusters.FormaldehydeConcentrationMeasurement = require "FormaldehydeConcentrationMeasurement" -clusters.NitrogenDioxideConcentrationMeasurement = require "NitrogenDioxideConcentrationMeasurement" -clusters.OzoneConcentrationMeasurement = require "OzoneConcentrationMeasurement" -clusters.Pm1ConcentrationMeasurement = require "Pm1ConcentrationMeasurement" -clusters.Pm10ConcentrationMeasurement = require "Pm10ConcentrationMeasurement" -clusters.Pm25ConcentrationMeasurement = require "Pm25ConcentrationMeasurement" -clusters.RadonConcentrationMeasurement = require "RadonConcentrationMeasurement" -clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement = require "TotalVolatileOrganicCompoundsConcentrationMeasurement" +clusters.HepaFilterMonitoring = require "embedded_clusters.HepaFilterMonitoring" +clusters.ActivatedCarbonFilterMonitoring = require "embedded_clusters.ActivatedCarbonFilterMonitoring" +clusters.AirQuality = require "embedded_clusters.AirQuality" +clusters.CarbonMonoxideConcentrationMeasurement = require "embedded_clusters.CarbonMonoxideConcentrationMeasurement" +clusters.CarbonDioxideConcentrationMeasurement = require "embedded_clusters.CarbonDioxideConcentrationMeasurement" +clusters.FormaldehydeConcentrationMeasurement = require "embedded_clusters.FormaldehydeConcentrationMeasurement" +clusters.NitrogenDioxideConcentrationMeasurement = require "embedded_clusters.NitrogenDioxideConcentrationMeasurement" +clusters.OzoneConcentrationMeasurement = require "embedded_clusters.OzoneConcentrationMeasurement" +clusters.Pm1ConcentrationMeasurement = require "embedded_clusters.Pm1ConcentrationMeasurement" +clusters.Pm10ConcentrationMeasurement = require "embedded_clusters.Pm10ConcentrationMeasurement" +clusters.Pm25ConcentrationMeasurement = require "embedded_clusters.Pm25ConcentrationMeasurement" +clusters.RadonConcentrationMeasurement = require "embedded_clusters.RadonConcentrationMeasurement" +clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement = require "embedded_clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement" local mock_device = test.mock_device.build_test_matter_device({ profile = t_utils.get_profile_definition("air-purifier-hepa-ac-wind.yml"), diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_air_purifier_modular.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_air_purifier_modular.lua index 4f2b5bd0f4..745076cd04 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_air_purifier_modular.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_air_purifier_modular.lua @@ -1,16 +1,5 @@ --- Copyright 2025 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 local test = require "integration_test" local capabilities = require "st.capabilities" @@ -23,19 +12,19 @@ local version = require "version" test.disable_startup_messages() if version.api < 10 then - clusters.HepaFilterMonitoring = require "HepaFilterMonitoring" - clusters.ActivatedCarbonFilterMonitoring = require "ActivatedCarbonFilterMonitoring" - clusters.AirQuality = require "AirQuality" - clusters.CarbonMonoxideConcentrationMeasurement = require "CarbonMonoxideConcentrationMeasurement" - clusters.CarbonDioxideConcentrationMeasurement = require "CarbonDioxideConcentrationMeasurement" - clusters.FormaldehydeConcentrationMeasurement = require "FormaldehydeConcentrationMeasurement" - clusters.NitrogenDioxideConcentrationMeasurement = require "NitrogenDioxideConcentrationMeasurement" - clusters.OzoneConcentrationMeasurement = require "OzoneConcentrationMeasurement" - clusters.Pm1ConcentrationMeasurement = require "Pm1ConcentrationMeasurement" - clusters.Pm10ConcentrationMeasurement = require "Pm10ConcentrationMeasurement" - clusters.Pm25ConcentrationMeasurement = require "Pm25ConcentrationMeasurement" - clusters.RadonConcentrationMeasurement = require "RadonConcentrationMeasurement" - clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement = require "TotalVolatileOrganicCompoundsConcentrationMeasurement" + clusters.HepaFilterMonitoring = require "embedded_clusters.HepaFilterMonitoring" + clusters.ActivatedCarbonFilterMonitoring = require "embedded_clusters.ActivatedCarbonFilterMonitoring" + clusters.AirQuality = require "embedded_clusters.AirQuality" + clusters.CarbonMonoxideConcentrationMeasurement = require "embedded_clusters.CarbonMonoxideConcentrationMeasurement" + clusters.CarbonDioxideConcentrationMeasurement = require "embedded_clusters.CarbonDioxideConcentrationMeasurement" + clusters.FormaldehydeConcentrationMeasurement = require "embedded_clusters.FormaldehydeConcentrationMeasurement" + clusters.NitrogenDioxideConcentrationMeasurement = require "embedded_clusters.NitrogenDioxideConcentrationMeasurement" + clusters.OzoneConcentrationMeasurement = require "embedded_clusters.OzoneConcentrationMeasurement" + clusters.Pm1ConcentrationMeasurement = require "embedded_clusters.Pm1ConcentrationMeasurement" + clusters.Pm10ConcentrationMeasurement = require "embedded_clusters.Pm10ConcentrationMeasurement" + clusters.Pm25ConcentrationMeasurement = require "embedded_clusters.Pm25ConcentrationMeasurement" + clusters.RadonConcentrationMeasurement = require "embedded_clusters.RadonConcentrationMeasurement" + clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement = require "embedded_clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement" end local mock_device_basic = test.mock_device.build_test_matter_device({ @@ -315,10 +304,6 @@ test.register_coroutine_test( "Test profile change on init for basic Air Purifier device", function() test.socket.device_lifecycle:__queue_receive({ mock_device_basic.id, "doConfigure" }) - test.socket.matter:__queue_receive({ - mock_device_basic.id, - clusters.Thermostat.attributes.AttributeList:build_test_report_data(mock_device_basic, 1, {uint32(0)}) - }) mock_device_basic:expect_metadata_update(expected_update_metadata) mock_device_basic:expect_metadata_update({ provisioning_state = "PROVISIONED" }) diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_fan.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_fan.lua index 4a94a9a7e9..8eefceb23b 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_fan.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_fan.lua @@ -1,16 +1,6 @@ --- Copyright 2025 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local test = require "integration_test" local t_utils = require "integration_test.utils" diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_heat_pump.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_heat_pump.lua index ba097d0451..e96bfb69fc 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_heat_pump.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_heat_pump.lua @@ -1,16 +1,5 @@ --- Copyright 2024 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 local test = require "integration_test" local clusters = require "st.matter.clusters" @@ -27,8 +16,8 @@ local HEAT_PUMP_DEVICE_TYPE_ID = 0x0309 local THERMOSTAT_DEVICE_TYPE_ID = 0x0301 if version.api < 11 then - clusters.ElectricalEnergyMeasurement = require "ElectricalEnergyMeasurement" - clusters.ElectricalPowerMeasurement = require "ElectricalPowerMeasurement" + clusters.ElectricalEnergyMeasurement = require "embedded_clusters.ElectricalEnergyMeasurement" + clusters.ElectricalPowerMeasurement = require "embedded_clusters.ElectricalPowerMeasurement" end local device_desc = { diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_room_ac.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_room_ac.lua index cec0cb9ffc..35728e628d 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_room_ac.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_room_ac.lua @@ -1,16 +1,6 @@ --- Copyright 2024 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local test = require "integration_test" test.set_rpc_version(0) local capabilities = require "st.capabilities" diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_room_ac_modular.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_room_ac_modular.lua index 2da9e5c023..96577d72a3 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_room_ac_modular.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_room_ac_modular.lua @@ -1,16 +1,6 @@ --- Copyright 2024 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + local test = require "integration_test" local capabilities = require "st.capabilities" local t_utils = require "integration_test.utils" diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_battery.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_battery.lua index 35f312c67e..9fb8536c23 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_battery.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_battery.lua @@ -1,16 +1,5 @@ --- Copyright 2023 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 local test = require "integration_test" local t_utils = require "integration_test.utils" diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_featuremap.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_featuremap.lua index 37f73d0e50..c898fab362 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_featuremap.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_featuremap.lua @@ -1,16 +1,5 @@ --- 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 local test = require "integration_test" local t_utils = require "integration_test.utils" diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_multiple_device_types.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_multiple_device_types.lua index 7f9577fa8f..a8acdc253e 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_multiple_device_types.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_multiple_device_types.lua @@ -1,16 +1,5 @@ --- Copyright 2025 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 local test = require "integration_test" local t_utils = require "integration_test.utils" diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_setpoint_limits.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_setpoint_limits.lua index b28d11dcc7..ae9c25940f 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_setpoint_limits.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_setpoint_limits.lua @@ -1,16 +1,5 @@ --- Copyright 2025 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 local test = require "integration_test" local capabilities = require "st.capabilities" diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_setpoint_limits_rpc.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_setpoint_limits_rpc.lua index 72b46af4f3..bbd084cb72 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_setpoint_limits_rpc.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_setpoint_limits_rpc.lua @@ -1,15 +1,5 @@ --- --- 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 local test = require "integration_test" local t_utils = require "integration_test.utils" diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat.lua index 44ed395797..e40572ff5d 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat.lua @@ -1,16 +1,5 @@ --- 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 local test = require "integration_test" local capabilities = require "st.capabilities" diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat_composed_bridged.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat_composed_bridged.lua index e901ac59a4..1f1a4e6cb4 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat_composed_bridged.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat_composed_bridged.lua @@ -1,16 +1,5 @@ --- Copyright 2023 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 local test = require "integration_test" local capabilities = require "st.capabilities" diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat_modular.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat_modular.lua index 6645d39692..73d180da7e 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat_modular.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat_modular.lua @@ -1,16 +1,5 @@ --- Copyright 2025 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 local test = require "integration_test" local t_utils = require "integration_test.utils" diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat_rpc5.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat_rpc5.lua index b52b08027e..03e7df8ae2 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat_rpc5.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermostat_rpc5.lua @@ -1,16 +1,5 @@ --- 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 local clusters = require "st.matter.clusters" local test = require "integration_test" diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_water_heater.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_water_heater.lua index cbd6464a48..b39db4136b 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_water_heater.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_water_heater.lua @@ -1,16 +1,5 @@ --- 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. +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 local test = require "integration_test" local capabilities = require "st.capabilities" @@ -19,11 +8,12 @@ local version = require "version" local clusters = require "st.matter.clusters" if version.api < 13 then - clusters.WaterHeaterMode = require "WaterHeaterMode" + clusters.WaterHeaterMode = require "embedded_clusters.WaterHeaterMode" end if version.api < 11 then - clusters.ElectricalEnergyMeasurement = require "ElectricalEnergyMeasurement" + clusters.ElectricalEnergyMeasurement = require "embedded_clusters.ElectricalEnergyMeasurement" + clusters.ElectricalPowerMeasurement = require "embedded_clusters.ElectricalPowerMeasurement" end local WATER_HEATER_EP = 10 diff --git a/drivers/SmartThings/matter-thermostat/src/thermostat_handlers/attribute_handlers.lua b/drivers/SmartThings/matter-thermostat/src/thermostat_handlers/attribute_handlers.lua new file mode 100644 index 0000000000..78f5ee3bf2 --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/thermostat_handlers/attribute_handlers.lua @@ -0,0 +1,661 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local log = require "log" +local version = require "version" +local capabilities = require "st.capabilities" +local clusters = require "st.matter.clusters" +local st_utils = require "st.utils" +local fields = require "thermostat_utils.fields" +local thermostat_utils = require "thermostat_utils.utils" + +if version.api < 10 then + clusters.HepaFilterMonitoring = require "embedded_clusters.HepaFilterMonitoring" + clusters.ActivatedCarbonFilterMonitoring = require "embedded_clusters.ActivatedCarbonFilterMonitoring" +end + +if version.api < 11 then + clusters.ElectricalEnergyMeasurement = require "embedded_clusters.ElectricalEnergyMeasurement" +end + +if version.api < 13 then + clusters.WaterHeaterMode = require "embedded_clusters.WaterHeaterMode" +end + +local AttributeHandlers = {} + + +-- [[ THERMOSTAT CLUSTER ATTRIBUTES ]] -- + +function AttributeHandlers.thermostat_attribute_list_handler(driver, device, ib, response) + local device_cfg = require "thermostat_utils.device_configuration" + for _, attr in ipairs(ib.data.elements) do + -- mark whether the optional attribute ThermostatRunningState (0x029) is present and try profiling + if attr.value == 0x029 then + device:set_field(fields.profiling_data.THERMOSTAT_RUNNING_STATE_SUPPORT, true) + device_cfg.match_profile(device) + return + end + end + device:set_field(fields.profiling_data.THERMOSTAT_RUNNING_STATE_SUPPORT, false) + device_cfg.match_profile(device) +end + +function AttributeHandlers.system_mode_handler(driver, device, ib, response) + if device:get_field(fields.OPTIONAL_THERMOSTAT_MODES_SEEN) == nil then -- this being nil means the control_sequence_of_operation_handler hasn't run. + device.log.info_with({hub_logs = true}, "In the SystemMode handler: ControlSequenceOfOperation has not run yet. Exiting early.") + device:set_field(fields.SAVED_SYSTEM_MODE_IB, ib) + return + end + + local supported_modes = device:get_latest_state(device:endpoint_to_component(ib.endpoint_id), capabilities.thermostatMode.ID, capabilities.thermostatMode.supportedThermostatModes.NAME) or {} + -- check that the given mode was in the supported modes list + if thermostat_utils.tbl_contains(supported_modes, fields.THERMOSTAT_MODE_MAP[ib.data.value].NAME) then + device:emit_event_for_endpoint(ib.endpoint_id, fields.THERMOSTAT_MODE_MAP[ib.data.value]()) + return + end + -- if the value is not found in the supported modes list, check if it's disallowed and early return if so. + local disallowed_thermostat_modes = device:get_field(fields.DISALLOWED_THERMOSTAT_MODES) or {} + if thermostat_utils.tbl_contains(disallowed_thermostat_modes, fields.THERMOSTAT_MODE_MAP[ib.data.value].NAME) then + return + end + -- if we get here, then the reported mode is allowed and not in our mode map + -- add the mode to the OPTIONAL_THERMOSTAT_MODES_SEEN and supportedThermostatModes tables + local optional_modes_seen = st_utils.deep_copy(device:get_field(fields.OPTIONAL_THERMOSTAT_MODES_SEEN)) or {} + table.insert(optional_modes_seen, fields.THERMOSTAT_MODE_MAP[ib.data.value].NAME) + device:set_field(fields.OPTIONAL_THERMOSTAT_MODES_SEEN, optional_modes_seen, {persist=true}) + local sm_copy = st_utils.deep_copy(supported_modes) + table.insert(sm_copy, fields.THERMOSTAT_MODE_MAP[ib.data.value].NAME) + local supported_modes_event = capabilities.thermostatMode.supportedThermostatModes(sm_copy, {visibility = {displayed = false}}) + device:emit_event_for_endpoint(ib.endpoint_id, supported_modes_event) + device:emit_event_for_endpoint(ib.endpoint_id, fields.THERMOSTAT_MODE_MAP[ib.data.value]()) +end + +function AttributeHandlers.thermostat_running_state_handler(driver, device, ib, response) + for mode, operating_state in pairs(fields.THERMOSTAT_OPERATING_MODE_MAP) do + if ((ib.data.value >> mode) & 1) > 0 then + device:emit_event_for_endpoint(ib.endpoint_id, operating_state()) + return + end + end + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.thermostatOperatingState.thermostatOperatingState.idle()) +end + +function AttributeHandlers.control_sequence_of_operation_handler(driver, device, ib, response) + -- The ControlSequenceOfOperation attribute only directly specifies what can't be operated by the operating environment, not what can. + -- However, we assert here that a Cooling enum value implies that SystemMode supports cooling, and the same for a Heating enum. + -- We also assert that Off is supported, though per spec this is optional. + if device:get_field(fields.OPTIONAL_THERMOSTAT_MODES_SEEN) == nil then + device:set_field(fields.OPTIONAL_THERMOSTAT_MODES_SEEN, {capabilities.thermostatMode.thermostatMode.off.NAME}, {persist=true}) + end + local supported_modes = st_utils.deep_copy(device:get_field(fields.OPTIONAL_THERMOSTAT_MODES_SEEN)) + local disallowed_mode_operations = {} + + local modes_for_inclusion = {} + if ib.data.value <= clusters.Thermostat.attributes.ControlSequenceOfOperation.COOLING_WITH_REHEAT then + local _, found_idx = thermostat_utils.tbl_contains(supported_modes, capabilities.thermostatMode.thermostatMode.emergency_heat.NAME) + if found_idx then + table.remove(supported_modes, found_idx) -- if seen before, remove now + end + table.insert(supported_modes, capabilities.thermostatMode.thermostatMode.cool.NAME) + table.insert(disallowed_mode_operations, capabilities.thermostatMode.thermostatMode.heat.NAME) + table.insert(disallowed_mode_operations, capabilities.thermostatMode.thermostatMode.emergency_heat.NAME) + elseif ib.data.value <= clusters.Thermostat.attributes.ControlSequenceOfOperation.HEATING_WITH_REHEAT then + local _, found_idx = thermostat_utils.tbl_contains(supported_modes, capabilities.thermostatMode.thermostatMode.precooling.NAME) + if found_idx then + table.remove(supported_modes, found_idx) -- if seen before, remove now + end + table.insert(supported_modes, capabilities.thermostatMode.thermostatMode.heat.NAME) + table.insert(disallowed_mode_operations, capabilities.thermostatMode.thermostatMode.cool.NAME) + table.insert(disallowed_mode_operations, capabilities.thermostatMode.thermostatMode.precooling.NAME) + elseif ib.data.value <= clusters.Thermostat.attributes.ControlSequenceOfOperation.COOLING_AND_HEATING_WITH_REHEAT then + table.insert(modes_for_inclusion, capabilities.thermostatMode.thermostatMode.cool.NAME) + table.insert(modes_for_inclusion, capabilities.thermostatMode.thermostatMode.heat.NAME) + end + + -- check whether the Auto Mode should be supported in SystemMode, though this is unrelated to ControlSequenceOfOperation + local auto = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.AUTOMODE}) + if #auto > 0 then + table.insert(modes_for_inclusion, capabilities.thermostatMode.thermostatMode.auto.NAME) + else + table.insert(disallowed_mode_operations, capabilities.thermostatMode.thermostatMode.auto.NAME) + end + + -- if a disallowed value was once allowed and added, it should be removed now. + for index, mode in pairs(supported_modes) do + if thermostat_utils.tbl_contains(disallowed_mode_operations, mode) then + table.remove(supported_modes, index) + end + end + -- do not include any values twice + for _, mode in pairs(modes_for_inclusion) do + if not thermostat_utils.tbl_contains(supported_modes, mode) then + table.insert(supported_modes, mode) + end + end + device:set_field(fields.DISALLOWED_THERMOSTAT_MODES, disallowed_mode_operations) + local event = capabilities.thermostatMode.supportedThermostatModes(supported_modes, {visibility = {displayed = false}}) + device:emit_event_for_endpoint(ib.endpoint_id, event) + + -- will be set by the SystemMode handler if this handler hasn't run yet. + if device:get_field(fields.SAVED_SYSTEM_MODE_IB) then + AttributeHandlers.system_mode_handler(driver, device, device:get_field(fields.SAVED_SYSTEM_MODE_IB), response) + device:set_field(fields.SAVED_SYSTEM_MODE_IB, nil) + end +end + +function AttributeHandlers.min_setpoint_deadband_handler(driver, device, ib, response) + local val = ib.data.value / 10.0 + log.info("Setting " .. fields.setpoint_limit_device_field.MIN_DEADBAND .. " to " .. string.format("%s", val)) + device:set_field(fields.setpoint_limit_device_field.MIN_DEADBAND, val, { persist = true }) + device:set_field(fields.setpoint_limit_device_field.MIN_SETPOINT_DEADBAND_CHECKED, true, {persist = true}) +end + +function AttributeHandlers.abs_heat_setpoint_limit_factory(minOrMax) + return function(driver, device, ib, response) + if ib.data.value == nil then + return + end + local MAX_TEMP_IN_C = fields.THERMOSTAT_MAX_TEMP_IN_C + local MIN_TEMP_IN_C = fields.THERMOSTAT_MIN_TEMP_IN_C + local is_water_heater_device = (thermostat_utils.get_device_type(device) == fields.WATER_HEATER_DEVICE_TYPE_ID) + if is_water_heater_device then + MAX_TEMP_IN_C = fields.WATER_HEATER_MAX_TEMP_IN_C + MIN_TEMP_IN_C = fields.WATER_HEATER_MIN_TEMP_IN_C + end + local val = ib.data.value / 100.0 + val = st_utils.clamp_value(val, MIN_TEMP_IN_C, MAX_TEMP_IN_C) + device:set_field(minOrMax, val) + local min = device:get_field(fields.setpoint_limit_device_field.MIN_HEAT) + local max = device:get_field(fields.setpoint_limit_device_field.MAX_HEAT) + if min ~= nil and max ~= nil then + if min < max then + -- Only emit the capability for RPC version >= 5 (unit conversion for + -- heating setpoint range capability is only supported for RPC >= 5) + if version.rpc >= 5 then + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.thermostatHeatingSetpoint.heatingSetpointRange({ value = { minimum = min, maximum = max, step = 0.1 }, unit = "C" })) + end + else + device.log.warn_with({hub_logs = true}, string.format("Device reported a min heating setpoint %d that is not lower than the reported max %d", min, max)) + end + end + end +end + +function AttributeHandlers.abs_cool_setpoint_limit_factory(minOrMax) + return function(driver, device, ib, response) + if ib.data.value == nil then + return + end + local val = ib.data.value / 100.0 + val = st_utils.clamp_value(val, fields.THERMOSTAT_MIN_TEMP_IN_C, fields.THERMOSTAT_MAX_TEMP_IN_C) + device:set_field(minOrMax, val) + local min = device:get_field(fields.setpoint_limit_device_field.MIN_COOL) + local max = device:get_field(fields.setpoint_limit_device_field.MAX_COOL) + if min ~= nil and max ~= nil then + if min < max then + -- Only emit the capability for RPC version >= 5 (unit conversion for + -- cooling setpoint range capability is only supported for RPC >= 5) + if version.rpc >= 5 then + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.thermostatCoolingSetpoint.coolingSetpointRange({ value = { minimum = min, maximum = max, step = 0.1 }, unit = "C" })) + end + else + device.log.warn_with({hub_logs = true}, string.format("Device reported a min cooling setpoint %d that is not lower than the reported max %d", min, max)) + end + end + end +end + + +-- [[ TEMPERATURE MEASUREMENT CLUSER ATTRIBUTES ]] -- + +function AttributeHandlers.temperature_handler_factory(attribute) + return function(driver, device, ib, response) + if ib.data.value == nil then + return + end + local unit = "C" + + -- Only emit the capability for RPC version >= 5, since unit conversion for + -- range capabilities is only supported in that case. + if version.rpc >= 5 then + local event + if attribute == capabilities.thermostatCoolingSetpoint.coolingSetpoint then + local range = { + minimum = device:get_field(fields.setpoint_limit_device_field.MIN_COOL) or fields.THERMOSTAT_MIN_TEMP_IN_C, + maximum = device:get_field(fields.setpoint_limit_device_field.MAX_COOL) or fields.THERMOSTAT_MAX_TEMP_IN_C, + step = 0.1 + } + event = capabilities.thermostatCoolingSetpoint.coolingSetpointRange({value = range, unit = unit}) + device:emit_event_for_endpoint(ib.endpoint_id, event) + elseif attribute == capabilities.thermostatHeatingSetpoint.heatingSetpoint then + local MAX_TEMP_IN_C = fields.THERMOSTAT_MAX_TEMP_IN_C + local MIN_TEMP_IN_C = fields.THERMOSTAT_MIN_TEMP_IN_C + local is_water_heater_device = thermostat_utils.get_device_type(device) == fields.WATER_HEATER_DEVICE_TYPE_ID + if is_water_heater_device then + MAX_TEMP_IN_C = fields.WATER_HEATER_MAX_TEMP_IN_C + MIN_TEMP_IN_C = fields.WATER_HEATER_MIN_TEMP_IN_C + end + + local range = { + minimum = device:get_field(fields.setpoint_limit_device_field.MIN_HEAT) or MIN_TEMP_IN_C, + maximum = device:get_field(fields.setpoint_limit_device_field.MAX_HEAT) or MAX_TEMP_IN_C, + step = 0.1 + } + event = capabilities.thermostatHeatingSetpoint.heatingSetpointRange({value = range, unit = unit}) + device:emit_event_for_endpoint(ib.endpoint_id, event) + end + end + + local temp = ib.data.value / 100.0 + device:emit_event_for_endpoint(ib.endpoint_id, attribute({value = temp, unit = unit})) + end +end + +function AttributeHandlers.temperature_measured_value_bounds_factory(minOrMax) + return function(driver, device, ib, response) + if ib.data.value == nil then + return + end + local temp = ib.data.value / 100.0 + local unit = "C" + temp = st_utils.clamp_value(temp, fields.THERMOSTAT_MIN_TEMP_IN_C, fields.THERMOSTAT_MAX_TEMP_IN_C) + thermostat_utils.set_field_for_endpoint(device, minOrMax, ib.endpoint_id, temp) + local min = thermostat_utils.get_field_for_endpoint(device, fields.setpoint_limit_device_field.MIN_TEMP, ib.endpoint_id) + local max = thermostat_utils.get_field_for_endpoint(device, fields.setpoint_limit_device_field.MAX_TEMP, ib.endpoint_id) + if min ~= nil and max ~= nil then + if min < max then + -- Only emit the capability for RPC version >= 5 (unit conversion for + -- temperature range capability is only supported for RPC >= 5) + if version.rpc >= 5 then + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.temperatureMeasurement.temperatureRange({ value = { minimum = min, maximum = max }, unit = unit })) + end + thermostat_utils.set_field_for_endpoint(device, fields.setpoint_limit_device_field.MIN_TEMP, ib.endpoint_id, nil) + thermostat_utils.set_field_for_endpoint(device, fields.setpoint_limit_device_field.MAX_TEMP, ib.endpoint_id, nil) + else + device.log.warn_with({hub_logs = true}, string.format("Device reported a min temperature %d that is not lower than the reported max temperature %d", min, max)) + end + end + end +end + + +--[[ FAN CONTROL CLUSTER ATTRIBUTES ]] -- + +function AttributeHandlers.fan_mode_handler(driver, device, ib, response) + local fan_mode_event = { + [clusters.FanControl.attributes.FanMode.OFF] = { capabilities.fanMode.fanMode.off(), + capabilities.airConditionerFanMode.fanMode("off"), + capabilities.airPurifierFanMode.airPurifierFanMode.off(), + nil }, -- 'OFF' is not supported by thermostatFanMode + [clusters.FanControl.attributes.FanMode.LOW] = { capabilities.fanMode.fanMode.low(), + capabilities.airConditionerFanMode.fanMode("low"), + capabilities.airPurifierFanMode.airPurifierFanMode.low(), + capabilities.thermostatFanMode.thermostatFanMode.on() }, + [clusters.FanControl.attributes.FanMode.MEDIUM] = { capabilities.fanMode.fanMode.medium(), + capabilities.airConditionerFanMode.fanMode("medium"), + capabilities.airPurifierFanMode.airPurifierFanMode.medium(), + capabilities.thermostatFanMode.thermostatFanMode.on() }, + [clusters.FanControl.attributes.FanMode.HIGH] = { capabilities.fanMode.fanMode.high(), + capabilities.airConditionerFanMode.fanMode("high"), + capabilities.airPurifierFanMode.airPurifierFanMode.high(), + capabilities.thermostatFanMode.thermostatFanMode.on() }, + [clusters.FanControl.attributes.FanMode.ON] = { capabilities.fanMode.fanMode.auto(), + capabilities.airConditionerFanMode.fanMode("auto"), + capabilities.airPurifierFanMode.airPurifierFanMode.auto(), + capabilities.thermostatFanMode.thermostatFanMode.on() }, + [clusters.FanControl.attributes.FanMode.AUTO] = { capabilities.fanMode.fanMode.auto(), + capabilities.airConditionerFanMode.fanMode("auto"), + capabilities.airPurifierFanMode.airPurifierFanMode.auto(), + capabilities.thermostatFanMode.thermostatFanMode.auto() }, + [clusters.FanControl.attributes.FanMode.SMART] = { capabilities.fanMode.fanMode.auto(), + capabilities.airConditionerFanMode.fanMode("auto"), + capabilities.airPurifierFanMode.airPurifierFanMode.auto(), + capabilities.thermostatFanMode.thermostatFanMode.auto() } + } + local fan_mode_idx = device:supports_capability_by_id(capabilities.fanMode.ID) and 1 or + device:supports_capability_by_id(capabilities.airConditionerFanMode.ID) and 2 or + device:supports_capability_by_id(capabilities.airPurifierFanMode.ID) and 3 or + device:supports_capability_by_id(capabilities.thermostatFanMode.ID) and 4 + if fan_mode_idx ~= false and fan_mode_event[ib.data.value][fan_mode_idx] then + device:emit_event_for_endpoint(ib.endpoint_id, fan_mode_event[ib.data.value][fan_mode_idx]) + else + log.warn(string.format("Invalid Fan Mode (%s)", ib.data.value)) + end +end + +function AttributeHandlers.fan_mode_sequence_handler(driver, device, ib, response) + local supportedFanModes, supported_fan_modes_attribute + if ib.data.value == clusters.FanControl.attributes.FanModeSequence.OFF_LOW_MED_HIGH then + supportedFanModes = { "off", "low", "medium", "high" } + elseif ib.data.value == clusters.FanControl.attributes.FanModeSequence.OFF_LOW_HIGH then + supportedFanModes = { "off", "low", "high" } + elseif ib.data.value == clusters.FanControl.attributes.FanModeSequence.OFF_LOW_MED_HIGH_AUTO then + supportedFanModes = { "off", "low", "medium", "high", "auto" } + elseif ib.data.value == clusters.FanControl.attributes.FanModeSequence.OFF_LOW_HIGH_AUTO then + supportedFanModes = { "off", "low", "high", "auto" } + elseif ib.data.value == clusters.FanControl.attributes.FanModeSequence.OFF_HIGH_AUTO then + supportedFanModes = { "off", "high", "auto" } + else + supportedFanModes = { "off", "high" } + end + + if device:supports_capability_by_id(capabilities.airPurifierFanMode.ID) then + supported_fan_modes_attribute = capabilities.airPurifierFanMode.supportedAirPurifierFanModes + elseif device:supports_capability_by_id(capabilities.airConditionerFanMode.ID) then + supported_fan_modes_attribute = capabilities.airConditionerFanMode.supportedAcFanModes + elseif device:supports_capability_by_id(capabilities.thermostatFanMode.ID) then + supported_fan_modes_attribute = capabilities.thermostatFanMode.supportedThermostatFanModes + -- Our thermostat fan mode control is not granular enough to handle all of the supported modes + if ib.data.value >= clusters.FanControl.attributes.FanModeSequence.OFF_LOW_MED_HIGH_AUTO and + ib.data.value <= clusters.FanControl.attributes.FanModeSequence.OFF_ON_AUTO then + supportedFanModes = { "auto", "on" } + else + supportedFanModes = { "on" } + end + else + supported_fan_modes_attribute = capabilities.fanMode.supportedFanModes + end + + local event = supported_fan_modes_attribute(supportedFanModes, {visibility = {displayed = false}}) + device:emit_event_for_endpoint(ib.endpoint_id, event) +end + +function AttributeHandlers.percent_current_handler(driver, device, ib, response) + local speed = 0 + if ib.data.value ~= nil then + speed = st_utils.clamp_value(ib.data.value, fields.MIN_ALLOWED_PERCENT_VALUE, fields.MAX_ALLOWED_PERCENT_VALUE) + end + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.fanSpeedPercent.percent(speed)) +end + +function AttributeHandlers.wind_support_handler(driver, device, ib, response) + local supported_wind_modes = {capabilities.windMode.windMode.noWind.NAME} + for mode, wind_mode in pairs(fields.WIND_MODE_MAP) do + if ((ib.data.value >> mode) & 1) > 0 then + table.insert(supported_wind_modes, wind_mode.NAME) + end + end + local event = capabilities.windMode.supportedWindModes(supported_wind_modes, {visibility = {displayed = false}}) + device:emit_event_for_endpoint(ib.endpoint_id, event) +end + +function AttributeHandlers.wind_setting_handler(driver, device, ib, response) + for index, wind_mode in pairs(fields.WIND_MODE_MAP) do + if ((ib.data.value >> index) & 1) > 0 then + device:emit_event_for_endpoint(ib.endpoint_id, wind_mode()) + return + end + end + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.windMode.windMode.noWind()) +end + +function AttributeHandlers.rock_support_handler(driver, device, ib, response) + local supported_rock_modes = {capabilities.fanOscillationMode.fanOscillationMode.off.NAME} + for mode, rock_mode in pairs(fields.ROCK_MODE_MAP) do + if ((ib.data.value >> mode) & 1) > 0 then + table.insert(supported_rock_modes, rock_mode.NAME) + end + end + local event = capabilities.fanOscillationMode.supportedFanOscillationModes(supported_rock_modes, {visibility = {displayed = false}}) + device:emit_event_for_endpoint(ib.endpoint_id, event) +end + +function AttributeHandlers.rock_setting_handler(driver, device, ib, response) + for index, rock_mode in pairs(fields.ROCK_MODE_MAP) do + if ((ib.data.value >> index) & 1) > 0 then + device:emit_event_for_endpoint(ib.endpoint_id, rock_mode()) + return + end + end + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.fanOscillationMode.fanOscillationMode.off()) +end + + +-- [[ HEPA FILTER MONITORING CLUSTER ATTRIBUTES ]] -- + +function AttributeHandlers.hepa_filter_condition_handler(driver, device, ib, response) + local component = device.profile.components["hepaFilter"] + local condition = ib.data.value + device:emit_component_event(component, capabilities.filterState.filterLifeRemaining(condition)) +end + +function AttributeHandlers.hepa_filter_change_indication_handler(driver, device, ib, response) + local component = device.profile.components["hepaFilter"] + if ib.data.value == clusters.HepaFilterMonitoring.attributes.ChangeIndication.OK then + device:emit_component_event(component, capabilities.filterStatus.filterStatus.normal()) + elseif ib.data.value == clusters.HepaFilterMonitoring.attributes.ChangeIndication.WARNING then + device:emit_component_event(component, capabilities.filterStatus.filterStatus.normal()) + elseif ib.data.value == clusters.HepaFilterMonitoring.attributes.ChangeIndication.CRITICAL then + device:emit_component_event(component, capabilities.filterStatus.filterStatus.replace()) + end +end + + +-- [[ ACTIVATED CARBON FILTER MONITORING CLUSTER ATTRIBUTES ]] -- + +function AttributeHandlers.activated_carbon_filter_condition_handler(driver, device, ib, response) + local component = device.profile.components["activatedCarbonFilter"] + local condition = ib.data.value + device:emit_component_event(component, capabilities.filterState.filterLifeRemaining(condition)) +end + +function AttributeHandlers.activated_carbon_filter_change_indication_handler(driver, device, ib, response) + local component = device.profile.components["activatedCarbonFilter"] + if ib.data.value == clusters.ActivatedCarbonFilterMonitoring.attributes.ChangeIndication.OK then + device:emit_component_event(component, capabilities.filterStatus.filterStatus.normal()) + elseif ib.data.value == clusters.ActivatedCarbonFilterMonitoring.attributes.ChangeIndication.WARNING then + device:emit_component_event(component, capabilities.filterStatus.filterStatus.normal()) + elseif ib.data.value == clusters.ActivatedCarbonFilterMonitoring.attributes.ChangeIndication.CRITICAL then + device:emit_component_event(component, capabilities.filterStatus.filterStatus.replace()) + end +end + + +--[[ AIR QUALITY SENSOR CLUSTER ATTRIBUTES ]] -- + +function AttributeHandlers.air_quality_handler(driver, device, ib, response) + local state = ib.data.value + if state == 0 then -- Unknown + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.airQualityHealthConcern.airQualityHealthConcern.unknown()) + elseif state == 1 then -- Good + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.airQualityHealthConcern.airQualityHealthConcern.good()) + elseif state == 2 then -- Fair + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.airQualityHealthConcern.airQualityHealthConcern.moderate()) + elseif state == 3 then -- Moderate + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.airQualityHealthConcern.airQualityHealthConcern.slightlyUnhealthy()) + elseif state == 4 then -- Poor + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.airQualityHealthConcern.airQualityHealthConcern.unhealthy()) + elseif state == 5 then -- VeryPoor + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.airQualityHealthConcern.airQualityHealthConcern.veryUnhealthy()) + elseif state == 6 then -- ExtremelyPoor + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.airQualityHealthConcern.airQualityHealthConcern.hazardous()) + end +end + + +-- [[ CONCENTRATION CLUSTER ATTRIBUTES ]] -- + +function AttributeHandlers.concentration_measurement_unit_factory(capability_name) + return function(driver, device, ib, response) + device:set_field(capability_name.."_unit", ib.data.value, {persist = true}) + end +end + +function AttributeHandlers.concentration_level_value_factory(attribute) + return function(driver, device, ib, response) + device:emit_event_for_endpoint(ib.endpoint_id, attribute(fields.level_strings[ib.data.value])) + end +end + +function AttributeHandlers.concentration_measured_value_factory(capability_name, attribute, target_unit) + return function(driver, device, ib, response) + local reporting_unit = device:get_field(capability_name.."_unit") + + if not reporting_unit then + reporting_unit = fields.unit_default[capability_name] + device:set_field(capability_name.."_unit", reporting_unit, {persist = true}) + end + + local value = nil + if reporting_unit then + value = thermostat_utils.unit_conversion(ib.data.value, reporting_unit, target_unit, capability_name) + end + + if value then + device:emit_event_for_endpoint(ib.endpoint_id, attribute({value = value, unit = fields.unit_strings[target_unit]})) + -- handle case where device profile supports both fineDustLevel and dustLevel + if capability_name == capabilities.fineDustSensor.NAME and device:supports_capability(capabilities.dustSensor) then + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.dustSensor.fineDustLevel({value = value, unit = fields.unit_strings[target_unit]})) + end + end + end +end + + +-- [[ POWER SOURCE CLUSTER ATTRIBUTES ]] -- + +function AttributeHandlers.bat_percent_remaining_handler(driver, device, ib, response) + if ib.data.value then + device:emit_event(capabilities.battery.battery(math.floor(ib.data.value / 2.0 + 0.5))) + end +end + +function AttributeHandlers.bat_charge_level_handler(driver, device, ib, response) + if ib.data.value == clusters.PowerSource.types.BatChargeLevelEnum.OK then + device:emit_event(capabilities.batteryLevel.battery.normal()) + elseif ib.data.value == clusters.PowerSource.types.BatChargeLevelEnum.WARNING then + device:emit_event(capabilities.batteryLevel.battery.warning()) + elseif ib.data.value == clusters.PowerSource.types.BatChargeLevelEnum.CRITICAL then + device:emit_event(capabilities.batteryLevel.battery.critical()) + end +end + +function AttributeHandlers.power_source_attribute_list_handler(driver, device, ib, response) + local device_cfg = require "thermostat_utils.device_configuration" + for _, attr in ipairs(ib.data.elements) do + -- mark if the device if BatPercentRemaining (Attribute ID 0x0C) or + -- BatChargeLevel (Attribute ID 0x0E) is present and try profiling. + if attr.value == 0x0C then + device:set_field(fields.profiling_data.BATTERY_SUPPORT, fields.battery_support.BATTERY_PERCENTAGE) + device_cfg.match_profile(device) + return + elseif attr.value == 0x0E then + device:set_field(fields.profiling_data.BATTERY_SUPPORT, fields.battery_support.BATTERY_LEVEL) + device_cfg.match_profile(device) + return + end + end +end + + +-- [[ ELECTRICAL POWER MEASUREMENT CLUSTER ATTRIBUTES ]] -- + +function AttributeHandlers.active_power_handler(driver, device, ib, response) + if ib.data.value then + local watt_value = ib.data.value / 1000 + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.powerMeter.power({ value = watt_value, unit = "W" })) + if type(device.register_native_capability_attr_handler) == "function" then + device:register_native_capability_attr_handler("powerMeter","power") + end + end +end + + +-- [[ ELECTRICAL ENERGY MEASUREMENT CLUSTER ATTRIBUTES ]] -- + +local function periodic_energy_imported_handler(driver, device, ib, response) + if ib.data then + if version.api < 11 then + clusters.ElectricalEnergyMeasurement.server.attributes.PeriodicEnergyImported:augment_type(ib.data) + end + local endpoint_id = string.format(ib.endpoint_id) + local energy_imported_Wh = st_utils.round(ib.data.elements.energy.value / 1000) --convert mWh to Wh + local cumulative_energy_imported = device:get_field(fields.TOTAL_CUMULATIVE_ENERGY_IMPORTED_MAP) or {} + cumulative_energy_imported[endpoint_id] = cumulative_energy_imported[endpoint_id] or 0 + cumulative_energy_imported[endpoint_id] = cumulative_energy_imported[endpoint_id] + energy_imported_Wh + device:set_field(fields.TOTAL_CUMULATIVE_ENERGY_IMPORTED_MAP, cumulative_energy_imported, { persist = true }) + local total_cumulative_energy_imported = thermostat_utils.get_total_cumulative_energy_imported(device) + device:emit_component_event(device.profile.components["main"], ib.endpoint_id, capabilities.energyMeter.energy({value = total_cumulative_energy_imported, unit = "Wh"})) + thermostat_utils.report_power_consumption_to_st_energy(device, total_cumulative_energy_imported) + end +end + +local function cumulative_energy_imported_handler(driver, device, ib, response) + if ib.data then + if version.api < 11 then + clusters.ElectricalEnergyMeasurement.server.attributes.CumulativeEnergyImported:augment_type(ib.data) + end + local endpoint_id = string.format(ib.endpoint_id) + local cumulative_energy_imported = device:get_field(fields.TOTAL_CUMULATIVE_ENERGY_IMPORTED_MAP) or {} + local cumulative_energy_imported_Wh = st_utils.round( ib.data.elements.energy.value / 1000) -- convert mWh to Wh + cumulative_energy_imported[endpoint_id] = cumulative_energy_imported_Wh + device:set_field(fields.TOTAL_CUMULATIVE_ENERGY_IMPORTED_MAP, cumulative_energy_imported, { persist = true }) + local total_cumulative_energy_imported = thermostat_utils.get_total_cumulative_energy_imported(device) + device:emit_component_event(device.profile.components["main"], capabilities.energyMeter.energy({ value = total_cumulative_energy_imported, unit = "Wh" })) + thermostat_utils.report_power_consumption_to_st_energy(device, total_cumulative_energy_imported) + end +end + +function AttributeHandlers.energy_imported_factory(is_cumulative_report) + return function(driver, device, ib, response) + if is_cumulative_report then + cumulative_energy_imported_handler(driver, device, ib, response) + elseif device:get_field(fields.CUMULATIVE_REPORTS_NOT_SUPPORTED) then + periodic_energy_imported_handler(driver, device, ib, response) + end + end +end + + +-- [[ WATER HEATER MODE CLUSTER ATTRIBUTES ]] -- + +function AttributeHandlers.water_heater_supported_modes_handler(driver, device, ib, response) + local supportWaterHeaterModes = {} + local supportWaterHeaterModesWithIdx = {} + for _, mode in ipairs(ib.data.elements) do + if version.api < 13 then + clusters.WaterHeaterMode.types.ModeOptionStruct:augment_type(mode) + end + table.insert(supportWaterHeaterModes, mode.elements.label.value) + table.insert(supportWaterHeaterModesWithIdx, {mode.elements.mode.value, mode.elements.label.value}) + end + device:set_field(fields.SUPPORTED_WATER_HEATER_MODES_WITH_IDX, supportWaterHeaterModesWithIdx, { persist = true }) + local event = capabilities.mode.supportedModes(supportWaterHeaterModes, { visibility = { displayed = false } }) + device:emit_event_for_endpoint(ib.endpoint_id, event) + event = capabilities.mode.supportedArguments(supportWaterHeaterModes, { visibility = { displayed = false } }) + device:emit_event_for_endpoint(ib.endpoint_id, event) +end + +function AttributeHandlers.water_heater_current_mode_handler(driver, device, ib, response) + device.log.info(string.format("water_heater_current_mode_handler mode: %s", ib.data.value)) + local supportWaterHeaterModesWithIdx = device:get_field(fields.SUPPORTED_WATER_HEATER_MODES_WITH_IDX) or {} + local currentMode = ib.data.value + for i, mode in ipairs(supportWaterHeaterModesWithIdx) do + if mode[1] == currentMode then + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.mode.mode(mode[2])) + break + end + end +end + + +-- [[ RELATIVE HUMIDITY MEASUREMENT CLUSTER ATTRIBUTES ]] -- + +function AttributeHandlers.relative_humidity_measured_value_handler(driver, device, ib, response) + local humidity = math.floor(ib.data.value / 100.0) + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.relativeHumidityMeasurement.humidity(humidity)) +end + + +-- [[ ON OFF CLUSTER ATTRIBUTES ]] -- + +function AttributeHandlers.on_off_handler(driver, device, ib, response) + if ib.data.value then + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.switch.switch.on()) + else + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.switch.switch.off()) + end +end + +return AttributeHandlers diff --git a/drivers/SmartThings/matter-thermostat/src/thermostat_handlers/capability_handlers.lua b/drivers/SmartThings/matter-thermostat/src/thermostat_handlers/capability_handlers.lua new file mode 100644 index 0000000000..4a32b7a700 --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/thermostat_handlers/capability_handlers.lua @@ -0,0 +1,256 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local log = require "log" +local version = require "version" +local st_utils = require "st.utils" +local clusters = require "st.matter.clusters" +local capabilities = require "st.capabilities" +local fields = require "thermostat_utils.fields" +local thermostat_utils = require "thermostat_utils.utils" + +if version.api < 10 then + clusters.HepaFilterMonitoring = require "embedded_clusters.HepaFilterMonitoring" + clusters.ActivatedCarbonFilterMonitoring = require "embedded_clusters.ActivatedCarbonFilterMonitoring" +end + +if version.api < 13 then + clusters.WaterHeaterMode = require "embedded_clusters.WaterHeaterMode" +end + +local CapabilityHandlers = {} + + +-- [[ FAN SPEED PERCENT CAPABILITY HANDLERS ]] -- + +function CapabilityHandlers.handle_fan_speed_set_percent(driver, device, cmd) + local speed = math.floor(cmd.args.percent) + device:send(clusters.FanControl.attributes.PercentSetting:write(device, thermostat_utils.component_to_endpoint(device, cmd.component, clusters.FanControl.ID), speed)) +end + + +-- [[ WIND MODE CAPABILITY HANDLERS ]] -- + +function CapabilityHandlers.handle_set_wind_mode(driver, device, cmd) + local wind_mode = 0 + if cmd.args.windMode == capabilities.windMode.windMode.sleepWind.NAME then + wind_mode = clusters.FanControl.types.WindSupportMask.SLEEP_WIND + elseif cmd.args.windMode == capabilities.windMode.windMode.naturalWind.NAME then + wind_mode = clusters.FanControl.types.WindSupportMask.NATURAL_WIND + end + device:send(clusters.FanControl.attributes.WindSetting:write(device, thermostat_utils.component_to_endpoint(device, cmd.component, clusters.FanControl.ID), wind_mode)) +end + + +-- [[ FAN OSCILLATION MODE HANDLERS ]] -- + +function CapabilityHandlers.handle_set_fan_oscillation_mode(driver, device, cmd) + local rock_mode = 0 + if cmd.args.fanOscillationMode == capabilities.fanOscillationMode.fanOscillationMode.horizontal.NAME then + rock_mode = clusters.FanControl.types.RockSupportMask.ROCK_LEFT_RIGHT + elseif cmd.args.fanOscillationMode == capabilities.fanOscillationMode.fanOscillationMode.vertical.NAME then + rock_mode = clusters.FanControl.types.RockSupportMask.ROCK_UP_DOWN + elseif cmd.args.fanOscillationMode == capabilities.fanOscillationMode.fanOscillationMode.swing.NAME then + rock_mode = clusters.FanControl.types.RockSupportMask.ROCK_ROUND + end + device:send(clusters.FanControl.attributes.RockSetting:write(device, thermostat_utils.component_to_endpoint(device, cmd.component, clusters.FanControl.ID), rock_mode)) +end + + +-- [[ MODE CAPABILITY HANDLERS ]] -- + +function CapabilityHandlers.handle_set_mode(driver, device, cmd) + device.log.info(string.format("set_water_heater_mode mode: %s", cmd.args.mode)) + local endpoint_id = thermostat_utils.component_to_endpoint(device, cmd.component, clusters.Thermostat.ID) + local supportedWaterHeaterModesWithIdx = device:get_field(fields.SUPPORTED_WATER_HEATER_MODES_WITH_IDX) or {} + for i, mode in ipairs(supportedWaterHeaterModesWithIdx) do + if cmd.args.mode == mode[2] then + device:send(clusters.WaterHeaterMode.commands.ChangeToMode(device, endpoint_id, mode[1])) + return + end + end +end + + +-- [[ FILTER STATE CAPABLITY HANDLERS ]] -- + +function CapabilityHandlers.handle_filter_state_reset_filter(driver, device, cmd) + local endpoint_id = device:component_to_endpoint(cmd.component) + if cmd.component == "hepaFilter" then + device:send(clusters.HepaFilterMonitoring.server.commands.ResetCondition(device, endpoint_id)) + else + device:send(clusters.ActivatedCarbonFilterMonitoring.server.commands.ResetCondition(device, endpoint_id)) + end +end + + +-- [[ SWITCH CAPABLITY HANDLERS ]] -- + +function CapabilityHandlers.handle_switch_on(driver, device, cmd) + local endpoint_id = thermostat_utils.component_to_endpoint(device, cmd.component, clusters.OnOff.ID) + local req = clusters.OnOff.server.commands.On(device, endpoint_id) + device:send(req) +end + +function CapabilityHandlers.handle_switch_off(driver, device, cmd) + local endpoint_id = thermostat_utils.component_to_endpoint(device, cmd.component, clusters.OnOff.ID) + local req = clusters.OnOff.server.commands.Off(device, endpoint_id) + device:send(req) +end + + +-- [[ THERMOSTAT MODE CAPABLITY HANDLERS ]] -- + +function CapabilityHandlers.handle_set_thermostat_mode(driver, device, cmd) + local mode_id = nil + for value, mode in pairs(fields.THERMOSTAT_MODE_MAP) do + if mode.NAME == cmd.args.mode then + mode_id = value + break + end + end + if mode_id then + device:send(clusters.Thermostat.attributes.SystemMode:write(device, thermostat_utils.component_to_endpoint(device, cmd.component, clusters.Thermostat.ID), mode_id)) + end +end + +function CapabilityHandlers.thermostat_mode_command_factory(mode_name) + return function(driver, device, cmd) + return CapabilityHandlers.handle_set_thermostat_mode(driver, device, {component = cmd.component, args = {mode = mode_name}}) + end +end + + +-- [[ FAN MODE CAPABILITY HANDLERS ]] -- + +local function set_fan_mode(device, cmd, fan_mode_capability) + local command_argument = cmd.args.fanMode + if fan_mode_capability == capabilities.airPurifierFanMode then + command_argument = cmd.args.airPurifierFanMode + elseif fan_mode_capability == capabilities.thermostatFanMode then + command_argument = cmd.args.mode + end + local fan_mode_id + if command_argument == "off" then + fan_mode_id = clusters.FanControl.attributes.FanMode.OFF + elseif command_argument == "on" then + fan_mode_id = clusters.FanControl.attributes.FanMode.ON + elseif command_argument == "auto" then + fan_mode_id = clusters.FanControl.attributes.FanMode.AUTO + elseif command_argument == "high" then + fan_mode_id = clusters.FanControl.attributes.FanMode.HIGH + elseif command_argument == "medium" then + fan_mode_id = clusters.FanControl.attributes.FanMode.MEDIUM + elseif thermostat_utils.tbl_contains({ "low", "sleep", "quiet", "windFree" }, command_argument) then + fan_mode_id = clusters.FanControl.attributes.FanMode.LOW + else + device.log.warn(string.format("Invalid Fan Mode (%s) received from capability command", command_argument)) + return + end + device:send(clusters.FanControl.attributes.FanMode:write(device, thermostat_utils.component_to_endpoint(device, cmd.component, clusters.FanControl.ID), fan_mode_id)) +end + +function CapabilityHandlers.fan_mode_command_factory(fan_mode_capability) + return function(driver, device, cmd) + set_fan_mode(device, cmd, fan_mode_capability) + end +end + + +-- [[ THERMOSTAT FAN MODE CAPABILITY HANDLERS ]] -- + +function CapabilityHandlers.thermostat_fan_mode_command_factory(mode_name) + return function(driver, device, cmd) + set_fan_mode(device, {component = cmd.component, args = {mode = mode_name}}, capabilities.thermostatFanMode) + end +end + + +-- [[ THERMOSTAT HEATING/COOLING CAPABILITY HANDLERS ]] -- + +function CapabilityHandlers.thermostat_set_setpoint_factory(setpoint) + return function(driver, device, cmd) + local endpoint_id = thermostat_utils.component_to_endpoint(device, cmd.component, clusters.Thermostat.ID) + local MAX_TEMP_IN_C = fields.THERMOSTAT_MAX_TEMP_IN_C + local MIN_TEMP_IN_C = fields.THERMOSTAT_MIN_TEMP_IN_C + local is_water_heater_device = thermostat_utils.get_device_type(device) == fields.WATER_HEATER_DEVICE_TYPE_ID + if is_water_heater_device then + MAX_TEMP_IN_C = fields.WATER_HEATER_MAX_TEMP_IN_C + MIN_TEMP_IN_C = fields.WATER_HEATER_MIN_TEMP_IN_C + end + local value = cmd.args.setpoint + if version.rpc <= 5 and value > MAX_TEMP_IN_C then + value = st_utils.f_to_c(value) + end + + -- Gather cached setpoint values when considering setpoint limits + -- Note: cached values should always exist, but defaults are chosen just in case to prevent + -- nil operation errors, and deadband logic from triggering. + local cached_cooling_val, cooling_setpoint = device:get_latest_state( + cmd.component, capabilities.thermostatCoolingSetpoint.ID, + capabilities.thermostatCoolingSetpoint.coolingSetpoint.NAME, + MAX_TEMP_IN_C, { value = MAX_TEMP_IN_C, unit = "C" } + ) + if cooling_setpoint and cooling_setpoint.unit == "F" then + cached_cooling_val = st_utils.f_to_c(cached_cooling_val) + end + local cached_heating_val, heating_setpoint = device:get_latest_state( + cmd.component, capabilities.thermostatHeatingSetpoint.ID, + capabilities.thermostatHeatingSetpoint.heatingSetpoint.NAME, + MIN_TEMP_IN_C, { value = MIN_TEMP_IN_C, unit = "C" } + ) + if heating_setpoint and heating_setpoint.unit == "F" then + cached_heating_val = st_utils.f_to_c(cached_heating_val) + end + local is_auto_capable = #device:get_endpoints( + clusters.Thermostat.ID, + {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.AUTOMODE} + ) > 0 + + --Check setpoint limits for the device + local setpoint_type = string.match(setpoint.NAME, "Heat") or "Cool" + local deadband = device:get_field(fields.setpoint_limit_device_field.MIN_DEADBAND) or 2.5 --spec default + if setpoint_type == "Heat" then + local min = device:get_field(fields.setpoint_limit_device_field.MIN_HEAT) or MIN_TEMP_IN_C + local max = device:get_field(fields.setpoint_limit_device_field.MAX_HEAT) or MAX_TEMP_IN_C + if value < min or value > max then + log.warn(string.format( + "Invalid setpoint (%s) outside the min (%s) and the max (%s)", + value, min, max + )) + device:emit_event_for_endpoint(endpoint_id, capabilities.thermostatHeatingSetpoint.heatingSetpoint(heating_setpoint, {state_change = true})) + return + end + if is_auto_capable and value > (cached_cooling_val - deadband) then + log.warn(string.format( + "Invalid setpoint (%s) is greater than the cooling setpoint (%s) with the deadband (%s)", + value, cooling_setpoint, deadband + )) + device:emit_event_for_endpoint(endpoint_id, capabilities.thermostatHeatingSetpoint.heatingSetpoint(heating_setpoint, {state_change = true})) + return + end + else + local min = device:get_field(fields.setpoint_limit_device_field.MIN_COOL) or MIN_TEMP_IN_C + local max = device:get_field(fields.setpoint_limit_device_field.MAX_COOL) or MAX_TEMP_IN_C + if value < min or value > max then + log.warn(string.format( + "Invalid setpoint (%s) outside the min (%s) and the max (%s)", + value, min, max + )) + device:emit_event_for_endpoint(endpoint_id, capabilities.thermostatCoolingSetpoint.coolingSetpoint(cooling_setpoint, {state_change = true})) + return + end + if is_auto_capable and value < (cached_heating_val + deadband) then + log.warn(string.format( + "Invalid setpoint (%s) is less than the heating setpoint (%s) with the deadband (%s)", + value, heating_setpoint, deadband + )) + device:emit_event_for_endpoint(endpoint_id, capabilities.thermostatCoolingSetpoint.coolingSetpoint(cooling_setpoint, {state_change = true})) + return + end + end + device:send(setpoint:write(device, thermostat_utils.component_to_endpoint(device, cmd.component, clusters.Thermostat.ID), st_utils.round(value * 100.0))) + end +end + +return CapabilityHandlers diff --git a/drivers/SmartThings/matter-thermostat/src/thermostat_utils/device_configuration.lua b/drivers/SmartThings/matter-thermostat/src/thermostat_utils/device_configuration.lua new file mode 100644 index 0000000000..102b6e8d3b --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/thermostat_utils/device_configuration.lua @@ -0,0 +1,324 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local capabilities = require "st.capabilities" +local clusters = require "st.matter.clusters" +local embedded_cluster_utils = require "thermostat_utils.embedded_cluster_utils" +local version = require "version" +local fields = require "thermostat_utils.fields" +local thermostat_utils = require "thermostat_utils.utils" + +if version.api < 10 then + clusters.HepaFilterMonitoring = require "embedded_clusters.HepaFilterMonitoring" + clusters.ActivatedCarbonFilterMonitoring = require "embedded_clusters.ActivatedCarbonFilterMonitoring" +end + +if version.api < 11 then + clusters.ElectricalEnergyMeasurement = require "embedded_clusters.ElectricalEnergyMeasurement" +end + +if version.api < 13 then + clusters.WaterHeaterMode = require "embedded_clusters.WaterHeaterMode" +end + +local DeviceConfigurationHelpers = {} + +function DeviceConfigurationHelpers.supported_level_measurements(device) + local measurement_caps, level_caps = {}, {} + for _, details in ipairs(fields.AIR_QUALITY_MAP) do + local cap_id = details[1] + local cluster = details[3] + -- capability describes either a HealthConcern or Measurement/Sensor + if (cap_id:match("HealthConcern$")) then + local attr_eps = embedded_cluster_utils.get_endpoints(device, cluster.ID, { feature_bitmap = cluster.types.Feature.LEVEL_INDICATION }) + if #attr_eps > 0 then + table.insert(level_caps, cap_id) + end + elseif (cap_id:match("Measurement$") or cap_id:match("Sensor$")) then + local attr_eps = embedded_cluster_utils.get_endpoints(device, cluster.ID, { feature_bitmap = cluster.types.Feature.NUMERIC_MEASUREMENT }) + if #attr_eps > 0 then + table.insert(measurement_caps, cap_id) + end + end + end + return measurement_caps, level_caps +end + +function DeviceConfigurationHelpers.get_thermostat_optional_capabilities(device) + local heat_eps = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.HEATING}) + local cool_eps = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.COOLING}) + local running_state_supported = device:get_field(fields.profiling_data.THERMOSTAT_RUNNING_STATE_SUPPORT) + + local supported_thermostat_capabilities = {} + + if #heat_eps > 0 then + table.insert(supported_thermostat_capabilities, capabilities.thermostatHeatingSetpoint.ID) + end + if #cool_eps > 0 then + table.insert(supported_thermostat_capabilities, capabilities.thermostatCoolingSetpoint.ID) + end + + if running_state_supported then + table.insert(supported_thermostat_capabilities, capabilities.thermostatOperatingState.ID) + end + + return supported_thermostat_capabilities +end + +function DeviceConfigurationHelpers.get_air_quality_optional_capabilities(device) + local supported_air_quality_capabilities = {} + + local measurement_caps, level_caps = DeviceConfigurationHelpers.supported_level_measurements(device) + + for _, cap_id in ipairs(measurement_caps) do + table.insert(supported_air_quality_capabilities, cap_id) + end + + for _, cap_id in ipairs(level_caps) do + table.insert(supported_air_quality_capabilities, cap_id) + end + + return supported_air_quality_capabilities +end + + +local DeviceConfiguration = {} + +function DeviceConfiguration.match_modular_profile_air_purifer(device) + local optional_supported_component_capabilities = {} + local main_component_capabilities = {} + local hepa_filter_component_capabilities = {} + local ac_filter_component_capabilties = {} + local profile_name = "air-purifier-modular" + + local MAIN_COMPONENT_IDX = 1 + local CAPABILITIES_LIST_IDX = 2 + + local humidity_eps = device:get_endpoints(clusters.RelativeHumidityMeasurement.ID) + local temp_eps = embedded_cluster_utils.get_endpoints(device, clusters.TemperatureMeasurement.ID) + if #humidity_eps > 0 then + table.insert(main_component_capabilities, capabilities.relativeHumidityMeasurement.ID) + end + if #temp_eps > 0 then + table.insert(main_component_capabilities, capabilities.temperatureMeasurement.ID) + end + + local hepa_filter_eps = embedded_cluster_utils.get_endpoints(device, clusters.HepaFilterMonitoring.ID) + local ac_filter_eps = embedded_cluster_utils.get_endpoints(device, clusters.ActivatedCarbonFilterMonitoring.ID) + + if #hepa_filter_eps > 0 then + local filter_state_eps = embedded_cluster_utils.get_endpoints(device, clusters.HepaFilterMonitoring.ID, {feature_bitmap = clusters.HepaFilterMonitoring.types.Feature.CONDITION}) + if #filter_state_eps > 0 then + table.insert(hepa_filter_component_capabilities, capabilities.filterState.ID) + end + + table.insert(hepa_filter_component_capabilities, capabilities.filterStatus.ID) + end + if #ac_filter_eps > 0 then + local filter_state_eps = embedded_cluster_utils.get_endpoints(device, clusters.ActivatedCarbonFilterMonitoring.ID, {feature_bitmap = clusters.ActivatedCarbonFilterMonitoring.types.Feature.CONDITION}) + if #filter_state_eps > 0 then + table.insert(ac_filter_component_capabilties, capabilities.filterState.ID) + end + + table.insert(ac_filter_component_capabilties, capabilities.filterStatus.ID) + end + + -- determine fan capabilities, note that airPurifierFanMode is already mandatory + local rock_eps = device:get_endpoints(clusters.FanControl.ID, {feature_bitmap = clusters.FanControl.types.Feature.ROCKING}) + local wind_eps = device:get_endpoints(clusters.FanControl.ID, {feature_bitmap = clusters.FanControl.types.FanControlFeature.WIND}) + + if #rock_eps > 0 then + table.insert(main_component_capabilities, capabilities.fanOscillationMode.ID) + end + if #wind_eps > 0 then + table.insert(main_component_capabilities, capabilities.windMode.ID) + end + + local thermostat_eps = device:get_endpoints(clusters.Thermostat.ID) + + if #thermostat_eps > 0 then + -- thermostatMode and temperatureMeasurement should be expected if thermostat is present + table.insert(main_component_capabilities, capabilities.thermostatMode.ID) + + -- only add temperatureMeasurement if it is not already added via TemperatureMeasurement cluster support + if #temp_eps == 0 then + table.insert(main_component_capabilities, capabilities.temperatureMeasurement.ID) + end + local thermostat_capabilities = DeviceConfigurationHelpers.get_thermostat_optional_capabilities(device) + for _, capability_id in pairs(thermostat_capabilities) do + table.insert(main_component_capabilities, capability_id) + end + end + + local aqs_eps = embedded_cluster_utils.get_endpoints(device, clusters.AirQuality.ID) + if #aqs_eps > 0 then + table.insert(main_component_capabilities, capabilities.airQualityHealthConcern.ID) + end + + local supported_air_quality_capabilities = DeviceConfigurationHelpers.get_air_quality_optional_capabilities(device) + for _, capability_id in pairs(supported_air_quality_capabilities) do + table.insert(main_component_capabilities, capability_id) + end + + table.insert(optional_supported_component_capabilities, {"main", main_component_capabilities}) + if #ac_filter_component_capabilties > 0 then + table.insert(optional_supported_component_capabilities, {"activatedCarbonFilter", ac_filter_component_capabilties}) + end + if #hepa_filter_component_capabilities > 0 then + table.insert(optional_supported_component_capabilities, {"hepaFilter", hepa_filter_component_capabilities}) + end + + device:try_update_metadata({profile = profile_name, optional_component_capabilities = optional_supported_component_capabilities}) + + -- earlier modular profile gating (min api v14, rpc 8) ensures we are running >= 0.57 FW. + -- This gating specifies a workaround required only for 0.57 FW, which is not needed for 0.58 and higher. + if version.api < 15 or version.rpc < 9 then + -- add mandatory capabilities for subscription + local total_supported_capabilities = optional_supported_component_capabilities + table.insert(total_supported_capabilities[MAIN_COMPONENT_IDX][CAPABILITIES_LIST_IDX], capabilities.airPurifierFanMode.ID) + table.insert(total_supported_capabilities[MAIN_COMPONENT_IDX][CAPABILITIES_LIST_IDX], capabilities.fanSpeedPercent.ID) + table.insert(total_supported_capabilities[MAIN_COMPONENT_IDX][CAPABILITIES_LIST_IDX], capabilities.refresh.ID) + table.insert(total_supported_capabilities[MAIN_COMPONENT_IDX][CAPABILITIES_LIST_IDX], capabilities.firmwareUpdate.ID) + + device:set_field(fields.SUPPORTED_COMPONENT_CAPABILITIES, total_supported_capabilities, { persist = true }) + end +end + +function DeviceConfiguration.match_modular_profile_thermostat(device) + local optional_supported_component_capabilities = {} + local main_component_capabilities = {} + local profile_name = "thermostat-modular" + + local humidity_eps = device:get_endpoints(clusters.RelativeHumidityMeasurement.ID) + if #humidity_eps > 0 then + table.insert(main_component_capabilities, capabilities.relativeHumidityMeasurement.ID) + end + + -- determine fan capabilities + local fan_eps = device:get_endpoints(clusters.FanControl.ID) + local rock_eps = device:get_endpoints(clusters.FanControl.ID, {feature_bitmap = clusters.FanControl.types.Feature.ROCKING}) + local wind_eps = device:get_endpoints(clusters.FanControl.ID, {feature_bitmap = clusters.FanControl.types.FanControlFeature.WIND}) + + if #fan_eps > 0 then + table.insert(main_component_capabilities, capabilities.fanMode.ID) + end + if #rock_eps > 0 then + table.insert(main_component_capabilities, capabilities.fanOscillationMode.ID) + end + if #wind_eps > 0 then + table.insert(main_component_capabilities, capabilities.windMode.ID) + end + + local thermostat_capabilities = DeviceConfigurationHelpers.get_thermostat_optional_capabilities(device) + for _, capability_id in pairs(thermostat_capabilities) do + table.insert(main_component_capabilities, capability_id) + end + + local battery_supported = device:get_field(fields.profiling_data.BATTERY_SUPPORT) + if battery_supported == fields.battery_support.BATTERY_LEVEL then + table.insert(main_component_capabilities, capabilities.batteryLevel.ID) + elseif battery_supported == fields.battery_support.BATTERY_PERCENTAGE then + table.insert(main_component_capabilities, capabilities.battery.ID) + end + + table.insert(optional_supported_component_capabilities, {"main", main_component_capabilities}) + device:try_update_metadata({profile = profile_name, optional_component_capabilities = optional_supported_component_capabilities}) + + -- earlier modular profile gating (min api v14, rpc 8) ensures we are running >= 0.57 FW. + -- This gating specifies a workaround required only for 0.57 FW, which is not needed for 0.58 and higher. + if version.api < 15 or version.rpc < 9 then + -- add mandatory capabilities for subscription + local total_supported_capabilities = optional_supported_component_capabilities + table.insert(main_component_capabilities, capabilities.thermostatMode.ID) + table.insert(main_component_capabilities, capabilities.temperatureMeasurement.ID) + table.insert(main_component_capabilities, capabilities.refresh.ID) + table.insert(main_component_capabilities, capabilities.firmwareUpdate.ID) + + device:set_field(fields.SUPPORTED_COMPONENT_CAPABILITIES, total_supported_capabilities, { persist = true }) + end +end + +function DeviceConfiguration.match_modular_profile_room_ac(device) + local running_state_supported = device:get_field(fields.profiling_data.THERMOSTAT_RUNNING_STATE_SUPPORT) + local humidity_eps = device:get_endpoints(clusters.RelativeHumidityMeasurement.ID) + local optional_supported_component_capabilities = {} + local main_component_capabilities = {} + local profile_name = "room-air-conditioner-modular" + + if #humidity_eps > 0 then + table.insert(main_component_capabilities, capabilities.relativeHumidityMeasurement.ID) + end + + -- determine fan capabilities + local fan_eps = device:get_endpoints(clusters.FanControl.ID) + local wind_eps = device:get_endpoints(clusters.FanControl.ID, {feature_bitmap = clusters.FanControl.types.FanControlFeature.WIND}) + -- Note: Room AC does not support the rocking feature of FanControl. + + if #fan_eps > 0 then + table.insert(main_component_capabilities, capabilities.airConditionerFanMode.ID) + table.insert(main_component_capabilities, capabilities.fanSpeedPercent.ID) + end + if #wind_eps > 0 then + table.insert(main_component_capabilities, capabilities.windMode.ID) + end + + local heat_eps = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.HEATING}) + local cool_eps = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.COOLING}) + + if #heat_eps > 0 then + table.insert(main_component_capabilities, capabilities.thermostatHeatingSetpoint.ID) + end + if #cool_eps > 0 then + table.insert(main_component_capabilities, capabilities.thermostatCoolingSetpoint.ID) + end + + if running_state_supported then + table.insert(main_component_capabilities, capabilities.thermostatOperatingState.ID) + end + + table.insert(optional_supported_component_capabilities, {"main", main_component_capabilities}) + device:try_update_metadata({profile = profile_name, optional_component_capabilities = optional_supported_component_capabilities}) + + -- earlier modular profile gating (min api v14, rpc 8) ensures we are running >= 0.57 FW. + -- This gating specifies a workaround required only for 0.57 FW, which is not needed for 0.58 and higher. + if version.api < 15 or version.rpc < 9 then + -- add mandatory capabilities for subscription + local total_supported_capabilities = optional_supported_component_capabilities + table.insert(main_component_capabilities, capabilities.switch.ID) + table.insert(main_component_capabilities, capabilities.temperatureMeasurement.ID) + table.insert(main_component_capabilities, capabilities.thermostatMode.ID) + table.insert(main_component_capabilities, capabilities.refresh.ID) + table.insert(main_component_capabilities, capabilities.firmwareUpdate.ID) + + device:set_field(fields.SUPPORTED_COMPONENT_CAPABILITIES, total_supported_capabilities, { persist = true }) + end +end + +local match_modular_device_type = { + [fields.AP_DEVICE_TYPE_ID] = DeviceConfiguration.match_modular_profile_air_purifer, + [fields.RAC_DEVICE_TYPE_ID] = DeviceConfiguration.match_modular_profile_room_ac, + [fields.THERMOSTAT_DEVICE_TYPE_ID] = DeviceConfiguration.match_modular_profile_thermostat, +} + +local function profiling_data_still_required(device) + for _, field in pairs(fields.profiling_data) do + if device:get_field(field) == nil then + return true -- data still required if a field is nil + end + end + return false +end + +function DeviceConfiguration.match_profile(device) + if profiling_data_still_required(device) then return end + local primary_device_type = thermostat_utils.get_device_type(device) + if version.api >= 14 and version.rpc >= 8 and match_modular_device_type[primary_device_type] then + match_modular_device_type[primary_device_type](device) + return + else + local legacy_device_cfg = require "thermostat_utils.legacy_device_configuration" + legacy_device_cfg.match_profile(device) + end +end + +return DeviceConfiguration diff --git a/drivers/SmartThings/matter-thermostat/src/embedded-cluster-utils.lua b/drivers/SmartThings/matter-thermostat/src/thermostat_utils/embedded_cluster_utils.lua similarity index 67% rename from drivers/SmartThings/matter-thermostat/src/embedded-cluster-utils.lua rename to drivers/SmartThings/matter-thermostat/src/thermostat_utils/embedded_cluster_utils.lua index 3c38ef55d0..3368fe2fa4 100644 --- a/drivers/SmartThings/matter-thermostat/src/embedded-cluster-utils.lua +++ b/drivers/SmartThings/matter-thermostat/src/thermostat_utils/embedded_cluster_utils.lua @@ -1,31 +1,32 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 +local version = require "version" local clusters = require "st.matter.clusters" local utils = require "st.utils" --- Include driver-side definitions when lua libs api version is < 10 -local version = require "version" if version.api < 10 then - clusters.HepaFilterMonitoring = require "HepaFilterMonitoring" - clusters.ActivatedCarbonFilterMonitoring = require "ActivatedCarbonFilterMonitoring" - clusters.AirQuality = require "AirQuality" - clusters.CarbonMonoxideConcentrationMeasurement = require "CarbonMonoxideConcentrationMeasurement" - clusters.CarbonDioxideConcentrationMeasurement = require "CarbonDioxideConcentrationMeasurement" - clusters.FormaldehydeConcentrationMeasurement = require "FormaldehydeConcentrationMeasurement" - clusters.NitrogenDioxideConcentrationMeasurement = require "NitrogenDioxideConcentrationMeasurement" - clusters.OzoneConcentrationMeasurement = require "OzoneConcentrationMeasurement" - clusters.Pm1ConcentrationMeasurement = require "Pm1ConcentrationMeasurement" - clusters.Pm10ConcentrationMeasurement = require "Pm10ConcentrationMeasurement" - clusters.Pm25ConcentrationMeasurement = require "Pm25ConcentrationMeasurement" - clusters.RadonConcentrationMeasurement = require "RadonConcentrationMeasurement" - clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement = require "TotalVolatileOrganicCompoundsConcentrationMeasurement" + clusters.HepaFilterMonitoring = require "embedded_clusters.HepaFilterMonitoring" + clusters.ActivatedCarbonFilterMonitoring = require "embedded_clusters.ActivatedCarbonFilterMonitoring" + clusters.AirQuality = require "embedded_clusters.AirQuality" + clusters.CarbonMonoxideConcentrationMeasurement = require "embedded_clusters.CarbonMonoxideConcentrationMeasurement" + clusters.CarbonDioxideConcentrationMeasurement = require "embedded_clusters.CarbonDioxideConcentrationMeasurement" + clusters.FormaldehydeConcentrationMeasurement = require "embedded_clusters.FormaldehydeConcentrationMeasurement" + clusters.NitrogenDioxideConcentrationMeasurement = require "embedded_clusters.NitrogenDioxideConcentrationMeasurement" + clusters.OzoneConcentrationMeasurement = require "embedded_clusters.OzoneConcentrationMeasurement" + clusters.Pm1ConcentrationMeasurement = require "embedded_clusters.Pm1ConcentrationMeasurement" + clusters.Pm10ConcentrationMeasurement = require "embedded_clusters.Pm10ConcentrationMeasurement" + clusters.Pm25ConcentrationMeasurement = require "embedded_clusters.Pm25ConcentrationMeasurement" + clusters.RadonConcentrationMeasurement = require "embedded_clusters.RadonConcentrationMeasurement" + clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement = require "embedded_clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement" end if version.api < 11 then - clusters.ElectricalEnergyMeasurement = require "ElectricalEnergyMeasurement" - clusters.ElectricalPowerMeasurement = require "ElectricalPowerMeasurement" + clusters.ElectricalEnergyMeasurement = require "embedded_clusters.ElectricalEnergyMeasurement" + clusters.ElectricalPowerMeasurement = require "embedded_clusters.ElectricalPowerMeasurement" end if version.api < 13 then - clusters.WaterHeaterMode = require "WaterHeaterMode" + clusters.WaterHeaterMode = require "embedded_clusters.WaterHeaterMode" end local embedded_cluster_utils = {} diff --git a/drivers/SmartThings/matter-thermostat/src/thermostat_utils/fields.lua b/drivers/SmartThings/matter-thermostat/src/thermostat_utils/fields.lua new file mode 100644 index 0000000000..770fd7d65e --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/thermostat_utils/fields.lua @@ -0,0 +1,238 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local version = require "version" +local capabilities = require "st.capabilities" +local clusters = require "st.matter.clusters" +local st_utils = require "st.utils" + +if version.api < 10 then + clusters.CarbonDioxideConcentrationMeasurement = require "embedded_clusters.CarbonDioxideConcentrationMeasurement" + clusters.CarbonMonoxideConcentrationMeasurement = require "embedded_clusters.CarbonMonoxideConcentrationMeasurement" + clusters.Pm10ConcentrationMeasurement = require "embedded_clusters.Pm10ConcentrationMeasurement" + clusters.Pm25ConcentrationMeasurement = require "embedded_clusters.Pm25ConcentrationMeasurement" + clusters.FormaldehydeConcentrationMeasurement = require "embedded_clusters.FormaldehydeConcentrationMeasurement" + clusters.NitrogenDioxideConcentrationMeasurement = require "embedded_clusters.NitrogenDioxideConcentrationMeasurement" + clusters.OzoneConcentrationMeasurement = require "embedded_clusters.OzoneConcentrationMeasurement" + clusters.RadonConcentrationMeasurement = require "embedded_clusters.RadonConcentrationMeasurement" + clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement = require "embedded_clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement" + clusters.Pm1ConcentrationMeasurement = require "embedded_clusters.Pm1ConcentrationMeasurement" + clusters.Thermostat.types.ThermostatSystemMode.DRY = 0x8 -- ThermostatSystemMode added in Matter 1.2 + clusters.Thermostat.types.ThermostatSystemMode.SLEEP = 0x9 -- ThermostatSystemMode added in Matter 1.2 +end + +local ThermostatFields = {} + +ThermostatFields.SUPPORTED_COMPONENT_CAPABILITIES = "__supported_component_capabilities" + +ThermostatFields.SAVED_SYSTEM_MODE_IB = "__saved_system_mode_ib" +ThermostatFields.DISALLOWED_THERMOSTAT_MODES = "__DISALLOWED_CONTROL_OPERATIONS" +ThermostatFields.OPTIONAL_THERMOSTAT_MODES_SEEN = "__OPTIONAL_THERMOSTAT_MODES_SEEN" + +ThermostatFields.RAC_DEVICE_TYPE_ID = 0x0072 +ThermostatFields.AP_DEVICE_TYPE_ID = 0x002D +ThermostatFields.FAN_DEVICE_TYPE_ID = 0x002B +ThermostatFields.WATER_HEATER_DEVICE_TYPE_ID = 0x050F +ThermostatFields.HEAT_PUMP_DEVICE_TYPE_ID = 0x0309 +ThermostatFields.THERMOSTAT_DEVICE_TYPE_ID = 0x0301 +ThermostatFields.ELECTRICAL_SENSOR_DEVICE_TYPE_ID = 0x0510 + +ThermostatFields.MIN_ALLOWED_PERCENT_VALUE = 0 +ThermostatFields.MAX_ALLOWED_PERCENT_VALUE = 100 +ThermostatFields.CUMULATIVE_REPORTS_NOT_SUPPORTED = "__cumulative_reports_not_supported" +ThermostatFields.LAST_IMPORTED_REPORT_TIMESTAMP = "__last_imported_report_timestamp" +ThermostatFields.MINIMUM_ST_ENERGY_REPORT_INTERVAL = (15 * 60) -- 15 minutes, reported in seconds +ThermostatFields.TOTAL_CUMULATIVE_ENERGY_IMPORTED_MAP = "__total_cumulative_energy_imported_map" +ThermostatFields.SUPPORTED_WATER_HEATER_MODES_WITH_IDX = "__supported_water_heater_modes_with_idx" +ThermostatFields.COMPONENT_TO_ENDPOINT_MAP = "__component_to_endpoint_map" +ThermostatFields.MGM3_PPM_CONVERSION_FACTOR = 24.45 + +-- For RPC version < 6: +-- issue context: driver cannot know a setpoint capability's unit (whether Celsius or Farenheit) +-- when a command is received, as the received arguments do not contain the unit. +-- workaround: map the following temperature ranges to either Celsius or Farenheit: +-- For Thermostats: +-- 1. if the received setpoint command value is in the range 5 ~ 40, it is inferred as *C +-- 2. if the received setpoint command value is in the range 41 ~ 104, it is inferred as *F +-- For Water Heaters: +-- 1. if the received setpoint command value is in the range 30 ~ 80, it is inferred as *C +-- 2. if the received setpoint command value is in the range 86 ~ 176, it is inferred as *F +-- For RPC version >= 6: +-- temperatureSetpoint always reports in Celsius, removing the need for the above workaround. +-- In this case, we use these fields simply to limit the setpoint's range to "reasonable" values on the platform. +ThermostatFields.THERMOSTAT_MAX_TEMP_IN_C = version.rpc >= 6 and 100.0 or 40.0 +ThermostatFields.THERMOSTAT_MIN_TEMP_IN_C = version.rpc >= 6 and 0.0 or 5.0 +ThermostatFields.WATER_HEATER_MAX_TEMP_IN_C = version.rpc >= 6 and 100.0 or 80.0 +ThermostatFields.WATER_HEATER_MIN_TEMP_IN_C = version.rpc >= 6 and 0.0 or 30.0 + +ThermostatFields.setpoint_limit_device_field = { + MIN_SETPOINT_DEADBAND_CHECKED = "MIN_SETPOINT_DEADBAND_CHECKED", + MIN_HEAT = "MIN_HEAT", + MAX_HEAT = "MAX_HEAT", + MIN_COOL = "MIN_COOL", + MAX_COOL = "MAX_COOL", + MIN_DEADBAND = "MIN_DEADBAND", + MIN_TEMP = "MIN_TEMP", + MAX_TEMP = "MAX_TEMP" +} + +ThermostatFields.battery_support = { + NO_BATTERY = "NO_BATTERY", + BATTERY_LEVEL = "BATTERY_LEVEL", + BATTERY_PERCENTAGE = "BATTERY_PERCENTAGE" +} + +ThermostatFields.profiling_data = { + BATTERY_SUPPORT = "__BATTERY_SUPPORT", + THERMOSTAT_RUNNING_STATE_SUPPORT = "__THERMOSTAT_RUNNING_STATE_SUPPORT" +} + +ThermostatFields.THERMOSTAT_MODE_MAP = { + [clusters.Thermostat.types.ThermostatSystemMode.OFF] = capabilities.thermostatMode.thermostatMode.off, + [clusters.Thermostat.types.ThermostatSystemMode.AUTO] = capabilities.thermostatMode.thermostatMode.auto, + [clusters.Thermostat.types.ThermostatSystemMode.COOL] = capabilities.thermostatMode.thermostatMode.cool, + [clusters.Thermostat.types.ThermostatSystemMode.HEAT] = capabilities.thermostatMode.thermostatMode.heat, + [clusters.Thermostat.types.ThermostatSystemMode.EMERGENCY_HEATING] = capabilities.thermostatMode.thermostatMode.emergency_heat, + [clusters.Thermostat.types.ThermostatSystemMode.PRECOOLING] = capabilities.thermostatMode.thermostatMode.precooling, + [clusters.Thermostat.types.ThermostatSystemMode.FAN_ONLY] = capabilities.thermostatMode.thermostatMode.fanonly, + [clusters.Thermostat.types.ThermostatSystemMode.DRY] = capabilities.thermostatMode.thermostatMode.dryair, + [clusters.Thermostat.types.ThermostatSystemMode.SLEEP] = capabilities.thermostatMode.thermostatMode.asleep, +} + +ThermostatFields.THERMOSTAT_OPERATING_MODE_MAP = { + [0] = capabilities.thermostatOperatingState.thermostatOperatingState.heating, + [1] = capabilities.thermostatOperatingState.thermostatOperatingState.cooling, + [2] = capabilities.thermostatOperatingState.thermostatOperatingState.fan_only, + [3] = capabilities.thermostatOperatingState.thermostatOperatingState.heating, + [4] = capabilities.thermostatOperatingState.thermostatOperatingState.cooling, + [5] = capabilities.thermostatOperatingState.thermostatOperatingState.fan_only, + [6] = capabilities.thermostatOperatingState.thermostatOperatingState.fan_only, +} + +ThermostatFields.WIND_MODE_MAP = { + [0] = capabilities.windMode.windMode.sleepWind, + [1] = capabilities.windMode.windMode.naturalWind +} + +ThermostatFields.ROCK_MODE_MAP = { + [0] = capabilities.fanOscillationMode.fanOscillationMode.horizontal, + [1] = capabilities.fanOscillationMode.fanOscillationMode.vertical, + [2] = capabilities.fanOscillationMode.fanOscillationMode.swing +} + +ThermostatFields.AIR_QUALITY_MAP = { + {capabilities.carbonDioxideMeasurement.ID, "-co2", clusters.CarbonDioxideConcentrationMeasurement}, + {capabilities.carbonDioxideHealthConcern.ID, "-co2", clusters.CarbonDioxideConcentrationMeasurement}, + {capabilities.carbonMonoxideMeasurement.ID, "-co", clusters.CarbonMonoxideConcentrationMeasurement}, + {capabilities.carbonMonoxideHealthConcern.ID, "-co", clusters.CarbonMonoxideConcentrationMeasurement}, + {capabilities.dustSensor.ID, "-pm10", clusters.Pm10ConcentrationMeasurement}, + {capabilities.dustHealthConcern.ID, "-pm10", clusters.Pm10ConcentrationMeasurement}, + {capabilities.fineDustSensor.ID, "-pm25", clusters.Pm25ConcentrationMeasurement}, + {capabilities.fineDustHealthConcern.ID, "-pm25", clusters.Pm25ConcentrationMeasurement}, + {capabilities.formaldehydeMeasurement.ID, "-ch2o", clusters.FormaldehydeConcentrationMeasurement}, + {capabilities.formaldehydeHealthConcern.ID, "-ch2o", clusters.FormaldehydeConcentrationMeasurement}, + {capabilities.nitrogenDioxideHealthConcern.ID, "-no2", clusters.NitrogenDioxideConcentrationMeasurement}, + {capabilities.nitrogenDioxideMeasurement.ID, "-no2", clusters.NitrogenDioxideConcentrationMeasurement}, + {capabilities.ozoneHealthConcern.ID, "-ozone", clusters.OzoneConcentrationMeasurement}, + {capabilities.ozoneMeasurement.ID, "-ozone", clusters.OzoneConcentrationMeasurement}, + {capabilities.radonHealthConcern.ID, "-radon", clusters.RadonConcentrationMeasurement}, + {capabilities.radonMeasurement.ID, "-radon", clusters.RadonConcentrationMeasurement}, + {capabilities.tvocHealthConcern.ID, "-tvoc", clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement}, + {capabilities.tvocMeasurement.ID, "-tvoc", clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement}, + {capabilities.veryFineDustHealthConcern.ID, "-pm1", clusters.Pm1ConcentrationMeasurement}, + {capabilities.veryFineDustSensor.ID, "-pm1", clusters.Pm1ConcentrationMeasurement}, +} + +ThermostatFields.units = { + PPM = 0, + PPB = 1, + PPT = 2, + MGM3 = 3, + UGM3 = 4, + NGM3 = 5, + PM3 = 6, + BQM3 = 7, + PCIL = 0xFF -- not in matter spec +} + +local units = ThermostatFields.units -- copy units to avoid references below + +ThermostatFields.unit_strings = { + [units.PPM] = "ppm", + [units.PPB] = "ppb", + [units.PPT] = "ppt", + [units.MGM3] = "mg/m^3", + [units.NGM3] = "ng/m^3", + [units.UGM3] = "μg/m^3", + [units.BQM3] = "Bq/m^3", + [units.PCIL] = "pCi/L" +} + +ThermostatFields.unit_default = { + [capabilities.carbonMonoxideMeasurement.NAME] = units.PPM, + [capabilities.carbonDioxideMeasurement.NAME] = units.PPM, + [capabilities.nitrogenDioxideMeasurement.NAME] = units.PPM, + [capabilities.ozoneMeasurement.NAME] = units.PPM, + [capabilities.formaldehydeMeasurement.NAME] = units.PPM, + [capabilities.veryFineDustSensor.NAME] = units.UGM3, + [capabilities.fineDustSensor.NAME] = units.UGM3, + [capabilities.dustSensor.NAME] = units.UGM3, + [capabilities.radonMeasurement.NAME] = units.BQM3, + [capabilities.tvocMeasurement.NAME] = units.PPB -- TVOC is typically within the range of 0-5500 ppb, with good to moderate values being < 660 ppb +} + +-- All ConcentrationMesurement clusters inherit from the same base cluster definitions, +-- so CarbonMonoxideConcentratinMeasurement is used below but the same enum types exist +-- in all ConcentrationMeasurement clusters +ThermostatFields.level_strings = { + [clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.UNKNOWN] = "unknown", + [clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.LOW] = "good", + [clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.MEDIUM] = "moderate", + [clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.HIGH] = "unhealthy", + [clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.CRITICAL] = "hazardous", +} + +-- measured in g/mol +ThermostatFields.molecular_weights = { + [capabilities.carbonDioxideMeasurement.NAME] = 44.010, + [capabilities.nitrogenDioxideMeasurement.NAME] = 28.014, + [capabilities.ozoneMeasurement.NAME] = 48.0, + [capabilities.formaldehydeMeasurement.NAME] = 30.031, + [capabilities.veryFineDustSensor.NAME] = "N/A", + [capabilities.fineDustSensor.NAME] = "N/A", + [capabilities.dustSensor.NAME] = "N/A", + [capabilities.radonMeasurement.NAME] = 222.018, + [capabilities.tvocMeasurement.NAME] = "N/A", +} + +ThermostatFields.conversion_tables = { + [units.PPM] = { + [units.PPM] = function(value) return st_utils.round(value) end, + [units.PPB] = function(value) return st_utils.round(value * (10^3)) end, + [units.UGM3] = function(value, molecular_weight) return st_utils.round((value * molecular_weight * 10^3) / ThermostatFields.MGM3_PPM_CONVERSION_FACTOR) end, + [units.MGM3] = function(value, molecular_weight) return st_utils.round((value * molecular_weight) / ThermostatFields.MGM3_PPM_CONVERSION_FACTOR) end, + }, + [units.PPB] = { + [units.PPM] = function(value) return st_utils.round(value/(10^3)) end, + [units.PPB] = function(value) return st_utils.round(value) end, + }, + [units.PPT] = { + [units.PPM] = function(value) return st_utils.round(value/(10^6)) end + }, + [units.MGM3] = { + [units.UGM3] = function(value) return st_utils.round(value * (10^3)) end, + [units.PPM] = function(value, molecular_weight) return st_utils.round((value * ThermostatFields.MGM3_PPM_CONVERSION_FACTOR) / molecular_weight) end, + }, + [units.UGM3] = { + [units.UGM3] = function(value) return st_utils.round(value) end, + [units.PPM] = function(value, molecular_weight) return st_utils.round((value * ThermostatFields.MGM3_PPM_CONVERSION_FACTOR) / (molecular_weight * 10^3)) end, + }, + [units.NGM3] = { + [units.UGM3] = function(value) return st_utils.round(value/(10^3)) end + }, + [units.BQM3] = { + [units.PCIL] = function(value) return st_utils.round(value/37) end + }, +} + +return ThermostatFields \ No newline at end of file diff --git a/drivers/SmartThings/matter-thermostat/src/thermostat_utils/legacy_device_configuration.lua b/drivers/SmartThings/matter-thermostat/src/thermostat_utils/legacy_device_configuration.lua new file mode 100644 index 0000000000..6cdda2a53a --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/thermostat_utils/legacy_device_configuration.lua @@ -0,0 +1,259 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local version = require "version" +local clusters = require "st.matter.clusters" +local embedded_cluster_utils = require "thermostat_utils.embedded_cluster_utils" +local fields = require "thermostat_utils.fields" +local thermostat_utils = require "thermostat_utils.utils" + +if version.api < 10 then + clusters.HepaFilterMonitoring = require "embedded_clusters.HepaFilterMonitoring" + clusters.ActivatedCarbonFilterMonitoring = require "embedded_clusters.ActivatedCarbonFilterMonitoring" + clusters.AirQuality = require "embedded_clusters.AirQuality" +end + +local LegacyConfigurationHelpers = {} + +function LegacyConfigurationHelpers.create_level_measurement_profile(device) + local meas_name, level_name = "", "" + for _, details in ipairs(fields.AIR_QUALITY_MAP) do + local cap_id = details[1] + local cluster = details[3] + -- capability describes either a HealthConcern or Measurement/Sensor + if (cap_id:match("HealthConcern$")) then + local attr_eps = embedded_cluster_utils.get_endpoints(device, cluster.ID, { feature_bitmap = cluster.types.Feature.LEVEL_INDICATION }) + if #attr_eps > 0 then + level_name = level_name .. details[2] + end + elseif (cap_id:match("Measurement$") or cap_id:match("Sensor$")) then + local attr_eps = embedded_cluster_utils.get_endpoints(device, cluster.ID, { feature_bitmap = cluster.types.Feature.NUMERIC_MEASUREMENT }) + if #attr_eps > 0 then + meas_name = meas_name .. details[2] + end + end + end + return meas_name, level_name +end + +function LegacyConfigurationHelpers.create_air_quality_sensor_profile(device) + local aqs_eps = embedded_cluster_utils.get_endpoints(device, clusters.AirQuality.ID) + local profile_name = "" + if #aqs_eps > 0 then + profile_name = profile_name .. "-aqs" + end + local meas_name, level_name = LegacyConfigurationHelpers.create_level_measurement_profile(device) + if meas_name ~= "" then + profile_name = profile_name .. meas_name .. "-meas" + end + if level_name ~= "" then + profile_name = profile_name .. level_name .. "-level" + end + return profile_name +end + +function LegacyConfigurationHelpers.create_fan_profile(device) + local fan_eps = device:get_endpoints(clusters.FanControl.ID) + local wind_eps = device:get_endpoints(clusters.FanControl.ID, {feature_bitmap = clusters.FanControl.types.FanControlFeature.WIND}) + local rock_eps = device:get_endpoints(clusters.FanControl.ID, {feature_bitmap = clusters.FanControl.types.Feature.ROCKING}) + local profile_name = "" + if #fan_eps > 0 then + profile_name = profile_name .. "-fan" + end + if #rock_eps > 0 then + profile_name = profile_name .. "-rock" + end + if #wind_eps > 0 then + profile_name = profile_name .. "-wind" + end + return profile_name +end + +function LegacyConfigurationHelpers.create_air_purifier_profile(device) + local hepa_filter_eps = embedded_cluster_utils.get_endpoints(device, clusters.HepaFilterMonitoring.ID) + local ac_filter_eps = embedded_cluster_utils.get_endpoints(device, clusters.ActivatedCarbonFilterMonitoring.ID) + local fan_eps_seen = false + local profile_name = "air-purifier" + if #hepa_filter_eps > 0 then + profile_name = profile_name .. "-hepa" + end + if #ac_filter_eps > 0 then + profile_name = profile_name .. "-ac" + end + + -- air purifier profiles include -fan later in the name for historical reasons. + -- save this information for use at that point. + local fan_profile = LegacyConfigurationHelpers.create_fan_profile(device) + if fan_profile ~= "" then + fan_eps_seen = true + end + fan_profile = string.gsub(fan_profile, "-fan", "") + profile_name = profile_name .. fan_profile + + return profile_name, fan_eps_seen +end + +function LegacyConfigurationHelpers.create_thermostat_modes_profile(device) + local heat_eps = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.HEATING}) + local cool_eps = device:get_endpoints(clusters.Thermostat.ID, {feature_bitmap = clusters.Thermostat.types.ThermostatFeature.COOLING}) + + local thermostat_modes = "" + if #heat_eps == 0 and #cool_eps == 0 then + return "No Heating nor Cooling Support" + elseif #heat_eps > 0 and #cool_eps == 0 then + thermostat_modes = thermostat_modes .. "-heating-only" + elseif #cool_eps > 0 and #heat_eps == 0 then + thermostat_modes = thermostat_modes .. "-cooling-only" + end + return thermostat_modes +end + + +local LegacyDeviceConfiguration = {} + +function LegacyDeviceConfiguration.match_profile(device) + local running_state_supported = device:get_field(fields.profiling_data.THERMOSTAT_RUNNING_STATE_SUPPORT) + local battery_supported = device:get_field(fields.profiling_data.BATTERY_SUPPORT) + + local thermostat_eps = device:get_endpoints(clusters.Thermostat.ID) + local humidity_eps = device:get_endpoints(clusters.RelativeHumidityMeasurement.ID) + local device_type = thermostat_utils.get_device_type(device) + local profile_name + if device_type == fields.RAC_DEVICE_TYPE_ID then + profile_name = "room-air-conditioner" + + if #humidity_eps > 0 then + profile_name = profile_name .. "-humidity" + end + + -- Room AC does not support the rocking feature of FanControl. + local fan_name = LegacyConfigurationHelpers.create_fan_profile(device) + fan_name = string.gsub(fan_name, "-rock", "") + profile_name = profile_name .. fan_name + + local thermostat_modes = LegacyConfigurationHelpers.create_thermostat_modes_profile(device) + if thermostat_modes == "" then + profile_name = profile_name .. "-heating-cooling" + else + device.log.warn_with({hub_logs=true}, "Device does not support both heating and cooling. No matching profile") + return + end + + if profile_name == "room-air-conditioner-humidity-fan-wind-heating-cooling" then + profile_name = "room-air-conditioner" + end + + if not running_state_supported and profile_name == "room-air-conditioner-fan-heating-cooling" then + profile_name = profile_name .. "-nostate" + end + + elseif device_type == fields.FAN_DEVICE_TYPE_ID then + profile_name = LegacyConfigurationHelpers.create_fan_profile(device) + -- remove leading "-" + profile_name = string.sub(profile_name, 2) + if profile_name == "fan" then + profile_name = "fan-generic" + end + + elseif device_type == fields.AP_DEVICE_TYPE_ID then + local fan_eps_found + profile_name, fan_eps_found = LegacyConfigurationHelpers.create_air_purifier_profile(device) + if #thermostat_eps > 0 then + profile_name = profile_name .. "-thermostat" + + if #humidity_eps > 0 then + profile_name = profile_name .. "-humidity" + end + + if fan_eps_found then + profile_name = profile_name .. "-fan" + end + + local thermostat_modes = LegacyConfigurationHelpers.create_thermostat_modes_profile(device) + if thermostat_modes ~= "No Heating nor Cooling Support" then + profile_name = profile_name .. thermostat_modes + end + + if not running_state_supported then + profile_name = profile_name .. "-nostate" + end + + if battery_supported == fields.battery_support.BATTERY_LEVEL then + profile_name = profile_name .. "-batteryLevel" + elseif battery_supported == fields.battery_support.NO_BATTERY then + profile_name = profile_name .. "-nobattery" + end + elseif #device:get_endpoints(clusters.TemperatureMeasurement.ID) > 0 then + profile_name = profile_name .. "-temperature" + + if #humidity_eps > 0 then + profile_name = profile_name .. "-humidity" + end + + if fan_eps_found then + profile_name = profile_name .. "-fan" + end + end + profile_name = profile_name .. LegacyConfigurationHelpers.create_air_quality_sensor_profile(device) + elseif device_type == fields.WATER_HEATER_DEVICE_TYPE_ID then + -- If a Water Heater is composed of Electrical Sensor device type, it must support both ElectricalEnergyMeasurement and + -- ElectricalPowerMeasurement clusters. + local electrical_sensor_eps = thermostat_utils.get_endpoints_by_device_type(device, fields.ELECTRICAL_SENSOR_DEVICE_TYPE_ID) or {} + if #electrical_sensor_eps > 0 then + profile_name = "water-heater-power-energy-powerConsumption" + end + elseif device_type == fields.HEAT_PUMP_DEVICE_TYPE_ID then + profile_name = "heat-pump" + local MAX_HEAT_PUMP_THERMOSTAT_COMPONENTS = 2 + for i = 1, math.min(MAX_HEAT_PUMP_THERMOSTAT_COMPONENTS, #thermostat_eps) do + profile_name = profile_name .. "-thermostat" + if thermostat_utils.tbl_contains(humidity_eps, thermostat_eps[i]) then + profile_name = profile_name .. "-humidity" + end + end + elseif #thermostat_eps > 0 then + profile_name = "thermostat" + + if #humidity_eps > 0 then + profile_name = profile_name .. "-humidity" + end + + -- thermostat profiles support neither wind nor rocking FanControl attributes + local fan_name = LegacyConfigurationHelpers.create_fan_profile(device) + if fan_name ~= "" then + profile_name = profile_name .. "-fan" + end + + local thermostat_modes = LegacyConfigurationHelpers.create_thermostat_modes_profile(device) + if thermostat_modes == "No Heating nor Cooling Support" then + device.log.warn_with({hub_logs=true}, "Device does not support either heating or cooling. No matching profile") + return + else + profile_name = profile_name .. thermostat_modes + end + + if not running_state_supported then + profile_name = profile_name .. "-nostate" + end + + if battery_supported == fields.battery_support.BATTERY_LEVEL then + profile_name = profile_name .. "-batteryLevel" + elseif battery_supported == fields.battery_support.NO_BATTERY then + profile_name = profile_name .. "-nobattery" + end + else + device.log.warn_with({hub_logs=true}, "Device type is not supported in thermostat driver") + return + end + + if profile_name then + device.log.info_with({hub_logs=true}, string.format("Updating device profile to %s.", profile_name)) + device:try_update_metadata({profile = profile_name}) + end + -- clear all profiling data fields after profiling is complete. + for _, field in pairs(fields.profiling_data) do + device:set_field(field, nil) + end +end + +return LegacyDeviceConfiguration diff --git a/drivers/SmartThings/matter-thermostat/src/thermostat_utils/utils.lua b/drivers/SmartThings/matter-thermostat/src/thermostat_utils/utils.lua new file mode 100644 index 0000000000..8194f08c48 --- /dev/null +++ b/drivers/SmartThings/matter-thermostat/src/thermostat_utils/utils.lua @@ -0,0 +1,183 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local log = require "log" +local capabilities = require "st.capabilities" +local embedded_cluster_utils = require "thermostat_utils.embedded_cluster_utils" +local fields = require "thermostat_utils.fields" + +local ThermostatUtils = {} + +function ThermostatUtils.tbl_contains(array, value) + if value == nil then return false end + for _, element in pairs(array or {}) do + if element == value then + return true + end + end + return false +end + +function ThermostatUtils.get_field_for_endpoint(device, field, endpoint) + return device:get_field(string.format("%s_%d", field, endpoint)) +end + +function ThermostatUtils.set_field_for_endpoint(device, field, endpoint, value, additional_params) + device:set_field(string.format("%s_%d", field, endpoint), value, additional_params) +end + +function ThermostatUtils.find_default_endpoint(device, cluster) + local res = device.MATTER_DEFAULT_ENDPOINT + local eps = embedded_cluster_utils.get_endpoints(device, cluster) + table.sort(eps) + for _, v in ipairs(eps) do + if v ~= 0 then --0 is the matter RootNode endpoint + return v + end + end + device.log.warn(string.format("Did not find default endpoint, will use endpoint %d instead", device.MATTER_DEFAULT_ENDPOINT)) + return res +end + +function ThermostatUtils.component_to_endpoint(device, component_name, cluster_id) + -- Use the find_default_endpoint function to return the first endpoint that + -- supports a given cluster. + local component_to_endpoint_map = device:get_field(fields.COMPONENT_TO_ENDPOINT_MAP) + if component_to_endpoint_map ~= nil and component_to_endpoint_map[component_name] ~= nil then + return component_to_endpoint_map[component_name] + end + if not cluster_id then return device.MATTER_DEFAULT_ENDPOINT end + return ThermostatUtils.find_default_endpoint(device, cluster_id) +end + +function ThermostatUtils.endpoint_to_component(device, endpoint_id) + local component_to_endpoint_map = device:get_field(fields.COMPONENT_TO_ENDPOINT_MAP) + if component_to_endpoint_map ~= nil then + for comp, ep in pairs(component_to_endpoint_map) do + if ep == endpoint_id then + return comp + end + end + end + return "main" +end + +function ThermostatUtils.get_total_cumulative_energy_imported(device) + local total_cumulative_energy_imported = device:get_field(fields.TOTAL_CUMULATIVE_ENERGY_IMPORTED_MAP) or {} + local total_energy = 0 + for _, energyWh in pairs(total_cumulative_energy_imported) do + total_energy = total_energy + energyWh + end + return total_energy +end + +function ThermostatUtils.get_endpoints_by_device_type(device, device_type) + local endpoints = {} + for _, ep in ipairs(device.endpoints) do + for _, dt in ipairs(ep.device_types) do + if dt.device_type_id == device_type then + table.insert(endpoints, ep.endpoint_id) + break + end + end + end + table.sort(endpoints) + return endpoints +end + +function ThermostatUtils.get_device_type(device) + -- For cases where a device has multiple device types, this list indicates which + -- device type will be the "main" device type for purposes of selecting a profile + -- with an appropriate category. This is done to promote consistency between + -- devices with similar device type compositions that may report their device types + -- listed in different orders + local device_type_priority = { + [fields.HEAT_PUMP_DEVICE_TYPE_ID] = 1, + [fields.RAC_DEVICE_TYPE_ID] = 2, + [fields.AP_DEVICE_TYPE_ID] = 3, + [fields.THERMOSTAT_DEVICE_TYPE_ID] = 4, + [fields.FAN_DEVICE_TYPE_ID] = 5, + [fields.WATER_HEATER_DEVICE_TYPE_ID] = 6, + } + + local main_device_type = false + + for _, ep in ipairs(device.endpoints) do + if ep.device_types ~= nil then + for _, dt in ipairs(ep.device_types) do + if not device_type_priority[main_device_type] or (device_type_priority[dt.device_type_id] and + device_type_priority[dt.device_type_id] < device_type_priority[main_device_type]) then + main_device_type = dt.device_type_id + end + end + end + end + + return main_device_type +end + +function ThermostatUtils.unit_conversion(value, from_unit, to_unit, capability_name) + local conversion_function = fields.conversion_tables[from_unit] and fields.conversion_tables[from_unit][to_unit] or nil + if not conversion_function then + log.info_with( {hub_logs = true} , string.format("Unsupported unit conversion from %s to %s", fields.unit_strings[from_unit], fields.unit_strings[to_unit])) + return + end + + if not value then + log.info_with( {hub_logs = true} , "unit conversion value is nil") + return + end + + return conversion_function(value, fields.molecular_weights[capability_name]) +end + +function ThermostatUtils.supports_capability_by_id_modular(device, capability, component) + if not device:get_field(fields.SUPPORTED_COMPONENT_CAPABILITIES) then + device.log.warn_with({hub_logs = true}, "Device has overriden supports_capability_by_id, but does not have supported capabilities set.") + return false + end + for _, component_capabilities in ipairs(device:get_field(fields.SUPPORTED_COMPONENT_CAPABILITIES)) do + local comp_id = component_capabilities[1] + local capability_ids = component_capabilities[2] + if (component == nil) or (component == comp_id) then + for _, cap in ipairs(capability_ids) do + if cap == capability then + return true + end + end + end + end + return false +end + +function ThermostatUtils.report_power_consumption_to_st_energy(device, latest_total_imported_energy_wh) + local current_time = os.time() + local last_time = device:get_field(fields.LAST_IMPORTED_REPORT_TIMESTAMP) or 0 + + -- Ensure that the previous report was sent at least 15 minutes ago + if fields.MINIMUM_ST_ENERGY_REPORT_INTERVAL >= (current_time - last_time) then + return + end + + device:set_field(fields.LAST_IMPORTED_REPORT_TIMESTAMP, current_time, { persist = true }) + + -- Calculate the energy delta between reports + local energy_delta_wh = 0.0 + local previous_imported_report = device:get_latest_state("main", capabilities.powerConsumptionReport.ID, + capabilities.powerConsumptionReport.powerConsumption.NAME) + if previous_imported_report and previous_imported_report.energy then + energy_delta_wh = math.max(latest_total_imported_energy_wh - previous_imported_report.energy, 0.0) + end + + local epoch_to_iso8601 = function(time) return os.date("!%Y-%m-%dT%H:%M:%SZ", time) end + + -- Report the energy consumed during the time interval. The unit of these values should be 'Wh' + device:emit_component_event(device.profile.components["main"], capabilities.powerConsumptionReport.powerConsumption({ + start = epoch_to_iso8601(last_time), + ["end"] = epoch_to_iso8601(current_time - 1), + deltaEnergy = energy_delta_wh, + energy = latest_total_imported_energy_wh + })) +end + +return ThermostatUtils