From d1489fe76f1f7f2ed012cc43be80ac8768d1cc15 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Tue, 27 Dec 2022 17:40:59 +0100 Subject: [PATCH] Add mysensors climate tests (#84619) --- tests/components/mysensors/conftest.py | 48 +++ .../fixtures/hvac_node_auto_state.json | 26 ++ .../fixtures/hvac_node_cool_state.json | 24 ++ .../fixtures/hvac_node_heat_state.json | 24 ++ tests/components/mysensors/test_climate.py | 391 ++++++++++++++++++ 5 files changed, 513 insertions(+) create mode 100644 tests/components/mysensors/fixtures/hvac_node_auto_state.json create mode 100644 tests/components/mysensors/fixtures/hvac_node_cool_state.json create mode 100644 tests/components/mysensors/fixtures/hvac_node_heat_state.json create mode 100644 tests/components/mysensors/test_climate.py diff --git a/tests/components/mysensors/conftest.py b/tests/components/mysensors/conftest.py index 773d8adf721..805bb13e767 100644 --- a/tests/components/mysensors/conftest.py +++ b/tests/components/mysensors/conftest.py @@ -234,6 +234,54 @@ def dimmer_node(gateway_nodes: dict[int, Sensor], dimmer_node_state: dict) -> Se return node +@pytest.fixture(name="hvac_node_auto_state", scope="session") +def hvac_node_auto_state_fixture() -> dict: + """Load the hvac node auto state.""" + return load_nodes_state("hvac_node_auto_state.json") + + +@pytest.fixture +def hvac_node_auto( + gateway_nodes: dict[int, Sensor], hvac_node_auto_state: dict +) -> Sensor: + """Load the hvac auto child node.""" + nodes = update_gateway_nodes(gateway_nodes, deepcopy(hvac_node_auto_state)) + node = nodes[1] + return node + + +@pytest.fixture(name="hvac_node_cool_state", scope="session") +def hvac_node_cool_state_fixture() -> dict: + """Load the hvac node cool state.""" + return load_nodes_state("hvac_node_cool_state.json") + + +@pytest.fixture +def hvac_node_cool( + gateway_nodes: dict[int, Sensor], hvac_node_cool_state: dict +) -> Sensor: + """Load the hvac cool child node.""" + nodes = update_gateway_nodes(gateway_nodes, deepcopy(hvac_node_cool_state)) + node = nodes[1] + return node + + +@pytest.fixture(name="hvac_node_heat_state", scope="session") +def hvac_node_heat_state_fixture() -> dict: + """Load the hvac node heat state.""" + return load_nodes_state("hvac_node_heat_state.json") + + +@pytest.fixture +def hvac_node_heat( + gateway_nodes: dict[int, Sensor], hvac_node_heat_state: dict +) -> Sensor: + """Load the hvac heat child node.""" + nodes = update_gateway_nodes(gateway_nodes, deepcopy(hvac_node_heat_state)) + node = nodes[1] + return node + + @pytest.fixture(name="power_sensor_state", scope="session") def power_sensor_state_fixture() -> dict: """Load the power sensor state.""" diff --git a/tests/components/mysensors/fixtures/hvac_node_auto_state.json b/tests/components/mysensors/fixtures/hvac_node_auto_state.json new file mode 100644 index 00000000000..9e7f137492e --- /dev/null +++ b/tests/components/mysensors/fixtures/hvac_node_auto_state.json @@ -0,0 +1,26 @@ +{ + "1": { + "sensor_id": 1, + "children": { + "1": { + "id": 1, + "type": 29, + "description": "", + "values": { + "0": "20.0", + "21": "Off", + "22": "Normal", + "44": "21.0", + "45": "19.0", + "46": "Auto" + } + } + }, + "type": 17, + "sketch_name": "HVAC Node", + "sketch_version": "1.0", + "battery_level": 0, + "protocol_version": "2.3.2", + "heartbeat": 0 + } +} diff --git a/tests/components/mysensors/fixtures/hvac_node_cool_state.json b/tests/components/mysensors/fixtures/hvac_node_cool_state.json new file mode 100644 index 00000000000..f8716b8373a --- /dev/null +++ b/tests/components/mysensors/fixtures/hvac_node_cool_state.json @@ -0,0 +1,24 @@ +{ + "1": { + "sensor_id": 1, + "children": { + "1": { + "id": 1, + "type": 29, + "description": "", + "values": { + "0": "20.0", + "21": "Off", + "22": "Normal", + "44": "21.0" + } + } + }, + "type": 17, + "sketch_name": "HVAC Node", + "sketch_version": "1.0", + "battery_level": 0, + "protocol_version": "2.3.2", + "heartbeat": 0 + } +} diff --git a/tests/components/mysensors/fixtures/hvac_node_heat_state.json b/tests/components/mysensors/fixtures/hvac_node_heat_state.json new file mode 100644 index 00000000000..c12210c91f5 --- /dev/null +++ b/tests/components/mysensors/fixtures/hvac_node_heat_state.json @@ -0,0 +1,24 @@ +{ + "1": { + "sensor_id": 1, + "children": { + "1": { + "id": 1, + "type": 29, + "description": "", + "values": { + "0": "20.0", + "21": "Off", + "22": "Normal", + "45": "19.0" + } + } + }, + "type": 17, + "sketch_name": "HVAC Node", + "sketch_version": "1.0", + "battery_level": 0, + "protocol_version": "2.3.2", + "heartbeat": 0 + } +} diff --git a/tests/components/mysensors/test_climate.py b/tests/components/mysensors/test_climate.py new file mode 100644 index 00000000000..437a3010ce6 --- /dev/null +++ b/tests/components/mysensors/test_climate.py @@ -0,0 +1,391 @@ +"""Provide tests for mysensors climate platform.""" +from __future__ import annotations + +from collections.abc import Callable +from unittest.mock import MagicMock, call + +from mysensors.sensor import Sensor + +from homeassistant.components.climate import ( + ATTR_CURRENT_TEMPERATURE, + ATTR_FAN_MODE, + ATTR_HVAC_MODE, + ATTR_TARGET_TEMP_HIGH, + ATTR_TARGET_TEMP_LOW, + ATTR_TEMPERATURE, + DOMAIN as CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + SERVICE_SET_HVAC_MODE, + SERVICE_SET_TEMPERATURE, + HVACMode, +) +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.core import HomeAssistant + + +async def test_hvac_node_auto( + hass: HomeAssistant, + hvac_node_auto: Sensor, + receive_message: Callable[[str], None], + transport_write: MagicMock, +) -> None: + """Test a hvac auto node.""" + entity_id = "climate.hvac_node_1_1" + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.OFF + + # Test set hvac mode auto + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: HVACMode.AUTO}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;21;AutoChangeOver\n") + + receive_message("1;1;1;0;21;AutoChangeOver\n") + # the integration adds multiple jobs to do the update currently + await hass.async_block_till_done() + await hass.async_block_till_done() + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.AUTO + assert state.attributes[ATTR_TARGET_TEMP_HIGH] == 21.0 + assert state.attributes[ATTR_TARGET_TEMP_LOW] == 19.0 + assert state.attributes[ATTR_FAN_MODE] == "Normal" + assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 20.0 + + transport_write.reset_mock() + + # Test set low/high target temperature + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + { + ATTR_ENTITY_ID: entity_id, + ATTR_TARGET_TEMP_HIGH: 22.0, + ATTR_TARGET_TEMP_LOW: 20.0, + }, + blocking=True, + ) + + assert transport_write.call_count == 2 + assert transport_write.call_args_list[0] == call("1;1;1;1;45;20.0\n") + assert transport_write.call_args_list[1] == call("1;1;1;1;44;22.0\n") + + receive_message("1;1;1;0;45;20.0\n") + receive_message("1;1;1;0;44;22.0\n") + # the integration adds multiple jobs to do the update currently + await hass.async_block_till_done() + await hass.async_block_till_done() + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.AUTO + assert state.attributes[ATTR_TARGET_TEMP_HIGH] == 22.0 + assert state.attributes[ATTR_TARGET_TEMP_LOW] == 20.0 + assert state.attributes[ATTR_FAN_MODE] == "Normal" + + transport_write.reset_mock() + + # Test set fan mode + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + { + ATTR_ENTITY_ID: entity_id, + ATTR_FAN_MODE: "Max", + }, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;22;Max\n") + + receive_message("1;1;1;0;22;Max\n") + # the integration adds multiple jobs to do the update currently + await hass.async_block_till_done() + await hass.async_block_till_done() + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.AUTO + assert state.attributes[ATTR_FAN_MODE] == "Max" + + transport_write.reset_mock() + + # Test set hvac mode off + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: HVACMode.OFF}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;21;Off\n") + + receive_message("1;1;1;0;21;Off\n") + # the integration adds multiple jobs to do the update currently + await hass.async_block_till_done() + await hass.async_block_till_done() + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.OFF + + +async def test_hvac_node_heat( + hass: HomeAssistant, + hvac_node_heat: Sensor, + receive_message: Callable[[str], None], + transport_write: MagicMock, +) -> None: + """Test a hvac heat node.""" + entity_id = "climate.hvac_node_1_1" + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.OFF + + # Test set hvac mode heat + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: HVACMode.HEAT}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;21;HeatOn\n") + + receive_message("1;1;1;0;21;HeatOn\n") + # the integration adds multiple jobs to do the update currently + await hass.async_block_till_done() + await hass.async_block_till_done() + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.HEAT + assert state.attributes[ATTR_TEMPERATURE] == 19.0 + assert state.attributes[ATTR_FAN_MODE] == "Normal" + assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 20.0 + + transport_write.reset_mock() + + # Test set low/high target temperature + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + { + ATTR_ENTITY_ID: entity_id, + ATTR_TEMPERATURE: 20.0, + }, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;45;20.0\n") + + receive_message("1;1;1;0;45;20.0\n") + # the integration adds multiple jobs to do the update currently + await hass.async_block_till_done() + await hass.async_block_till_done() + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.HEAT + assert state.attributes[ATTR_TEMPERATURE] == 20.0 + assert state.attributes[ATTR_FAN_MODE] == "Normal" + + transport_write.reset_mock() + + # Test set fan mode + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + { + ATTR_ENTITY_ID: entity_id, + ATTR_FAN_MODE: "Min", + }, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;22;Min\n") + + receive_message("1;1;1;0;22;Min\n") + # the integration adds multiple jobs to do the update currently + await hass.async_block_till_done() + await hass.async_block_till_done() + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.HEAT + assert state.attributes[ATTR_FAN_MODE] == "Min" + + transport_write.reset_mock() + + # Test set hvac mode off + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: HVACMode.OFF}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;21;Off\n") + + receive_message("1;1;1;0;21;Off\n") + # the integration adds multiple jobs to do the update currently + await hass.async_block_till_done() + await hass.async_block_till_done() + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.OFF + + +async def test_hvac_node_cool( + hass: HomeAssistant, + hvac_node_cool: Sensor, + receive_message: Callable[[str], None], + transport_write: MagicMock, +) -> None: + """Test a hvac cool node.""" + entity_id = "climate.hvac_node_1_1" + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.OFF + + # Test set hvac mode heat + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: HVACMode.COOL}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;21;CoolOn\n") + + receive_message("1;1;1;0;21;CoolOn\n") + # the integration adds multiple jobs to do the update currently + await hass.async_block_till_done() + await hass.async_block_till_done() + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.COOL + assert state.attributes[ATTR_TEMPERATURE] == 21.0 + assert state.attributes[ATTR_FAN_MODE] == "Normal" + assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 20.0 + + transport_write.reset_mock() + + # Test set low/high target temperature + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + { + ATTR_ENTITY_ID: entity_id, + ATTR_TEMPERATURE: 20.0, + }, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;44;20.0\n") + + receive_message("1;1;1;0;44;20.0\n") + # the integration adds multiple jobs to do the update currently + await hass.async_block_till_done() + await hass.async_block_till_done() + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.COOL + assert state.attributes[ATTR_TEMPERATURE] == 20.0 + assert state.attributes[ATTR_FAN_MODE] == "Normal" + + transport_write.reset_mock() + + # Test set fan mode + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + { + ATTR_ENTITY_ID: entity_id, + ATTR_FAN_MODE: "Auto", + }, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;22;Auto\n") + + receive_message("1;1;1;0;22;Auto\n") + # the integration adds multiple jobs to do the update currently + await hass.async_block_till_done() + await hass.async_block_till_done() + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.COOL + assert state.attributes[ATTR_FAN_MODE] == "Auto" + + transport_write.reset_mock() + + # Test set hvac mode off + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: HVACMode.OFF}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;21;Off\n") + + receive_message("1;1;1;0;21;Off\n") + # the integration adds multiple jobs to do the update currently + await hass.async_block_till_done() + await hass.async_block_till_done() + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.OFF