From 42db48d8d0e5db9450eaee5579b4638cc5d6f5a3 Mon Sep 17 00:00:00 2001 From: YongSung Lee Date: Fri, 15 Dec 2023 18:00:42 +0900 Subject: [PATCH 1/2] add Resideo Korea DT300ST-M0000 Thermostat Resideo Korea Thermostat for WWST --- .../zigbee-thermostat/fingerprints.yml | 5 + .../thermostat-resideo-dt300st-m000.yml | 34 +++ .../zigbee-thermostat/src/init.lua | 3 +- .../src/resideo_korea/init.lua | 186 ++++++++++++++++ .../src/test/test_resideo_dt300st_m000.lua | 200 ++++++++++++++++++ 5 files changed, 427 insertions(+), 1 deletion(-) create mode 100644 drivers/SmartThings/zigbee-thermostat/profiles/thermostat-resideo-dt300st-m000.yml create mode 100644 drivers/SmartThings/zigbee-thermostat/src/resideo_korea/init.lua create mode 100644 drivers/SmartThings/zigbee-thermostat/src/test/test_resideo_dt300st_m000.lua diff --git a/drivers/SmartThings/zigbee-thermostat/fingerprints.yml b/drivers/SmartThings/zigbee-thermostat/fingerprints.yml index 1dd3137f16..b8793b0043 100644 --- a/drivers/SmartThings/zigbee-thermostat/fingerprints.yml +++ b/drivers/SmartThings/zigbee-thermostat/fingerprints.yml @@ -119,6 +119,11 @@ zigbeeManufacturer: manufacturer: Centralite model: 3156105 deviceProfileName: base-thermostat-no-operating-state + - id: "Resideo/DT300ST-M000" + deviceLabel: Main Thermostat + manufacturer: Resideo Korea + model: DT300ST-M000 + deviceProfileName: thermostat-resideo-dt300st-m000 zigbeeGeneric: - id: "genericThermostat" deviceLabel: Zigbee Thermostat diff --git a/drivers/SmartThings/zigbee-thermostat/profiles/thermostat-resideo-dt300st-m000.yml b/drivers/SmartThings/zigbee-thermostat/profiles/thermostat-resideo-dt300st-m000.yml new file mode 100644 index 0000000000..a31234eb90 --- /dev/null +++ b/drivers/SmartThings/zigbee-thermostat/profiles/thermostat-resideo-dt300st-m000.yml @@ -0,0 +1,34 @@ +name: thermostat-resideo-dt300st-m000 +components: +- id: main + capabilities: + - id: temperatureMeasurement + version: 1 + - id: thermostatHeatingSetpoint + version: 1 + config: + values: + - key: "heatingSetpoint.value" + range: [ 5, 35 ] + - id: thermostatMode + version: 1 + config: + values: + - key: "thermostatMode.value" + enabledValues: + - heating + - idle + - id: thermostatOperatingState + version: 1 + config: + values: + - key: "thermostatOperatingState.value" + enabledValues: + - heating + - idle + - id: firmwareUpdate + version: 1 + - id: refresh + version: 1 + categories: + - name: Thermostat diff --git a/drivers/SmartThings/zigbee-thermostat/src/init.lua b/drivers/SmartThings/zigbee-thermostat/src/init.lua index 0f35ada2d5..6602c1c3db 100644 --- a/drivers/SmartThings/zigbee-thermostat/src/init.lua +++ b/drivers/SmartThings/zigbee-thermostat/src/init.lua @@ -294,7 +294,8 @@ local zigbee_thermostat_driver = { require("leviton"), require("danfoss"), require("popp"), - require("vimar") + require("vimar"), + require("resideo_korea") }, } diff --git a/drivers/SmartThings/zigbee-thermostat/src/resideo_korea/init.lua b/drivers/SmartThings/zigbee-thermostat/src/resideo_korea/init.lua new file mode 100644 index 0000000000..a1e68ef02a --- /dev/null +++ b/drivers/SmartThings/zigbee-thermostat/src/resideo_korea/init.lua @@ -0,0 +1,186 @@ +-- 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. +local device_lib = require "st.device" +local device_management = require "st.zigbee.device_management" + +-- Zigbee Spec Utils +local clusters = require "st.zigbee.zcl.clusters" +local Thermostat = clusters.Thermostat +local ThermostatSystemMode = Thermostat.attributes.SystemMode + +local capabilities = require "st.capabilities" +local ThermostatMode = capabilities.thermostatMode +local ThermostatOperatingState = capabilities.thermostatOperatingState + +local do_refresh = function(self, device) + local attributes = {Thermostat.attributes.OccupiedHeatingSetpoint, Thermostat.attributes.LocalTemperature, + Thermostat.attributes.ControlSequenceOfOperation, Thermostat.attributes.ThermostatRunningState, + Thermostat.attributes.SystemMode} + for _, attribute in pairs(attributes) do + if device.network_type == device_lib.NETWORK_TYPE_ZIGBEE then + -- SmartThings Hub has a issue with setting parent endpoint + device:send(attribute:read(device):to_endpoint(0x01)) + else + device:send(attribute:read(device)) + end + end +end + +local do_configure = function(self, device) + if device.network_type == device_lib.NETWORK_TYPE_ZIGBEE then + for endpoint = 1, 6 do + device:send(device_management.build_bind_request(device, Thermostat.ID, self.environment_info.hub_zigbee_eui, + endpoint)) + end + end + device:send(Thermostat.attributes.OccupiedHeatingSetpoint:configure_reporting(device, 20, 300, 100)) + device:send(Thermostat.attributes.LocalTemperature:configure_reporting(device, 20, 300, 100)) + device:send(Thermostat.attributes.ThermostatRunningState:configure_reporting(device, 20, 300)) + device:send(Thermostat.attributes.SystemMode:configure_reporting(device, 20, 300)) + + do_refresh(self, device) +end + +local SUPPORTED_THERMOSTAT_MODES = {ThermostatMode.thermostatMode.away.NAME, ThermostatMode.thermostatMode.heat.NAME} + +local supported_thermostat_modes_handler = function(driver, device, supported_modes, zb_rx) + device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, + ThermostatMode.supportedThermostatModes(SUPPORTED_THERMOSTAT_MODES, { + visibility = { + displayed = false + } + })) +end + +local thermostat_operating_state_handler = function(driver, device, operating_state, zb_rx) + if (operating_state:is_heat_second_stage_on_set() or operating_state:is_heat_on_set()) then + device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, + ThermostatOperatingState.thermostatOperatingState.heating()) + elseif (operating_state:is_cool_second_stage_on_set() or operating_state:is_cool_on_set()) then + device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, + ThermostatOperatingState.thermostatOperatingState.cooling()) + elseif (operating_state:is_fan_on_set()) then + device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, + ThermostatOperatingState.thermostatOperatingState.fan_only()) + else + device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, + ThermostatOperatingState.thermostatOperatingState.idle()) + end +end + +local function thermostat_occupied_heating_setpoint_handler(driver, device, value, zb_rx) + device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, + capabilities.thermostatHeatingSetpoint.heatingSetpoint({ + value = value.value / 100.0, + unit = "C" + })) +end + +local set_thermostat_mode = function(driver, device, command) + if command.args.mode == ThermostatMode.thermostatMode.off.NAME or command.args.mode == + ThermostatMode.thermostatMode.away.NAME then + device:send_to_component(command.component, Thermostat.attributes.SystemMode:write(device, ThermostatSystemMode.OFF)) + device.thread:call_with_delay(1, function(d) + device:send_to_component(command.component, Thermostat.attributes.SystemMode:read(device)) + end) + elseif command.args.mode == ThermostatMode.thermostatMode.auto.NAME or command.args.mode == + ThermostatMode.thermostatMode.heat.NAME then + device:send_to_component(command.component, + Thermostat.attributes.SystemMode:write(device, ThermostatSystemMode.HEAT)) + device.thread:call_with_delay(1, function(d) + device:send_to_component(command.component, Thermostat.attributes.SystemMode:read(device)) + end) + end +end + +local thermostat_mode_setter = function(mode_name) + return function(driver, device, command) + return set_thermostat_mode(driver, device, { + component = command.component, + args = { + mode = mode_name + } + }) + end +end + +local thermostat_mode_handler = function(driver, device, thermostat_mode, zb_rx) + if thermostat_mode.value == ThermostatSystemMode.OFF then + device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, ThermostatMode.thermostatMode.away()) + else + device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, ThermostatMode.thermostatMode.heat()) + end +end + +local function added(driver, device, event) + if device.network_type == device_lib.NETWORK_TYPE_ZIGBEE then + for i = 2, 6, 1 do + local name = string.format("Room %d", i - 1) + local metadata = { + type = "EDGE_CHILD", + label = name, + profile = "thermostat-resideo-dt300st-m000", + parent_device_id = device.id, + parent_assigned_child_key = string.format("%02X", i), + vendor_provided_label = name + } + driver:try_create_device(metadata) + end + end +end + +local function find_child(parent, ep_id) + return parent:get_child_by_parent_assigned_key(string.format("%02X", ep_id)) +end + +local function init(driver, device, event) + if device.network_type == device_lib.NETWORK_TYPE_ZIGBEE then + device:set_find_child(find_child) + end +end + +local resideo_thermostat = { + NAME = "Resideo Thermostat Handler", + lifecycle_handlers = { + init = init, + added = added, + doConfigure = do_configure + }, + zigbee_handlers = { + attr = { + [Thermostat.ID] = { + [Thermostat.attributes.ControlSequenceOfOperation.ID] = supported_thermostat_modes_handler, + [Thermostat.attributes.ThermostatRunningState.ID] = thermostat_operating_state_handler, + [Thermostat.attributes.SystemMode.ID] = thermostat_mode_handler, + [Thermostat.attributes.OccupiedHeatingSetpoint.ID] = thermostat_occupied_heating_setpoint_handler + } + } + }, + capability_handlers = { + [ThermostatMode.ID] = { + [ThermostatMode.commands.setThermostatMode.NAME] = set_thermostat_mode, + [ThermostatMode.commands.auto.NAME] = thermostat_mode_setter(ThermostatMode.thermostatMode.heat.NAME), + [ThermostatMode.commands.off.NAME] = thermostat_mode_setter(ThermostatMode.thermostatMode.off.NAME), + [ThermostatMode.commands.heat.NAME] = thermostat_mode_setter(ThermostatMode.thermostatMode.heat.NAME) + }, + [capabilities.refresh.ID] = { + [capabilities.refresh.commands.refresh.NAME] = do_refresh + } + }, + can_handle = function(opts, driver, device, ...) + return device:get_manufacturer() == "Resideo Korea" and device:get_model() == "DT300ST-M000" + end +} + +return resideo_thermostat diff --git a/drivers/SmartThings/zigbee-thermostat/src/test/test_resideo_dt300st_m000.lua b/drivers/SmartThings/zigbee-thermostat/src/test/test_resideo_dt300st_m000.lua new file mode 100644 index 0000000000..ebd2819eb0 --- /dev/null +++ b/drivers/SmartThings/zigbee-thermostat/src/test/test_resideo_dt300st_m000.lua @@ -0,0 +1,200 @@ +-- 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. +local test = require "integration_test" +local t_utils = require "integration_test.utils" +local zigbee_test_utils = require "integration_test.zigbee_test_utils" +local clusters = require "st.zigbee.zcl.clusters" + +local Thermostat = clusters.Thermostat +local capabilities = require "st.capabilities" + +local mock_device = test.mock_device.build_test_zigbee_device({ + profile = t_utils.get_profile_definition("thermostat-resideo-dt300st-m000.yml"), + zigbee_endpoints = { + [1] = { + id = 1, + manufacturer = "Resideo Korea", + model = "DT300ST-M000", + server_clusters = {0x0201, 0x0402} + }, + [2] = { + id = 1, + manufacturer = "Resideo Korea", + model = "DT300ST-M000", + server_clusters = {0x0201, 0x0402} + }, + [3] = { + id = 1, + manufacturer = "Resideo Korea", + model = "DT300ST-M000", + server_clusters = {0x0201, 0x0402} + }, + [4] = { + id = 1, + manufacturer = "Resideo Korea", + model = "DT300ST-M000", + server_clusters = {0x0201, 0x0402} + }, + [5] = { + id = 1, + manufacturer = "Resideo Korea", + model = "DT300ST-M000", + server_clusters = {0x0201, 0x0402} + }, + [6] = { + id = 1, + manufacturer = "Resideo Korea", + model = "DT300ST-M000", + server_clusters = {0x0201, 0x0402} + } + } +}) + +zigbee_test_utils.prepare_zigbee_env_info() +local function test_init() + test.mock_device.add_test_device(mock_device) + zigbee_test_utils.init_noop_health_check_timer() +end + +test.set_test_init_function(test_init) + +test.register_coroutine_test("Configure should configure all necessary attributes", function() + test.socket.zigbee:__set_channel_ordering("relaxed") + test.socket.device_lifecycle:__queue_receive({mock_device.id, "doConfigure"}) + for endpoint = 1, 6 do + test.socket.zigbee:__expect_send({mock_device.id, + zigbee_test_utils.build_bind_request(mock_device, zigbee_test_utils.mock_hub_eui, + Thermostat.ID, endpoint)}) + end + + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.OccupiedHeatingSetpoint:configure_reporting(mock_device, 20, + 300, 100)}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.LocalTemperature:configure_reporting(mock_device, 20, 300, 100)}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.ThermostatRunningState:configure_reporting(mock_device, 20, + 300)}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.SystemMode:configure_reporting(mock_device, 20, 300)}) + + local attributes = {Thermostat.attributes.OccupiedHeatingSetpoint, Thermostat.attributes.LocalTemperature, + Thermostat.attributes.ControlSequenceOfOperation, Thermostat.attributes.ThermostatRunningState, + Thermostat.attributes.SystemMode} + for _, attribute in pairs(attributes) do + test.socket.zigbee:__expect_send({mock_device.id, attribute:read(mock_device)}) + end + mock_device:expect_metadata_update({ + provisioning_state = "PROVISIONED" + }) +end) + +test.register_coroutine_test("Refresh should read all necessary attributes", function() + test.socket.zigbee:__set_channel_ordering("relaxed") + test.socket.capability:__queue_receive({mock_device.id, { + capability = "refresh", + component = "main", + command = "refresh", + args = {} + }}) + local attributes = {Thermostat.attributes.OccupiedHeatingSetpoint, Thermostat.attributes.LocalTemperature, + Thermostat.attributes.ControlSequenceOfOperation, Thermostat.attributes.ThermostatRunningState, + Thermostat.attributes.SystemMode} + for _, attribute in pairs(attributes) do + test.socket.zigbee:__expect_send({mock_device.id, attribute:read(mock_device)}) + end +end) + +test.register_coroutine_test("Temperature reporting should create the appropriate events", function() + test.socket.zigbee:__queue_receive({mock_device.id, + Thermostat.attributes.LocalTemperature:build_test_attr_report(mock_device, 2100)}) + test.socket.capability:__expect_send(mock_device:generate_test_message("main", + capabilities.temperatureMeasurement.temperature({ + value = 21.0, + unit = "C" + }))) +end) + +test.register_coroutine_test("Thermostat mode reporting should create the appropriate events", function() + test.socket.zigbee:__queue_receive({mock_device.id, + Thermostat.attributes.SystemMode:build_test_attr_report(mock_device, + Thermostat.attributes.SystemMode.OFF)}) + test.socket.capability:__expect_send(mock_device:generate_test_message("main", capabilities.thermostatMode + .thermostatMode.away())) + test.socket.zigbee:__queue_receive({mock_device.id, + Thermostat.attributes.SystemMode:build_test_attr_report(mock_device, + Thermostat.attributes.SystemMode.HEAT)}) + test.socket.capability:__expect_send(mock_device:generate_test_message("main", capabilities.thermostatMode + .thermostatMode.heat())) +end) + +test.register_coroutine_test("ControlSequenceOfOperation reporting should create the appropriate events", function() + test.socket.zigbee:__queue_receive({mock_device.id, + Thermostat.attributes.ControlSequenceOfOperation:build_test_attr_report( + mock_device, 0x02)}) + test.socket.capability:__expect_send(mock_device:generate_test_message("main", + capabilities.thermostatMode.supportedThermostatModes({"away", "heat"}, { + visibility = { + displayed = false + } + }))) +end) + +test.register_coroutine_test("OccupiedHeatingSetpoint reporting shoulb create the appropriate events", function() + test.socket.zigbee:__queue_receive({mock_device.id, + Thermostat.attributes.OccupiedHeatingSetpoint:build_test_attr_report(mock_device, + 2100)}) + test.socket.capability:__expect_send(mock_device:generate_test_message("main", + capabilities.thermostatHeatingSetpoint.heatingSetpoint({ + value = 21.0, + unit = "C" + }))) +end) + +test.register_coroutine_test("Setting the heating setpoint should generate the appropriate messages", function() + test.socket.capability:__queue_receive({mock_device.id, { + component = "main", + capability = capabilities.thermostatHeatingSetpoint.ID, + command = "setHeatingSetpoint", + args = {21} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.OccupiedHeatingSetpoint:write(mock_device, 2100)}) +end) + +test.register_coroutine_test("Setting the thermostat mode to away should generate the appropriate messages", function() + test.socket.capability:__queue_receive({mock_device.id, { + component = "main", + capability = capabilities.thermostatMode.ID, + command = "setThermostatMode", + args = {"away"} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.SystemMode:write(mock_device, + Thermostat.attributes.SystemMode.OFF)}) +end) + +test.register_coroutine_test("Setting the thermostat mode to heat should generate the appropriate messages", function() + test.socket.capability:__queue_receive({mock_device.id, { + component = "main", + capability = capabilities.thermostatMode.ID, + command = "heat", + args = {} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.SystemMode:write(mock_device, + Thermostat.attributes.SystemMode.HEAT)}) +end) + +test.run_registered_tests() From 22c86382fae84b165defaac418a0a4ceae1da675 Mon Sep 17 00:00:00 2001 From: YongSung Lee Date: Fri, 19 Jan 2024 19:30:40 +0900 Subject: [PATCH 2/2] add unittest for child devices --- .../src/test/test_resideo_dt300st_m000.lua | 579 +++++++++++++++++- 1 file changed, 549 insertions(+), 30 deletions(-) diff --git a/drivers/SmartThings/zigbee-thermostat/src/test/test_resideo_dt300st_m000.lua b/drivers/SmartThings/zigbee-thermostat/src/test/test_resideo_dt300st_m000.lua index ebd2819eb0..2a3348e3d0 100644 --- a/drivers/SmartThings/zigbee-thermostat/src/test/test_resideo_dt300st_m000.lua +++ b/drivers/SmartThings/zigbee-thermostat/src/test/test_resideo_dt300st_m000.lua @@ -27,43 +27,58 @@ local mock_device = test.mock_device.build_test_zigbee_device({ manufacturer = "Resideo Korea", model = "DT300ST-M000", server_clusters = {0x0201, 0x0402} - }, - [2] = { - id = 1, - manufacturer = "Resideo Korea", - model = "DT300ST-M000", - server_clusters = {0x0201, 0x0402} - }, - [3] = { - id = 1, - manufacturer = "Resideo Korea", - model = "DT300ST-M000", - server_clusters = {0x0201, 0x0402} - }, - [4] = { - id = 1, - manufacturer = "Resideo Korea", - model = "DT300ST-M000", - server_clusters = {0x0201, 0x0402} - }, - [5] = { - id = 1, - manufacturer = "Resideo Korea", - model = "DT300ST-M000", - server_clusters = {0x0201, 0x0402} - }, - [6] = { - id = 1, - manufacturer = "Resideo Korea", - model = "DT300ST-M000", - server_clusters = {0x0201, 0x0402} } } }) +-- Room 1 (2nd Thermostat) +local mock_first_child = test.mock_device.build_test_child_device({ + profile = t_utils.get_profile_definition("thermostat-resideo-dt300st-m000.yml"), + device_network_id = string.format("%04X:%02X", mock_device:get_short_address(), 2), + parent_device_id = mock_device.id, + parent_assigned_child_key = string.format("%02X", 2) +}) + +-- Room 2 (3rd Thermostat) +local mock_second_child = test.mock_device.build_test_child_device({ + profile = t_utils.get_profile_definition("thermostat-resideo-dt300st-m000.yml"), + device_network_id = string.format("%04X:%02X", mock_device:get_short_address(), 3), + parent_device_id = mock_device.id, + parent_assigned_child_key = string.format("%02X", 3) +}) + +-- Room 3 (4th Thermostat) +local mock_third_child = test.mock_device.build_test_child_device({ + profile = t_utils.get_profile_definition("thermostat-resideo-dt300st-m000.yml"), + device_network_id = string.format("%04X:%02X", mock_device:get_short_address(), 4), + parent_device_id = mock_device.id, + parent_assigned_child_key = string.format("%02X", 4) +}) + +-- Room 4 (5th Thermostat) +local mock_forth_child = test.mock_device.build_test_child_device({ + profile = t_utils.get_profile_definition("thermostat-resideo-dt300st-m000.yml"), + device_network_id = string.format("%04X:%02X", mock_device:get_short_address(), 5), + parent_device_id = mock_device.id, + parent_assigned_child_key = string.format("%02X", 5) +}) + +-- Room 5 (6th Thermostat) +local mock_fifth_child = test.mock_device.build_test_child_device({ + profile = t_utils.get_profile_definition("thermostat-resideo-dt300st-m000.yml"), + device_network_id = string.format("%04X:%02X", mock_device:get_short_address(), 6), + parent_device_id = mock_device.id, + parent_assigned_child_key = string.format("%02X", 6) +}) + zigbee_test_utils.prepare_zigbee_env_info() local function test_init() test.mock_device.add_test_device(mock_device) + test.mock_device.add_test_device(mock_first_child) + test.mock_device.add_test_device(mock_second_child) + test.mock_device.add_test_device(mock_third_child) + test.mock_device.add_test_device(mock_forth_child) + test.mock_device.add_test_device(mock_fifth_child) zigbee_test_utils.init_noop_health_check_timer() end @@ -100,6 +115,9 @@ test.register_coroutine_test("Configure should configure all necessary attribute }) end) +-------------------------------------------------------------------------------- +-- Parent thermostat device + test.register_coroutine_test("Refresh should read all necessary attributes", function() test.socket.zigbee:__set_channel_ordering("relaxed") test.socket.capability:__queue_receive({mock_device.id, { @@ -197,4 +215,505 @@ test.register_coroutine_test("Setting the thermostat mode to heat should generat Thermostat.attributes.SystemMode.HEAT)}) end) +-------------------------------------------------------------------------------- +-- First child thermostat device + +test.register_coroutine_test("Refresh should read all necessary attributes with first child device", function() + test.socket.zigbee:__set_channel_ordering("relaxed") + test.socket.capability:__queue_receive({mock_first_child.id, { + capability = "refresh", + component = "main", + command = "refresh", + args = {} + }}) + local attributes = {Thermostat.attributes.OccupiedHeatingSetpoint, Thermostat.attributes.LocalTemperature, + Thermostat.attributes.ControlSequenceOfOperation, Thermostat.attributes.ThermostatRunningState, + Thermostat.attributes.SystemMode} + for _, attribute in pairs(attributes) do + test.socket.zigbee:__expect_send({mock_device.id, attribute:read(mock_first_child)}) + end +end) + +test.register_coroutine_test("Temperature reporting should create the appropriate events with first child device", function() + test.socket.zigbee:__queue_receive({mock_first_child.id, + Thermostat.attributes.LocalTemperature:build_test_attr_report(mock_first_child, 2100)}) + test.socket.capability:__expect_send(mock_first_child:generate_test_message("main", + capabilities.temperatureMeasurement.temperature({ + value = 21.0, + unit = "C" + }))) +end) + +test.register_coroutine_test("Thermostat mode reporting should create the appropriate events with first child device", function() + test.socket.zigbee:__queue_receive({mock_first_child.id, + Thermostat.attributes.SystemMode:build_test_attr_report(mock_first_child, + Thermostat.attributes.SystemMode.OFF)}) + test.socket.capability:__expect_send(mock_first_child:generate_test_message("main", capabilities.thermostatMode + .thermostatMode.away())) + test.socket.zigbee:__queue_receive({mock_first_child.id, + Thermostat.attributes.SystemMode:build_test_attr_report(mock_first_child, + Thermostat.attributes.SystemMode.HEAT)}) + test.socket.capability:__expect_send(mock_first_child:generate_test_message("main", capabilities.thermostatMode + .thermostatMode.heat())) +end) + +test.register_coroutine_test("ControlSequenceOfOperation reporting should create the appropriate events with first child device", function() + test.socket.zigbee:__queue_receive({mock_first_child.id, + Thermostat.attributes.ControlSequenceOfOperation:build_test_attr_report( + mock_first_child, 0x02)}) + test.socket.capability:__expect_send(mock_first_child:generate_test_message("main", + capabilities.thermostatMode.supportedThermostatModes({"away", "heat"}, { + visibility = { + displayed = false + } + }))) +end) + +test.register_coroutine_test("OccupiedHeatingSetpoint reporting shoulb create the appropriate events with first child device", function() + test.socket.zigbee:__queue_receive({mock_first_child.id, + Thermostat.attributes.OccupiedHeatingSetpoint:build_test_attr_report(mock_first_child, + 2100)}) + test.socket.capability:__expect_send(mock_first_child:generate_test_message("main", + capabilities.thermostatHeatingSetpoint.heatingSetpoint({ + value = 21.0, + unit = "C" + }))) +end) + +test.register_coroutine_test("Setting the heating setpoint should generate the appropriate messages with first child device", function() + test.socket.capability:__queue_receive({mock_first_child.id, { + component = "main", + capability = capabilities.thermostatHeatingSetpoint.ID, + command = "setHeatingSetpoint", + args = {21} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.OccupiedHeatingSetpoint:write(mock_first_child, 2100)}) +end) + +test.register_coroutine_test("Setting the thermostat mode to away should generate the appropriate messages with first child device", function() + test.socket.capability:__queue_receive({mock_first_child.id, { + component = "main", + capability = capabilities.thermostatMode.ID, + command = "setThermostatMode", + args = {"away"} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.SystemMode:write(mock_first_child, + Thermostat.attributes.SystemMode.OFF)}) +end) + +test.register_coroutine_test("Setting the thermostat mode to heat should generate the appropriate messages with first child device", function() + test.socket.capability:__queue_receive({mock_first_child.id, { + component = "main", + capability = capabilities.thermostatMode.ID, + command = "heat", + args = {} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.SystemMode:write(mock_first_child, + Thermostat.attributes.SystemMode.HEAT)}) +end) + +-------------------------------------------------------------------------------- +-- Second child thermostat device + +test.register_coroutine_test("Refresh should read all necessary attributes with second child device", function() + test.socket.zigbee:__set_channel_ordering("relaxed") + test.socket.capability:__queue_receive({mock_second_child.id, { + capability = "refresh", + component = "main", + command = "refresh", + args = {} + }}) + local attributes = {Thermostat.attributes.OccupiedHeatingSetpoint, Thermostat.attributes.LocalTemperature, + Thermostat.attributes.ControlSequenceOfOperation, Thermostat.attributes.ThermostatRunningState, + Thermostat.attributes.SystemMode} + for _, attribute in pairs(attributes) do + test.socket.zigbee:__expect_send({mock_device.id, attribute:read(mock_second_child)}) + end +end) + +test.register_coroutine_test("Temperature reporting should create the appropriate events with second child device", function() + test.socket.zigbee:__queue_receive({mock_second_child.id, + Thermostat.attributes.LocalTemperature:build_test_attr_report(mock_second_child, 2100)}) + test.socket.capability:__expect_send(mock_second_child:generate_test_message("main", + capabilities.temperatureMeasurement.temperature({ + value = 21.0, + unit = "C" + }))) +end) + +test.register_coroutine_test("Thermostat mode reporting should create the appropriate events with second child device", function() + test.socket.zigbee:__queue_receive({mock_second_child.id, + Thermostat.attributes.SystemMode:build_test_attr_report(mock_second_child, + Thermostat.attributes.SystemMode.OFF)}) + test.socket.capability:__expect_send(mock_second_child:generate_test_message("main", capabilities.thermostatMode + .thermostatMode.away())) + test.socket.zigbee:__queue_receive({mock_second_child.id, + Thermostat.attributes.SystemMode:build_test_attr_report(mock_second_child, + Thermostat.attributes.SystemMode.HEAT)}) + test.socket.capability:__expect_send(mock_second_child:generate_test_message("main", capabilities.thermostatMode + .thermostatMode.heat())) +end) + +test.register_coroutine_test("ControlSequenceOfOperation reporting should create the appropriate events with second child device", function() + test.socket.zigbee:__queue_receive({mock_second_child.id, + Thermostat.attributes.ControlSequenceOfOperation:build_test_attr_report( + mock_second_child, 0x02)}) + test.socket.capability:__expect_send(mock_second_child:generate_test_message("main", + capabilities.thermostatMode.supportedThermostatModes({"away", "heat"}, { + visibility = { + displayed = false + } + }))) +end) + +test.register_coroutine_test("OccupiedHeatingSetpoint reporting shoulb create the appropriate events with second child device", function() + test.socket.zigbee:__queue_receive({mock_second_child.id, + Thermostat.attributes.OccupiedHeatingSetpoint:build_test_attr_report(mock_second_child, + 2100)}) + test.socket.capability:__expect_send(mock_second_child:generate_test_message("main", + capabilities.thermostatHeatingSetpoint.heatingSetpoint({ + value = 21.0, + unit = "C" + }))) +end) + +test.register_coroutine_test("Setting the heating setpoint should generate the appropriate messages with second child device", function() + test.socket.capability:__queue_receive({mock_second_child.id, { + component = "main", + capability = capabilities.thermostatHeatingSetpoint.ID, + command = "setHeatingSetpoint", + args = {21} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.OccupiedHeatingSetpoint:write(mock_second_child, 2100)}) +end) + +test.register_coroutine_test("Setting the thermostat mode to away should generate the appropriate messages with second child device", function() + test.socket.capability:__queue_receive({mock_second_child.id, { + component = "main", + capability = capabilities.thermostatMode.ID, + command = "setThermostatMode", + args = {"away"} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.SystemMode:write(mock_second_child, + Thermostat.attributes.SystemMode.OFF)}) +end) + +test.register_coroutine_test("Setting the thermostat mode to heat should generate the appropriate messages with second child device", function() + test.socket.capability:__queue_receive({mock_second_child.id, { + component = "main", + capability = capabilities.thermostatMode.ID, + command = "heat", + args = {} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.SystemMode:write(mock_second_child, + Thermostat.attributes.SystemMode.HEAT)}) +end) + +-------------------------------------------------------------------------------- +-- Third child thermostat device + +test.register_coroutine_test("Refresh should read all necessary attributes with third child device", function() + test.socket.zigbee:__set_channel_ordering("relaxed") + test.socket.capability:__queue_receive({mock_third_child.id, { + capability = "refresh", + component = "main", + command = "refresh", + args = {} + }}) + local attributes = {Thermostat.attributes.OccupiedHeatingSetpoint, Thermostat.attributes.LocalTemperature, + Thermostat.attributes.ControlSequenceOfOperation, Thermostat.attributes.ThermostatRunningState, + Thermostat.attributes.SystemMode} + for _, attribute in pairs(attributes) do + test.socket.zigbee:__expect_send({mock_device.id, attribute:read(mock_third_child)}) + end +end) + +test.register_coroutine_test("Temperature reporting should create the appropriate events with third child device", function() + test.socket.zigbee:__queue_receive({mock_third_child.id, + Thermostat.attributes.LocalTemperature:build_test_attr_report(mock_third_child, 2100)}) + test.socket.capability:__expect_send(mock_third_child:generate_test_message("main", + capabilities.temperatureMeasurement.temperature({ + value = 21.0, + unit = "C" + }))) +end) + +test.register_coroutine_test("Thermostat mode reporting should create the appropriate events with third child device", function() + test.socket.zigbee:__queue_receive({mock_third_child.id, + Thermostat.attributes.SystemMode:build_test_attr_report(mock_third_child, + Thermostat.attributes.SystemMode.OFF)}) + test.socket.capability:__expect_send(mock_third_child:generate_test_message("main", capabilities.thermostatMode + .thermostatMode.away())) + test.socket.zigbee:__queue_receive({mock_third_child.id, + Thermostat.attributes.SystemMode:build_test_attr_report(mock_third_child, + Thermostat.attributes.SystemMode.HEAT)}) + test.socket.capability:__expect_send(mock_third_child:generate_test_message("main", capabilities.thermostatMode + .thermostatMode.heat())) +end) + +test.register_coroutine_test("ControlSequenceOfOperation reporting should create the appropriate events with third child device", function() + test.socket.zigbee:__queue_receive({mock_third_child.id, + Thermostat.attributes.ControlSequenceOfOperation:build_test_attr_report( + mock_third_child, 0x02)}) + test.socket.capability:__expect_send(mock_third_child:generate_test_message("main", + capabilities.thermostatMode.supportedThermostatModes({"away", "heat"}, { + visibility = { + displayed = false + } + }))) +end) + +test.register_coroutine_test("OccupiedHeatingSetpoint reporting shoulb create the appropriate events with third child device", function() + test.socket.zigbee:__queue_receive({mock_third_child.id, + Thermostat.attributes.OccupiedHeatingSetpoint:build_test_attr_report(mock_third_child, + 2100)}) + test.socket.capability:__expect_send(mock_third_child:generate_test_message("main", + capabilities.thermostatHeatingSetpoint.heatingSetpoint({ + value = 21.0, + unit = "C" + }))) +end) + +test.register_coroutine_test("Setting the heating setpoint should generate the appropriate messages with third child device", function() + test.socket.capability:__queue_receive({mock_third_child.id, { + component = "main", + capability = capabilities.thermostatHeatingSetpoint.ID, + command = "setHeatingSetpoint", + args = {21} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.OccupiedHeatingSetpoint:write(mock_third_child, 2100)}) +end) + +test.register_coroutine_test("Setting the thermostat mode to away should generate the appropriate messages with third child device", function() + test.socket.capability:__queue_receive({mock_third_child.id, { + component = "main", + capability = capabilities.thermostatMode.ID, + command = "setThermostatMode", + args = {"away"} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.SystemMode:write(mock_third_child, + Thermostat.attributes.SystemMode.OFF)}) +end) + +test.register_coroutine_test("Setting the thermostat mode to heat should generate the appropriate messages with third child device", function() + test.socket.capability:__queue_receive({mock_third_child.id, { + component = "main", + capability = capabilities.thermostatMode.ID, + command = "heat", + args = {} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.SystemMode:write(mock_third_child, + Thermostat.attributes.SystemMode.HEAT)}) +end) + +-------------------------------------------------------------------------------- +-- Forth child thermostat device + +test.register_coroutine_test("Refresh should read all necessary attributes with forth child device", function() + test.socket.zigbee:__set_channel_ordering("relaxed") + test.socket.capability:__queue_receive({mock_forth_child.id, { + capability = "refresh", + component = "main", + command = "refresh", + args = {} + }}) + local attributes = {Thermostat.attributes.OccupiedHeatingSetpoint, Thermostat.attributes.LocalTemperature, + Thermostat.attributes.ControlSequenceOfOperation, Thermostat.attributes.ThermostatRunningState, + Thermostat.attributes.SystemMode} + for _, attribute in pairs(attributes) do + test.socket.zigbee:__expect_send({mock_device.id, attribute:read(mock_forth_child)}) + end +end) + +test.register_coroutine_test("Temperature reporting should create the appropriate events with forth child device", function() + test.socket.zigbee:__queue_receive({mock_forth_child.id, + Thermostat.attributes.LocalTemperature:build_test_attr_report(mock_forth_child, 2100)}) + test.socket.capability:__expect_send(mock_forth_child:generate_test_message("main", + capabilities.temperatureMeasurement.temperature({ + value = 21.0, + unit = "C" + }))) +end) + +test.register_coroutine_test("Thermostat mode reporting should create the appropriate events with forth child device", function() + test.socket.zigbee:__queue_receive({mock_forth_child.id, + Thermostat.attributes.SystemMode:build_test_attr_report(mock_forth_child, + Thermostat.attributes.SystemMode.OFF)}) + test.socket.capability:__expect_send(mock_forth_child:generate_test_message("main", capabilities.thermostatMode + .thermostatMode.away())) + test.socket.zigbee:__queue_receive({mock_forth_child.id, + Thermostat.attributes.SystemMode:build_test_attr_report(mock_forth_child, + Thermostat.attributes.SystemMode.HEAT)}) + test.socket.capability:__expect_send(mock_forth_child:generate_test_message("main", capabilities.thermostatMode + .thermostatMode.heat())) +end) + +test.register_coroutine_test("ControlSequenceOfOperation reporting should create the appropriate events with forth child device", function() + test.socket.zigbee:__queue_receive({mock_forth_child.id, + Thermostat.attributes.ControlSequenceOfOperation:build_test_attr_report( + mock_forth_child, 0x02)}) + test.socket.capability:__expect_send(mock_forth_child:generate_test_message("main", + capabilities.thermostatMode.supportedThermostatModes({"away", "heat"}, { + visibility = { + displayed = false + } + }))) +end) + +test.register_coroutine_test("OccupiedHeatingSetpoint reporting shoulb create the appropriate events with forth child device", function() + test.socket.zigbee:__queue_receive({mock_forth_child.id, + Thermostat.attributes.OccupiedHeatingSetpoint:build_test_attr_report(mock_forth_child, + 2100)}) + test.socket.capability:__expect_send(mock_forth_child:generate_test_message("main", + capabilities.thermostatHeatingSetpoint.heatingSetpoint({ + value = 21.0, + unit = "C" + }))) +end) + +test.register_coroutine_test("Setting the heating setpoint should generate the appropriate messages with forth child device", function() + test.socket.capability:__queue_receive({mock_forth_child.id, { + component = "main", + capability = capabilities.thermostatHeatingSetpoint.ID, + command = "setHeatingSetpoint", + args = {21} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.OccupiedHeatingSetpoint:write(mock_forth_child, 2100)}) +end) + +test.register_coroutine_test("Setting the thermostat mode to away should generate the appropriate messages with forth child device", function() + test.socket.capability:__queue_receive({mock_forth_child.id, { + component = "main", + capability = capabilities.thermostatMode.ID, + command = "setThermostatMode", + args = {"away"} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.SystemMode:write(mock_forth_child, + Thermostat.attributes.SystemMode.OFF)}) +end) + +test.register_coroutine_test("Setting the thermostat mode to heat should generate the appropriate messages with forth child device", function() + test.socket.capability:__queue_receive({mock_forth_child.id, { + component = "main", + capability = capabilities.thermostatMode.ID, + command = "heat", + args = {} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.SystemMode:write(mock_forth_child, + Thermostat.attributes.SystemMode.HEAT)}) +end) + +-------------------------------------------------------------------------------- +-- Fifth child thermostat device + +test.register_coroutine_test("Refresh should read all necessary attributes with fifth child device", function() + test.socket.zigbee:__set_channel_ordering("relaxed") + test.socket.capability:__queue_receive({mock_fifth_child.id, { + capability = "refresh", + component = "main", + command = "refresh", + args = {} + }}) + local attributes = {Thermostat.attributes.OccupiedHeatingSetpoint, Thermostat.attributes.LocalTemperature, + Thermostat.attributes.ControlSequenceOfOperation, Thermostat.attributes.ThermostatRunningState, + Thermostat.attributes.SystemMode} + for _, attribute in pairs(attributes) do + test.socket.zigbee:__expect_send({mock_device.id, attribute:read(mock_fifth_child)}) + end +end) + +test.register_coroutine_test("Temperature reporting should create the appropriate events with fifth child device", function() + test.socket.zigbee:__queue_receive({mock_fifth_child.id, + Thermostat.attributes.LocalTemperature:build_test_attr_report(mock_fifth_child, 2100)}) + test.socket.capability:__expect_send(mock_fifth_child:generate_test_message("main", + capabilities.temperatureMeasurement.temperature({ + value = 21.0, + unit = "C" + }))) +end) + +test.register_coroutine_test("Thermostat mode reporting should create the appropriate events with fifth child device", function() + test.socket.zigbee:__queue_receive({mock_fifth_child.id, + Thermostat.attributes.SystemMode:build_test_attr_report(mock_fifth_child, + Thermostat.attributes.SystemMode.OFF)}) + test.socket.capability:__expect_send(mock_fifth_child:generate_test_message("main", capabilities.thermostatMode + .thermostatMode.away())) + test.socket.zigbee:__queue_receive({mock_fifth_child.id, + Thermostat.attributes.SystemMode:build_test_attr_report(mock_fifth_child, + Thermostat.attributes.SystemMode.HEAT)}) + test.socket.capability:__expect_send(mock_fifth_child:generate_test_message("main", capabilities.thermostatMode + .thermostatMode.heat())) +end) + +test.register_coroutine_test("ControlSequenceOfOperation reporting should create the appropriate events with fifth child device", function() + test.socket.zigbee:__queue_receive({mock_fifth_child.id, + Thermostat.attributes.ControlSequenceOfOperation:build_test_attr_report( + mock_fifth_child, 0x02)}) + test.socket.capability:__expect_send(mock_fifth_child:generate_test_message("main", + capabilities.thermostatMode.supportedThermostatModes({"away", "heat"}, { + visibility = { + displayed = false + } + }))) +end) + +test.register_coroutine_test("OccupiedHeatingSetpoint reporting shoulb create the appropriate events with fifth child device", function() + test.socket.zigbee:__queue_receive({mock_fifth_child.id, + Thermostat.attributes.OccupiedHeatingSetpoint:build_test_attr_report(mock_fifth_child, + 2100)}) + test.socket.capability:__expect_send(mock_fifth_child:generate_test_message("main", + capabilities.thermostatHeatingSetpoint.heatingSetpoint({ + value = 21.0, + unit = "C" + }))) +end) + +test.register_coroutine_test("Setting the heating setpoint should generate the appropriate messages with fifth child device", function() + test.socket.capability:__queue_receive({mock_fifth_child.id, { + component = "main", + capability = capabilities.thermostatHeatingSetpoint.ID, + command = "setHeatingSetpoint", + args = {21} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.OccupiedHeatingSetpoint:write(mock_fifth_child, 2100)}) +end) + +test.register_coroutine_test("Setting the thermostat mode to away should generate the appropriate messages with fifth child device", function() + test.socket.capability:__queue_receive({mock_fifth_child.id, { + component = "main", + capability = capabilities.thermostatMode.ID, + command = "setThermostatMode", + args = {"away"} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.SystemMode:write(mock_fifth_child, + Thermostat.attributes.SystemMode.OFF)}) +end) + +test.register_coroutine_test("Setting the thermostat mode to heat should generate the appropriate messages with fifth child device", function() + test.socket.capability:__queue_receive({mock_fifth_child.id, { + component = "main", + capability = capabilities.thermostatMode.ID, + command = "heat", + args = {} + }}) + test.socket.zigbee:__expect_send({mock_device.id, + Thermostat.attributes.SystemMode:write(mock_fifth_child, + Thermostat.attributes.SystemMode.HEAT)}) +end) + + test.run_registered_tests()