diff --git a/drivers/SmartThings/zigbee-thermostat/src/popp/init.lua b/drivers/SmartThings/zigbee-thermostat/src/popp/init.lua index f9c4e09406..e7f751cfe2 100644 --- a/drivers/SmartThings/zigbee-thermostat/src/popp/init.lua +++ b/drivers/SmartThings/zigbee-thermostat/src/popp/init.lua @@ -167,8 +167,8 @@ local turn_switch_on = function(driver, device) end end --- Custom setpoint command handler -local setpoint_cmd_handler = function(driver, device, cmd) +-- custom thermostatMode_handler +local thermostat_mode_handler = function(driver, device, cmd) local payload local mode = cmd.args.mode @@ -182,7 +182,7 @@ local setpoint_cmd_handler = function(driver, device, cmd) -- convert setpoint value into bytes e.g. 25.5 -> 2550 -> \x09\xF6 -> \xF6\x09 local p2 = last_setpointTemp & 0xFF local p3 = last_setpointTemp >> 8 - local type = device:get_latest_state("main", ThermostatMode.ID, ThermostatMode.thermostatMode.heat.NAME) or 'eco' + local type = 0x00 -- eco if mode == ThermostatMode.thermostatMode.heat.NAME then -- Setpoint type "1": the actuator will make a large movement to minimize reaction time to UI @@ -210,11 +210,31 @@ local setpoint_cmd_handler = function(driver, device, cmd) end -- temperature setpoint handler -local handle_set_setpoint = function(driver, device, command) +local thermostat_setpoint_handler = function(driver, device, command) local value = command.args.setpoint + local type = 0x00 -- default eco - -- write new setpoint - device:send(Thermostat.attributes.OccupiedHeatingSetpoint:write(device, value * 100)) + local mode = device:get_latest_state("main", ThermostatMode.ID, ThermostatMode.thermostatMode.NAME, 'eco') + + if mode == ThermostatMode.thermostatMode.heat.NAME then + -- Setpoint type "1": the actuator will make a large movement to minimize reaction time to UI + type = 0x01 + elseif mode == ThermostatMode.thermostatMode.eco.NAME then + -- Setpoint type "0": the behavior will be the same as setting the attribute "Occupied Heating Setpoint" to the same value + type = 0x00 + end + + -- prepare setpoint for correct 4 char dec format + local setpointTemp = math.floor(value * 100) + + -- convert setpoint value into bytes e.g. 25.5 -> 2550 -> \x09\xF6 -> \xF6\x09 + local p2 = setpointTemp & 0xFF + local p3 = setpointTemp >> 8 + + -- send thermostat setpoint command + local payload = string.char(type, p2, p3) + device:send(cluster_base.build_manufacturer_specific_command(device, Thermostat.ID, THERMOSTAT_SETPOINT_CMD_ID, + MFG_CODE, payload)) -- turn thermostat ventile on turn_switch_on(driver, device) @@ -252,13 +272,18 @@ end -- handle heating setpoint local thermostat_heating_set_point_attr_handler = function(driver, device, value, zb_rx) local point_value = value.value + local new_heating_setpoint = point_value / 100 + local last_heating_setpoint = device:get_latest_state("main", ThermostatHeatingSetpoint.ID, ThermostatHeatingSetpoint.heatingSetpoint.NAME) + device:emit_event(ThermostatHeatingSetpoint.heatingSetpoint({ - value = point_value / 100, + value = new_heating_setpoint, unit = "C" })) -- turn thermostat ventile on - turn_switch_on(driver, device) + if last_heating_setpoint ~= new_heating_setpoint then + turn_switch_on(driver, device) + end end -- handle external window open detection @@ -377,10 +402,10 @@ local popp_thermostat = { [capabilities.refresh.commands.refresh.NAME] = do_refresh }, [ThermostatHeatingSetpoint.ID] = { - [ThermostatHeatingSetpoint.commands.setHeatingSetpoint.NAME] = handle_set_setpoint + [ThermostatHeatingSetpoint.commands.setHeatingSetpoint.NAME] = thermostat_setpoint_handler }, [ThermostatMode.ID] = { - [ThermostatMode.commands.setThermostatMode.NAME] = setpoint_cmd_handler + [ThermostatMode.commands.setThermostatMode.NAME] = thermostat_mode_handler }, [Switch.ID] = { [Switch.commands.on.NAME] = switch_handler_factory('on'), diff --git a/drivers/SmartThings/zigbee-thermostat/src/test/test_popp_thermostat.lua b/drivers/SmartThings/zigbee-thermostat/src/test/test_popp_thermostat.lua index 084ff54c73..51048f07fa 100644 --- a/drivers/SmartThings/zigbee-thermostat/src/test/test_popp_thermostat.lua +++ b/drivers/SmartThings/zigbee-thermostat/src/test/test_popp_thermostat.lua @@ -18,6 +18,7 @@ local ThermostatMode = capabilities.thermostatMode local MFG_CODE = 0x1246 local ETRV_WINDOW_OPEN_DETECTION_ATTR_ID = 0x4000 local EXTERNAL_WINDOW_OPEN_DETECTION = 0x4003 +local THERMOSTAT_SETPOINT_CMD_ID = 0x40 -- utils local zigbee_test_utils = require "integration_test.zigbee_test_utils" @@ -81,7 +82,7 @@ test.register_coroutine_test( test.socket.zigbee:__expect_send( { mock_device.id, - Thermostat.attributes.OccupiedHeatingSetpoint:write(mock_device, 2750) + cluster_base.build_manufacturer_specific_command(mock_device, Thermostat.ID, THERMOSTAT_SETPOINT_CMD_ID, MFG_CODE, string.char(0x00, (math.floor(27.5 * 100) & 0xFF), (math.floor(27.5 * 100) >> 8))) } ) test.wait_for_events() @@ -96,6 +97,44 @@ test.register_coroutine_test( end ) +test.register_coroutine_test( + "External window open detection window open", + function() + test.timer.__create_and_queue_test_time_advance_timer(2, "oneshot") + test.socket.capability:__queue_receive( + { + mock_device.id, + { capability = "switch", component = "main", command = "on", args = {} } + } + ) + test.socket.zigbee:__expect_send( + { + mock_device.id, + cluster_base.write_manufacturer_specific_attribute(mock_device, Thermostat.ID, EXTERNAL_WINDOW_OPEN_DETECTION, MFG_CODE, data_types.Boolean, false) + } + ) + end +) + +test.register_coroutine_test( + "External window open detection window closed", + function() + test.timer.__create_and_queue_test_time_advance_timer(2, "oneshot") + test.socket.capability:__queue_receive( + { + mock_device.id, + { capability = "switch", component = "main", command = "off", args = {} } + } + ) + test.socket.zigbee:__expect_send( + { + mock_device.id, + cluster_base.write_manufacturer_specific_attribute(mock_device, Thermostat.ID, EXTERNAL_WINDOW_OPEN_DETECTION, MFG_CODE, data_types.Boolean, true) + } + ) + end +) + test.register_coroutine_test( "Configure should configure all necessary attributes", function() @@ -256,4 +295,73 @@ test.register_coroutine_test( end ) +test.register_coroutine_test( + "Setting the thermostat mode to heat should generate the correct zigbee messages", + function() + test.timer.__create_and_queue_test_time_advance_timer(2, "oneshot") + 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) + } + ) + + test.wait_for_events() + test.mock_time.advance_time(2) + test.socket.zigbee:__expect_send( + { + mock_device.id, + Thermostat.attributes.SystemMode:read(mock_device) + } + ) + end +) + +test.register_coroutine_test( + "Setting the thermostat mode to off should generate the correct zigbee messages", + function() + test.timer.__create_and_queue_test_time_advance_timer(2, "oneshot") + test.socket.capability:__queue_receive( + { + mock_device.id, + { + component = "main", + capability = capabilities.thermostatMode.ID, + command = "off", + args = {} + } + } + ) + test.socket.zigbee:__expect_send( + { + mock_device.id, + Thermostat.attributes.SystemMode:write(mock_device, + Thermostat.attributes.SystemMode.OFF) + } + ) + + test.wait_for_events() + test.mock_time.advance_time(2) + test.socket.zigbee:__expect_send( + { + mock_device.id, + Thermostat.attributes.SystemMode:read(mock_device) + } + ) + end +) + + test.run_registered_tests()