From 7a428a66bd1cb4cfa22d18c54b913f41802a574f Mon Sep 17 00:00:00 2001 From: Markus Adrario Date: Tue, 10 Jun 2025 11:41:13 -0600 Subject: [PATCH] Add support for HeatIt Thermostat TF056 to homee (#145515) * adapt climate for Heatit TF 056 * add sensors & numbers for Heatit TF056 * Add select for Heatit TF056 * Adapt climat tests for changes * Fix sentence case * fix review comments * Update homeassistant/components/homee/climate.py Co-authored-by: Joost Lekkerkerker * fix tests * update diagnostics snapshot for this change --------- Co-authored-by: Franck Nijhof Co-authored-by: Joost Lekkerkerker Co-authored-by: Franck Nijhof --- homeassistant/components/homee/climate.py | 32 +- homeassistant/components/homee/number.py | 29 ++ homeassistant/components/homee/select.py | 5 + homeassistant/components/homee/sensor.py | 10 + homeassistant/components/homee/strings.json | 34 ++ tests/components/homee/fixtures/numbers.json | 147 +++++++ tests/components/homee/fixtures/selects.json | 21 + tests/components/homee/fixtures/sensors.json | 56 +++ .../thermostat_with_alternate_preset.json | 102 +++++ .../homee/snapshots/test_climate.ambr | 76 ++++ .../homee/snapshots/test_diagnostics.ambr | 294 +++++++++++++ .../homee/snapshots/test_number.ambr | 407 ++++++++++++++++++ .../homee/snapshots/test_select.ambr | 57 +++ .../homee/snapshots/test_sensor.ambr | 56 +++ tests/components/homee/test_climate.py | 85 ++++ 15 files changed, 1402 insertions(+), 9 deletions(-) create mode 100644 tests/components/homee/fixtures/thermostat_with_alternate_preset.json diff --git a/homeassistant/components/homee/climate.py b/homeassistant/components/homee/climate.py index 3411d31461c..f6027522243 100644 --- a/homeassistant/components/homee/climate.py +++ b/homeassistant/components/homee/climate.py @@ -83,7 +83,7 @@ class HomeeClimate(HomeeNodeEntity, ClimateEntity): if ClimateEntityFeature.TURN_OFF in self.supported_features and ( self._heating_mode is not None ): - if self._heating_mode.current_value == 0: + if self._heating_mode.current_value == self._heating_mode.minimum: return HVACMode.OFF return HVACMode.HEAT @@ -91,7 +91,10 @@ class HomeeClimate(HomeeNodeEntity, ClimateEntity): @property def hvac_action(self) -> HVACAction: """Return the hvac action.""" - if self._heating_mode is not None and self._heating_mode.current_value == 0: + if ( + self._heating_mode is not None + and self._heating_mode.current_value == self._heating_mode.minimum + ): return HVACAction.OFF if ( @@ -110,10 +113,12 @@ class HomeeClimate(HomeeNodeEntity, ClimateEntity): if ( ClimateEntityFeature.PRESET_MODE in self.supported_features and self._heating_mode is not None - and self._heating_mode.current_value > 0 + and self._heating_mode.current_value > self._heating_mode.minimum ): assert self._attr_preset_modes is not None - return self._attr_preset_modes[int(self._heating_mode.current_value) - 1] + return self._attr_preset_modes[ + int(self._heating_mode.current_value - self._heating_mode.minimum) - 1 + ] return PRESET_NONE @@ -147,14 +152,16 @@ class HomeeClimate(HomeeNodeEntity, ClimateEntity): # Currently only HEAT and OFF are supported. assert self._heating_mode is not None await self.async_set_homee_value( - self._heating_mode, float(hvac_mode == HVACMode.HEAT) + self._heating_mode, + (hvac_mode == HVACMode.HEAT) + self._heating_mode.minimum, ) async def async_set_preset_mode(self, preset_mode: str) -> None: """Set new target preset mode.""" assert self._heating_mode is not None and self._attr_preset_modes is not None await self.async_set_homee_value( - self._heating_mode, self._attr_preset_modes.index(preset_mode) + 1 + self._heating_mode, + self._attr_preset_modes.index(preset_mode) + self._heating_mode.minimum + 1, ) async def async_set_temperature(self, **kwargs: Any) -> None: @@ -168,12 +175,16 @@ class HomeeClimate(HomeeNodeEntity, ClimateEntity): async def async_turn_on(self) -> None: """Turn the entity on.""" assert self._heating_mode is not None - await self.async_set_homee_value(self._heating_mode, 1) + await self.async_set_homee_value( + self._heating_mode, 1 + self._heating_mode.minimum + ) async def async_turn_off(self) -> None: """Turn the entity on.""" assert self._heating_mode is not None - await self.async_set_homee_value(self._heating_mode, 0) + await self.async_set_homee_value( + self._heating_mode, 0 + self._heating_mode.minimum + ) def get_climate_features( @@ -193,7 +204,10 @@ def get_climate_features( if attribute.maximum > 1: # Node supports more modes than off and heating. features |= ClimateEntityFeature.PRESET_MODE - preset_modes.extend([PRESET_ECO, PRESET_BOOST, PRESET_MANUAL]) + if attribute.maximum < 5: + preset_modes.extend([PRESET_ECO, PRESET_BOOST, PRESET_MANUAL]) + else: + preset_modes.extend([PRESET_ECO]) if len(preset_modes) > 0: preset_modes.insert(0, PRESET_NONE) diff --git a/homeassistant/components/homee/number.py b/homeassistant/components/homee/number.py index 231c2ecac94..5b824f18851 100644 --- a/homeassistant/components/homee/number.py +++ b/homeassistant/components/homee/number.py @@ -31,6 +31,22 @@ class HomeeNumberEntityDescription(NumberEntityDescription): NUMBER_DESCRIPTIONS = { + AttributeType.BUTTON_BRIGHTNESS_ACTIVE: HomeeNumberEntityDescription( + key="button_brightness_active", + entity_category=EntityCategory.CONFIG, + ), + AttributeType.BUTTON_BRIGHTNESS_DIMMED: HomeeNumberEntityDescription( + key="button_brightness_dimmed", + entity_category=EntityCategory.CONFIG, + ), + AttributeType.DISPLAY_BRIGHTNESS_ACTIVE: HomeeNumberEntityDescription( + key="display_brightness_active", + entity_category=EntityCategory.CONFIG, + ), + AttributeType.DISPLAY_BRIGHTNESS_DIMMED: HomeeNumberEntityDescription( + key="display_brightness_dimmed", + entity_category=EntityCategory.CONFIG, + ), AttributeType.DOWN_POSITION: HomeeNumberEntityDescription( key="down_position", entity_category=EntityCategory.CONFIG, @@ -48,6 +64,14 @@ NUMBER_DESCRIPTIONS = { key="endposition_configuration", entity_category=EntityCategory.CONFIG, ), + AttributeType.EXTERNAL_TEMPERATURE_OFFSET: HomeeNumberEntityDescription( + key="external_temperature_offset", + entity_category=EntityCategory.CONFIG, + ), + AttributeType.FLOOR_TEMPERATURE_OFFSET: HomeeNumberEntityDescription( + key="floor_temperature_offset", + entity_category=EntityCategory.CONFIG, + ), AttributeType.MOTION_ALARM_CANCELATION_DELAY: HomeeNumberEntityDescription( key="motion_alarm_cancelation_delay", device_class=NumberDeviceClass.DURATION, @@ -83,6 +107,11 @@ NUMBER_DESCRIPTIONS = { key="temperature_offset", entity_category=EntityCategory.CONFIG, ), + AttributeType.TEMPERATURE_REPORT_INTERVAL: HomeeNumberEntityDescription( + key="temperature_report_interval", + device_class=NumberDeviceClass.DURATION, + entity_category=EntityCategory.CONFIG, + ), AttributeType.UP_TIME: HomeeNumberEntityDescription( key="up_time", device_class=NumberDeviceClass.DURATION, diff --git a/homeassistant/components/homee/select.py b/homeassistant/components/homee/select.py index 70c7972bbda..694d1bc7456 100644 --- a/homeassistant/components/homee/select.py +++ b/homeassistant/components/homee/select.py @@ -14,6 +14,11 @@ from .entity import HomeeEntity PARALLEL_UPDATES = 0 SELECT_DESCRIPTIONS: dict[AttributeType, SelectEntityDescription] = { + AttributeType.DISPLAY_TEMPERATURE_SELECTION: SelectEntityDescription( + key="display_temperature_selection", + options=["target", "current"], + entity_category=EntityCategory.CONFIG, + ), AttributeType.REPEATER_MODE: SelectEntityDescription( key="repeater_mode", options=["off", "level1", "level2"], diff --git a/homeassistant/components/homee/sensor.py b/homeassistant/components/homee/sensor.py index ab1d5bd4f49..f977f705eb8 100644 --- a/homeassistant/components/homee/sensor.py +++ b/homeassistant/components/homee/sensor.py @@ -129,6 +129,16 @@ SENSOR_DESCRIPTIONS: dict[AttributeType, HomeeSensorEntityDescription] = { state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), + AttributeType.EXTERNAL_TEMPERATURE: HomeeSensorEntityDescription( + key="external_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), + AttributeType.FLOOR_TEMPERATURE: HomeeSensorEntityDescription( + key="floor_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), AttributeType.INDOOR_RELATIVE_HUMIDITY: HomeeSensorEntityDescription( key="indoor_humidity", device_class=SensorDeviceClass.HUMIDITY, diff --git a/homeassistant/components/homee/strings.json b/homeassistant/components/homee/strings.json index 19c21e662e0..e2e4c6659d6 100644 --- a/homeassistant/components/homee/strings.json +++ b/homeassistant/components/homee/strings.json @@ -199,6 +199,18 @@ } }, "number": { + "button_brightness_active": { + "name": "Button brightness (active)" + }, + "button_brightness_dimmed": { + "name": "Button brightness (dimmed)" + }, + "display_brightness_active": { + "name": "Display brightness (active)" + }, + "display_brightness_dimmed": { + "name": "Display brightness (dimmed)" + }, "down_position": { "name": "Down position" }, @@ -211,6 +223,12 @@ "endposition_configuration": { "name": "End position" }, + "external_temperature_offset": { + "name": "External temperature offset" + }, + "floor_temperature_offset": { + "name": "Floor temperature offset" + }, "motion_alarm_cancelation_delay": { "name": "Motion alarm delay" }, @@ -235,6 +253,9 @@ "temperature_offset": { "name": "Temperature offset" }, + "temperature_report_interval": { + "name": "Temperature report interval" + }, "up_time": { "name": "Up-movement duration" }, @@ -246,6 +267,13 @@ } }, "select": { + "display_temperature_selection": { + "name": "Displayed temperature", + "state": { + "target": "Target", + "current": "Measured" + } + }, "repeater_mode": { "name": "Repeater mode", "state": { @@ -277,6 +305,12 @@ "exhaust_motor_revs": { "name": "Exhaust motor speed" }, + "external_temperature": { + "name": "External temperature" + }, + "floor_temperature": { + "name": "Floor temperature" + }, "indoor_humidity": { "name": "Indoor humidity" }, diff --git a/tests/components/homee/fixtures/numbers.json b/tests/components/homee/fixtures/numbers.json index fd00ca4b5bd..6edd4903a8c 100644 --- a/tests/components/homee/fixtures/numbers.json +++ b/tests/components/homee/fixtures/numbers.json @@ -353,6 +353,153 @@ "based_on": 1, "data": "", "name": "" + }, + { + "id": 17, + "node_id": 1, + "instance": 0, + "minimum": -6, + "maximum": 6, + "current_value": 0.0, + "target_value": 0.0, + "last_value": 0.0, + "unit": "°C", + "step_value": 0.1, + "editable": 1, + "type": 340, + "state": 1, + "last_changed": 1624806665, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "" + }, + { + "id": 18, + "node_id": 1, + "instance": 0, + "minimum": -6, + "maximum": 6, + "current_value": 0.0, + "target_value": 0.0, + "last_value": 0.0, + "unit": "°C", + "step_value": 0.1, + "editable": 1, + "type": 341, + "state": 1, + "last_changed": 1624806728, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "" + }, + { + "id": 19, + "node_id": 1, + "instance": 0, + "minimum": 0, + "maximum": 32767, + "current_value": 60.0, + "target_value": 60.0, + "last_value": 60.0, + "unit": "s", + "step_value": 1.0, + "editable": 1, + "type": 59, + "state": 1, + "last_changed": 1624806729, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "" + }, + { + "id": 20, + "node_id": 1, + "instance": 0, + "minimum": 0, + "maximum": 100, + "current_value": 78.0, + "target_value": 78.0, + "last_value": 78.0, + "unit": "%", + "step_value": 1.0, + "editable": 1, + "type": 342, + "state": 1, + "last_changed": 1624806729, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "" + }, + { + "id": 21, + "node_id": 1, + "instance": 0, + "minimum": 0, + "maximum": 100, + "current_value": 23.0, + "target_value": 23.0, + "last_value": 23.0, + "unit": "%", + "step_value": 1.0, + "editable": 1, + "type": 343, + "state": 1, + "last_changed": 1624806729, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "" + }, + { + "id": 22, + "node_id": 1, + "instance": 0, + "minimum": 0, + "maximum": 100, + "current_value": 100.0, + "target_value": 100.0, + "last_value": 100.0, + "unit": "%", + "step_value": 1.0, + "editable": 1, + "type": 344, + "state": 1, + "last_changed": 1624806728, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "" + }, + { + "id": 23, + "node_id": 1, + "instance": 0, + "minimum": 0, + "maximum": 100, + "current_value": 23.0, + "target_value": 23.0, + "last_value": 50.0, + "unit": "%", + "step_value": 1.0, + "editable": 1, + "type": 345, + "state": 1, + "last_changed": 1624806728, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "" } ] } diff --git a/tests/components/homee/fixtures/selects.json b/tests/components/homee/fixtures/selects.json index 27adcf07298..2d42e37c7ce 100644 --- a/tests/components/homee/fixtures/selects.json +++ b/tests/components/homee/fixtures/selects.json @@ -38,6 +38,27 @@ "based_on": 1, "data": "", "name": "" + }, + { + "id": 2, + "node_id": 1, + "instance": 0, + "minimum": 0, + "maximum": 1, + "current_value": 0.0, + "target_value": 0.0, + "last_value": 0.0, + "unit": "", + "step_value": 1.0, + "editable": 1, + "type": 346, + "state": 1, + "last_changed": 1624806728, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "" } ] } diff --git a/tests/components/homee/fixtures/sensors.json b/tests/components/homee/fixtures/sensors.json index bcc36a85ee7..50daa59c99f 100644 --- a/tests/components/homee/fixtures/sensors.json +++ b/tests/components/homee/fixtures/sensors.json @@ -731,6 +731,62 @@ "based_on": 1, "data": "", "name": "" + }, + { + "id": 34, + "node_id": 1, + "instance": 0, + "minimum": -50, + "maximum": 125, + "current_value": 23.6, + "target_value": 23.6, + "last_value": 23.6, + "unit": "°C", + "step_value": 1.0, + "editable": 0, + "type": 315, + "state": 1, + "last_changed": 1747078279, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "", + "options": { + "history": { + "day": 1, + "week": 26, + "month": 6 + } + } + }, + { + "id": 35, + "node_id": 1, + "instance": 0, + "minimum": -50, + "maximum": 125, + "current_value": 0.0, + "target_value": 0.0, + "last_value": 0.0, + "unit": "°C", + "step_value": 1.0, + "editable": 0, + "type": 316, + "state": 1, + "last_changed": 1747078280, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "", + "options": { + "history": { + "day": 1, + "week": 26, + "month": 6 + } + } } ] } diff --git a/tests/components/homee/fixtures/thermostat_with_alternate_preset.json b/tests/components/homee/fixtures/thermostat_with_alternate_preset.json new file mode 100644 index 00000000000..9bd0b64451e --- /dev/null +++ b/tests/components/homee/fixtures/thermostat_with_alternate_preset.json @@ -0,0 +1,102 @@ +{ + "id": 5, + "name": "Test Thermostat 5", + "profile": 3033, + "image": "default", + "favorite": 0, + "order": 32, + "protocol": 1, + "routing": 0, + "state": 1, + "state_changed": 1712840187, + "added": 1655274291, + "history": 1, + "cube_type": 1, + "note": "", + "services": 7, + "phonetic_name": "", + "owner": 2, + "security": 0, + "attributes": [ + { + "id": 1, + "node_id": 5, + "instance": 0, + "minimum": 10, + "maximum": 32, + "current_value": 12.0, + "target_value": 13.0, + "last_value": 12.0, + "unit": "°C", + "step_value": 0.5, + "editable": 1, + "type": 6, + "state": 2, + "last_changed": 1713695529, + "changed_by": 3, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "", + "options": { + "automations": ["step"], + "history": { + "day": 35, + "week": 5, + "month": 1, + "stepped": true + } + } + }, + { + "id": 2, + "node_id": 5, + "instance": 0, + "minimum": -50, + "maximum": 125, + "current_value": 19.55, + "target_value": 19.55, + "last_value": 21.07, + "unit": "°C", + "step_value": 0.1, + "editable": 0, + "type": 5, + "state": 1, + "last_changed": 1713695528, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "", + "options": { + "observed_by": [240], + "history": { + "day": 1, + "week": 26, + "month": 6 + } + } + }, + { + "id": 3, + "node_id": 5, + "instance": 0, + "minimum": 10, + "maximum": 12, + "current_value": 11.0, + "target_value": 11.0, + "last_value": 11.0, + "unit": "", + "step_value": 1.0, + "editable": 1, + "type": 258, + "state": 1, + "last_changed": 1746379402, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "" + } + ] +} diff --git a/tests/components/homee/snapshots/test_climate.ambr b/tests/components/homee/snapshots/test_climate.ambr index 2c94c5ef8e0..3a1ec23a56d 100644 --- a/tests/components/homee/snapshots/test_climate.ambr +++ b/tests/components/homee/snapshots/test_climate.ambr @@ -276,3 +276,79 @@ 'state': 'heat', }) # --- +# name: test_climate_snapshot[climate.test_thermostat_5-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'hvac_modes': list([ + , + , + ]), + 'max_temp': 32, + 'min_temp': 10, + 'preset_modes': list([ + 'none', + 'eco', + ]), + 'target_temp_step': 0.5, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'climate', + 'entity_category': None, + 'entity_id': 'climate.test_thermostat_5', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': None, + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': , + 'translation_key': 'homee', + 'unique_id': '00055511EECC-5-1', + 'unit_of_measurement': None, + }) +# --- +# name: test_climate_snapshot[climate.test_thermostat_5-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'current_temperature': 19.6, + 'friendly_name': 'Test Thermostat 5', + 'hvac_action': , + 'hvac_modes': list([ + , + , + ]), + 'max_temp': 32, + 'min_temp': 10, + 'preset_mode': 'none', + 'preset_modes': list([ + 'none', + 'eco', + ]), + 'supported_features': , + 'target_temp_step': 0.5, + 'temperature': 12.0, + }), + 'context': , + 'entity_id': 'climate.test_thermostat_5', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'heat', + }) +# --- diff --git a/tests/components/homee/snapshots/test_diagnostics.ambr b/tests/components/homee/snapshots/test_diagnostics.ambr index e3e66980037..76d3f426e17 100644 --- a/tests/components/homee/snapshots/test_diagnostics.ambr +++ b/tests/components/homee/snapshots/test_diagnostics.ambr @@ -342,6 +342,153 @@ 'type': 338, 'unit': 'n/a', }), + dict({ + 'based_on': 1, + 'changed_by': 1, + 'changed_by_id': 0, + 'current_value': 0.0, + 'data': '', + 'editable': 1, + 'id': 17, + 'instance': 0, + 'last_changed': 1624806665, + 'last_value': 0.0, + 'maximum': 6, + 'minimum': -6, + 'name': '', + 'node_id': 1, + 'state': 1, + 'step_value': 0.1, + 'target_value': 0.0, + 'type': 340, + 'unit': '°C', + }), + dict({ + 'based_on': 1, + 'changed_by': 1, + 'changed_by_id': 0, + 'current_value': 0.0, + 'data': '', + 'editable': 1, + 'id': 18, + 'instance': 0, + 'last_changed': 1624806728, + 'last_value': 0.0, + 'maximum': 6, + 'minimum': -6, + 'name': '', + 'node_id': 1, + 'state': 1, + 'step_value': 0.1, + 'target_value': 0.0, + 'type': 341, + 'unit': '°C', + }), + dict({ + 'based_on': 1, + 'changed_by': 1, + 'changed_by_id': 0, + 'current_value': 60.0, + 'data': '', + 'editable': 1, + 'id': 19, + 'instance': 0, + 'last_changed': 1624806729, + 'last_value': 60.0, + 'maximum': 32767, + 'minimum': 0, + 'name': '', + 'node_id': 1, + 'state': 1, + 'step_value': 1.0, + 'target_value': 60.0, + 'type': 59, + 'unit': 's', + }), + dict({ + 'based_on': 1, + 'changed_by': 1, + 'changed_by_id': 0, + 'current_value': 78.0, + 'data': '', + 'editable': 1, + 'id': 20, + 'instance': 0, + 'last_changed': 1624806729, + 'last_value': 78.0, + 'maximum': 100, + 'minimum': 0, + 'name': '', + 'node_id': 1, + 'state': 1, + 'step_value': 1.0, + 'target_value': 78.0, + 'type': 342, + 'unit': '%', + }), + dict({ + 'based_on': 1, + 'changed_by': 1, + 'changed_by_id': 0, + 'current_value': 23.0, + 'data': '', + 'editable': 1, + 'id': 21, + 'instance': 0, + 'last_changed': 1624806729, + 'last_value': 23.0, + 'maximum': 100, + 'minimum': 0, + 'name': '', + 'node_id': 1, + 'state': 1, + 'step_value': 1.0, + 'target_value': 23.0, + 'type': 343, + 'unit': '%', + }), + dict({ + 'based_on': 1, + 'changed_by': 1, + 'changed_by_id': 0, + 'current_value': 100.0, + 'data': '', + 'editable': 1, + 'id': 22, + 'instance': 0, + 'last_changed': 1624806728, + 'last_value': 100.0, + 'maximum': 100, + 'minimum': 0, + 'name': '', + 'node_id': 1, + 'state': 1, + 'step_value': 1.0, + 'target_value': 100.0, + 'type': 344, + 'unit': '%', + }), + dict({ + 'based_on': 1, + 'changed_by': 1, + 'changed_by_id': 0, + 'current_value': 23.0, + 'data': '', + 'editable': 1, + 'id': 23, + 'instance': 0, + 'last_changed': 1624806728, + 'last_value': 50.0, + 'maximum': 100, + 'minimum': 0, + 'name': '', + 'node_id': 1, + 'state': 1, + 'step_value': 1.0, + 'target_value': 23.0, + 'type': 345, + 'unit': '%', + }), ]), 'cube_type': 3, 'favorite': 0, @@ -920,6 +1067,153 @@ 'type': 338, 'unit': 'n/a', }), + dict({ + 'based_on': 1, + 'changed_by': 1, + 'changed_by_id': 0, + 'current_value': 0.0, + 'data': '', + 'editable': 1, + 'id': 17, + 'instance': 0, + 'last_changed': 1624806665, + 'last_value': 0.0, + 'maximum': 6, + 'minimum': -6, + 'name': '', + 'node_id': 1, + 'state': 1, + 'step_value': 0.1, + 'target_value': 0.0, + 'type': 340, + 'unit': '°C', + }), + dict({ + 'based_on': 1, + 'changed_by': 1, + 'changed_by_id': 0, + 'current_value': 0.0, + 'data': '', + 'editable': 1, + 'id': 18, + 'instance': 0, + 'last_changed': 1624806728, + 'last_value': 0.0, + 'maximum': 6, + 'minimum': -6, + 'name': '', + 'node_id': 1, + 'state': 1, + 'step_value': 0.1, + 'target_value': 0.0, + 'type': 341, + 'unit': '°C', + }), + dict({ + 'based_on': 1, + 'changed_by': 1, + 'changed_by_id': 0, + 'current_value': 60.0, + 'data': '', + 'editable': 1, + 'id': 19, + 'instance': 0, + 'last_changed': 1624806729, + 'last_value': 60.0, + 'maximum': 32767, + 'minimum': 0, + 'name': '', + 'node_id': 1, + 'state': 1, + 'step_value': 1.0, + 'target_value': 60.0, + 'type': 59, + 'unit': 's', + }), + dict({ + 'based_on': 1, + 'changed_by': 1, + 'changed_by_id': 0, + 'current_value': 78.0, + 'data': '', + 'editable': 1, + 'id': 20, + 'instance': 0, + 'last_changed': 1624806729, + 'last_value': 78.0, + 'maximum': 100, + 'minimum': 0, + 'name': '', + 'node_id': 1, + 'state': 1, + 'step_value': 1.0, + 'target_value': 78.0, + 'type': 342, + 'unit': '%', + }), + dict({ + 'based_on': 1, + 'changed_by': 1, + 'changed_by_id': 0, + 'current_value': 23.0, + 'data': '', + 'editable': 1, + 'id': 21, + 'instance': 0, + 'last_changed': 1624806729, + 'last_value': 23.0, + 'maximum': 100, + 'minimum': 0, + 'name': '', + 'node_id': 1, + 'state': 1, + 'step_value': 1.0, + 'target_value': 23.0, + 'type': 343, + 'unit': '%', + }), + dict({ + 'based_on': 1, + 'changed_by': 1, + 'changed_by_id': 0, + 'current_value': 100.0, + 'data': '', + 'editable': 1, + 'id': 22, + 'instance': 0, + 'last_changed': 1624806728, + 'last_value': 100.0, + 'maximum': 100, + 'minimum': 0, + 'name': '', + 'node_id': 1, + 'state': 1, + 'step_value': 1.0, + 'target_value': 100.0, + 'type': 344, + 'unit': '%', + }), + dict({ + 'based_on': 1, + 'changed_by': 1, + 'changed_by_id': 0, + 'current_value': 23.0, + 'data': '', + 'editable': 1, + 'id': 23, + 'instance': 0, + 'last_changed': 1624806728, + 'last_value': 50.0, + 'maximum': 100, + 'minimum': 0, + 'name': '', + 'node_id': 1, + 'state': 1, + 'step_value': 1.0, + 'target_value': 23.0, + 'type': 345, + 'unit': '%', + }), ]), 'cube_type': 3, 'favorite': 0, diff --git a/tests/components/homee/snapshots/test_number.ambr b/tests/components/homee/snapshots/test_number.ambr index 53569fe8734..5f0981bae7f 100644 --- a/tests/components/homee/snapshots/test_number.ambr +++ b/tests/components/homee/snapshots/test_number.ambr @@ -1,4 +1,236 @@ # serializer version: 1 +# name: test_number_snapshot[number.test_number_button_brightness_active-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'max': 100, + 'min': 0, + 'mode': , + 'step': 1.0, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': , + 'entity_id': 'number.test_number_button_brightness_active', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Button brightness (active)', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'button_brightness_active', + 'unique_id': '00055511EECC-1-22', + 'unit_of_measurement': '%', + }) +# --- +# name: test_number_snapshot[number.test_number_button_brightness_active-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Test Number Button brightness (active)', + 'max': 100, + 'min': 0, + 'mode': , + 'step': 1.0, + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'number.test_number_button_brightness_active', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '100.0', + }) +# --- +# name: test_number_snapshot[number.test_number_button_brightness_dimmed-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'max': 100, + 'min': 0, + 'mode': , + 'step': 1.0, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': , + 'entity_id': 'number.test_number_button_brightness_dimmed', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Button brightness (dimmed)', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'button_brightness_dimmed', + 'unique_id': '00055511EECC-1-23', + 'unit_of_measurement': '%', + }) +# --- +# name: test_number_snapshot[number.test_number_button_brightness_dimmed-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Test Number Button brightness (dimmed)', + 'max': 100, + 'min': 0, + 'mode': , + 'step': 1.0, + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'number.test_number_button_brightness_dimmed', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '23.0', + }) +# --- +# name: test_number_snapshot[number.test_number_display_brightness_active-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'max': 100, + 'min': 0, + 'mode': , + 'step': 1.0, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': , + 'entity_id': 'number.test_number_display_brightness_active', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Display brightness (active)', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'display_brightness_active', + 'unique_id': '00055511EECC-1-20', + 'unit_of_measurement': '%', + }) +# --- +# name: test_number_snapshot[number.test_number_display_brightness_active-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Test Number Display brightness (active)', + 'max': 100, + 'min': 0, + 'mode': , + 'step': 1.0, + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'number.test_number_display_brightness_active', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '78.0', + }) +# --- +# name: test_number_snapshot[number.test_number_display_brightness_dimmed-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'max': 100, + 'min': 0, + 'mode': , + 'step': 1.0, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': , + 'entity_id': 'number.test_number_display_brightness_dimmed', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Display brightness (dimmed)', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'display_brightness_dimmed', + 'unique_id': '00055511EECC-1-21', + 'unit_of_measurement': '%', + }) +# --- +# name: test_number_snapshot[number.test_number_display_brightness_dimmed-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Test Number Display brightness (dimmed)', + 'max': 100, + 'min': 0, + 'mode': , + 'step': 1.0, + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'number.test_number_display_brightness_dimmed', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '23.0', + }) +# --- # name: test_number_snapshot[number.test_number_down_movement_duration-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -231,6 +463,122 @@ 'state': '129.0', }) # --- +# name: test_number_snapshot[number.test_number_external_temperature_offset-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'max': 6, + 'min': -6, + 'mode': , + 'step': 0.1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': , + 'entity_id': 'number.test_number_external_temperature_offset', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'External temperature offset', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'external_temperature_offset', + 'unique_id': '00055511EECC-1-17', + 'unit_of_measurement': , + }) +# --- +# name: test_number_snapshot[number.test_number_external_temperature_offset-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Test Number External temperature offset', + 'max': 6, + 'min': -6, + 'mode': , + 'step': 0.1, + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'number.test_number_external_temperature_offset', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_number_snapshot[number.test_number_floor_temperature_offset-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'max': 6, + 'min': -6, + 'mode': , + 'step': 0.1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': , + 'entity_id': 'number.test_number_floor_temperature_offset', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Floor temperature offset', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'floor_temperature_offset', + 'unique_id': '00055511EECC-1-18', + 'unit_of_measurement': , + }) +# --- +# name: test_number_snapshot[number.test_number_floor_temperature_offset-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Test Number Floor temperature offset', + 'max': 6, + 'min': -6, + 'mode': , + 'step': 0.1, + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'number.test_number_floor_temperature_offset', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- # name: test_number_snapshot[number.test_number_maximum_slat_angle-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -639,6 +987,65 @@ 'state': 'unavailable', }) # --- +# name: test_number_snapshot[number.test_number_temperature_report_interval-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'max': 32767, + 'min': 0, + 'mode': , + 'step': 1.0, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': , + 'entity_id': 'number.test_number_temperature_report_interval', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature report interval', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'temperature_report_interval', + 'unique_id': '00055511EECC-1-19', + 'unit_of_measurement': , + }) +# --- +# name: test_number_snapshot[number.test_number_temperature_report_interval-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'duration', + 'friendly_name': 'Test Number Temperature report interval', + 'max': 32767, + 'min': 0, + 'mode': , + 'step': 1.0, + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'number.test_number_temperature_report_interval', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '60.0', + }) +# --- # name: test_number_snapshot[number.test_number_threshold_for_wind_trigger-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/homee/snapshots/test_select.ambr b/tests/components/homee/snapshots/test_select.ambr index 9f52f75e691..49cb8612522 100644 --- a/tests/components/homee/snapshots/test_select.ambr +++ b/tests/components/homee/snapshots/test_select.ambr @@ -1,4 +1,61 @@ # serializer version: 1 +# name: test_select_snapshot[select.test_select_displayed_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'target', + 'current', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.test_select_displayed_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Displayed temperature', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'display_temperature_selection', + 'unique_id': '00055511EECC-1-2', + 'unit_of_measurement': None, + }) +# --- +# name: test_select_snapshot[select.test_select_displayed_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Test Select Displayed temperature', + 'options': list([ + 'target', + 'current', + ]), + }), + 'context': , + 'entity_id': 'select.test_select_displayed_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'target', + }) +# --- # name: test_select_snapshot[select.test_select_repeater_mode-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/homee/snapshots/test_sensor.ambr b/tests/components/homee/snapshots/test_sensor.ambr index 618f2bcfdf6..b5975af2d54 100644 --- a/tests/components/homee/snapshots/test_sensor.ambr +++ b/tests/components/homee/snapshots/test_sensor.ambr @@ -490,6 +490,62 @@ 'state': '2000.0', }) # --- +# name: test_sensor_snapshot[sensor.test_multisensor_floor_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.test_multisensor_floor_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Floor temperature', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'floor_temperature', + 'unique_id': '00055511EECC-1-35', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_snapshot[sensor.test_multisensor_floor_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Test MultiSensor Floor temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.test_multisensor_floor_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- # name: test_sensor_snapshot[sensor.test_multisensor_humidity-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/homee/test_climate.py b/tests/components/homee/test_climate.py index bb5ad98c7d2..bb650325240 100644 --- a/tests/components/homee/test_climate.py +++ b/tests/components/homee/test_climate.py @@ -177,6 +177,32 @@ async def test_current_preset_mode( assert attributes[ATTR_PRESET_MODE] == expected +@pytest.mark.parametrize( + ("preset_mode_int", "expected"), + [ + (10, PRESET_NONE), + (11, PRESET_NONE), + (12, PRESET_ECO), + ], +) +async def test_current_preset_mode_alternate( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_homee: MagicMock, + preset_mode_int: int, + expected: str, +) -> None: + """Test current preset mode of climate entities.""" + mock_homee.nodes = [build_mock_node("thermostat_with_alternate_preset.json")] + mock_homee.get_node_by_id.return_value = mock_homee.nodes[0] + node = mock_homee.nodes[0] + node.attributes[2].current_value = preset_mode_int + await setup_integration(hass, mock_config_entry) + + attributes = hass.states.get("climate.test_thermostat_5").attributes + assert attributes[ATTR_PRESET_MODE] == expected + + @pytest.mark.parametrize( ("service", "service_data", "expected"), [ @@ -250,6 +276,64 @@ async def test_climate_services( mock_homee.set_value.assert_called_once_with(*expected) +@pytest.mark.parametrize( + ("service", "service_data", "expected"), + [ + ( + SERVICE_TURN_ON, + {}, + (5, 3, 11), + ), + ( + SERVICE_TURN_OFF, + {}, + (5, 3, 10), + ), + ( + SERVICE_SET_HVAC_MODE, + {ATTR_HVAC_MODE: HVACMode.HEAT}, + (5, 3, 11), + ), + ( + SERVICE_SET_HVAC_MODE, + {ATTR_HVAC_MODE: HVACMode.OFF}, + (5, 3, 10), + ), + ( + SERVICE_SET_PRESET_MODE, + {ATTR_PRESET_MODE: PRESET_NONE}, + (5, 3, 11), + ), + ( + SERVICE_SET_PRESET_MODE, + {ATTR_PRESET_MODE: PRESET_ECO}, + (5, 3, 12), + ), + ], +) +async def test_climate_services_alternate( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_homee: MagicMock, + service: str, + service_data: dict, + expected: tuple[int, int, int], +) -> None: + """Test available services of climate entities.""" + await setup_mock_climate( + hass, mock_config_entry, mock_homee, "thermostat_with_alternate_preset.json" + ) + + await hass.services.async_call( + CLIMATE_DOMAIN, + service, + {ATTR_ENTITY_ID: "climate.test_thermostat_5", **service_data}, + blocking=True, + ) + + mock_homee.set_value.assert_called_once_with(*expected) + + async def test_climate_snapshot( hass: HomeAssistant, mock_homee: MagicMock, @@ -263,6 +347,7 @@ async def test_climate_snapshot( build_mock_node("thermostat_with_currenttemp.json"), build_mock_node("thermostat_with_heating_mode.json"), build_mock_node("thermostat_with_preset.json"), + build_mock_node("thermostat_with_alternate_preset.json"), ] with patch("homeassistant.components.homee.PLATFORMS", [Platform.CLIMATE]): await setup_integration(hass, mock_config_entry)