From 46aa161ccc7a349170a2e097b5e332117fef1a8c Mon Sep 17 00:00:00 2001 From: Mario Mett <1007649+mettolen@users.noreply.github.com> Date: Wed, 26 Nov 2025 13:04:15 +0000 Subject: [PATCH 1/5] Add sensor entities to Saunum integration --- homeassistant/components/saunum/__init__.py | 1 + .../components/saunum/quality_scale.yaml | 6 +- homeassistant/components/saunum/sensor.py | 99 +++++++++++ homeassistant/components/saunum/strings.json | 11 ++ .../saunum/snapshots/test_sensor.ambr | 165 ++++++++++++++++++ tests/components/saunum/test_sensor.py | 85 +++++++++ 6 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 homeassistant/components/saunum/sensor.py create mode 100644 tests/components/saunum/snapshots/test_sensor.ambr create mode 100644 tests/components/saunum/test_sensor.py diff --git a/homeassistant/components/saunum/__init__.py b/homeassistant/components/saunum/__init__.py index 32ccc7bd6e2e5..b014abfeb2c0b 100644 --- a/homeassistant/components/saunum/__init__.py +++ b/homeassistant/components/saunum/__init__.py @@ -14,6 +14,7 @@ PLATFORMS: list[Platform] = [ Platform.CLIMATE, Platform.LIGHT, + Platform.SENSOR, ] type LeilSaunaConfigEntry = ConfigEntry[LeilSaunaCoordinator] diff --git a/homeassistant/components/saunum/quality_scale.yaml b/homeassistant/components/saunum/quality_scale.yaml index af6674e6d6ddf..9dcd42d064132 100644 --- a/homeassistant/components/saunum/quality_scale.yaml +++ b/homeassistant/components/saunum/quality_scale.yaml @@ -60,7 +60,7 @@ rules: comment: Integration controls a single device; no dynamic device discovery needed. entity-category: done entity-device-class: done - entity-disabled-by-default: todo + entity-disabled-by-default: done entity-translations: done exception-translations: done icon-translations: todo @@ -74,5 +74,7 @@ rules: # Platinum async-dependency: todo - inject-websession: todo + inject-websession: + status: exempt + comment: Integration uses Modbus TCP protocol and does not make HTTP requests. strict-typing: todo diff --git a/homeassistant/components/saunum/sensor.py b/homeassistant/components/saunum/sensor.py new file mode 100644 index 0000000000000..f81fe9b902662 --- /dev/null +++ b/homeassistant/components/saunum/sensor.py @@ -0,0 +1,99 @@ +"""Sensor platform for Saunum Leil Sauna Control Unit integration.""" + +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +from typing import TYPE_CHECKING + +from pysaunum import SaunumData + +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) +from homeassistant.const import EntityCategory, UnitOfTemperature, UnitOfTime +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback + +from . import LeilSaunaConfigEntry +from .entity import LeilSaunaEntity + +if TYPE_CHECKING: + from .coordinator import LeilSaunaCoordinator + +PARALLEL_UPDATES = 0 + + +@dataclass(frozen=True, kw_only=True) +class LeilSaunaSensorEntityDescription(SensorEntityDescription): + """Describes Leil Sauna sensor entity.""" + + value_fn: Callable[[SaunumData], float | int | None] + + +SENSORS: tuple[LeilSaunaSensorEntityDescription, ...] = ( + LeilSaunaSensorEntityDescription( + key="current_temperature", + translation_key="current_temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda data: data.current_temperature, + ), + LeilSaunaSensorEntityDescription( + key="heater_elements_active", + translation_key="heater_elements_active", + icon="mdi:radiator", + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda data: data.heater_elements_active, + ), + LeilSaunaSensorEntityDescription( + key="on_time", + translation_key="on_time", + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=SensorDeviceClass.DURATION, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + state_class=SensorStateClass.TOTAL_INCREASING, + value_fn=lambda data: data.on_time, + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: LeilSaunaConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Set up Saunum Leil Sauna sensors from a config entry.""" + coordinator = entry.runtime_data + + async_add_entities( + LeilSaunaSensorEntity(coordinator, description) + for description in SENSORS + if description.value_fn(coordinator.data) is not None + ) + + +class LeilSaunaSensorEntity(LeilSaunaEntity, SensorEntity): + """Representation of a Saunum Leil Sauna sensor.""" + + entity_description: LeilSaunaSensorEntityDescription + + def __init__( + self, + coordinator: LeilSaunaCoordinator, + description: LeilSaunaSensorEntityDescription, + ) -> None: + """Initialize the sensor.""" + super().__init__(coordinator) + self._attr_unique_id = f"{coordinator.config_entry.entry_id}-{description.key}" + self.entity_description = description + + @property + def native_value(self) -> float | int | None: + """Return the value reported by the sensor.""" + return self.entity_description.value_fn(self.coordinator.data) diff --git a/homeassistant/components/saunum/strings.json b/homeassistant/components/saunum/strings.json index 8b986423bc577..583b4cb5e66e1 100644 --- a/homeassistant/components/saunum/strings.json +++ b/homeassistant/components/saunum/strings.json @@ -34,6 +34,17 @@ "light": { "name": "[%key:component::light::title%]" } + }, + "sensor": { + "current_temperature": { + "name": "[%key:component::sensor::entity_component::temperature::name%]" + }, + "heater_elements_active": { + "name": "Heater elements active" + }, + "on_time": { + "name": "On time" + } } }, "exceptions": { diff --git a/tests/components/saunum/snapshots/test_sensor.ambr b/tests/components/saunum/snapshots/test_sensor.ambr new file mode 100644 index 0000000000000..a50468e73947e --- /dev/null +++ b/tests/components/saunum/snapshots/test_sensor.ambr @@ -0,0 +1,165 @@ +# serializer version: 1 +# name: test_entities[sensor.saunum_leil_heater_elements_active-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.saunum_leil_heater_elements_active', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': 'mdi:radiator', + 'original_name': 'Heater elements active', + 'platform': 'saunum', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'heater_elements_active', + 'unique_id': '01K98T2T85R5GN0ZHYV25VFMMA-heater_elements_active', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[sensor.saunum_leil_heater_elements_active-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Saunum Leil Heater elements active', + 'icon': 'mdi:radiator', + 'state_class': , + }), + 'context': , + 'entity_id': 'sensor.saunum_leil_heater_elements_active', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- +# name: test_entities[sensor.saunum_leil_on_time-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': , + 'entity_id': 'sensor.saunum_leil_on_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'On time', + 'platform': 'saunum', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'on_time', + 'unique_id': '01K98T2T85R5GN0ZHYV25VFMMA-on_time', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.saunum_leil_on_time-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'duration', + 'friendly_name': 'Saunum Leil On time', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.saunum_leil_on_time', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '3600', + }) +# --- +# name: test_entities[sensor.saunum_leil_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.saunum_leil_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': 'Temperature', + 'platform': 'saunum', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'current_temperature', + 'unique_id': '01K98T2T85R5GN0ZHYV25VFMMA-current_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_entities[sensor.saunum_leil_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Saunum Leil Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.saunum_leil_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '75.0', + }) +# --- diff --git a/tests/components/saunum/test_sensor.py b/tests/components/saunum/test_sensor.py new file mode 100644 index 0000000000000..9fa223ecaa010 --- /dev/null +++ b/tests/components/saunum/test_sensor.py @@ -0,0 +1,85 @@ +"""Test the Saunum sensor platform.""" + +from __future__ import annotations + +from dataclasses import replace + +from freezegun.api import FrozenDateTimeFactory +from pysaunum import SaunumException +import pytest +from syrupy.assertion import SnapshotAssertion + +from homeassistant.const import STATE_UNAVAILABLE, Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform + + +@pytest.fixture +def platforms() -> list[Platform]: + """Fixture to specify platforms to test.""" + return [Platform.SENSOR] + + +@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") +async def test_entities( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + mock_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, +) -> None: + """Test all entities.""" + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) + + +async def test_sensor_not_created_when_value_is_none( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_saunum_client, +) -> None: + """Test sensors are not created when initial value is None.""" + base_data = mock_saunum_client.async_get_data.return_value + mock_saunum_client.async_get_data.return_value = replace( + base_data, + current_temperature=None, + heater_elements_active=None, + on_time=None, + ) + + mock_config_entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get("sensor.saunum_leil_temperature") is None + assert hass.states.get("sensor.saunum_leil_heater_elements_active") is None + assert hass.states.get("sensor.saunum_leil_on_time") is None + + +@pytest.mark.usefixtures("init_integration") +async def test_entity_unavailable_on_update_failure( + hass: HomeAssistant, + mock_saunum_client, + freezer: FrozenDateTimeFactory, +) -> None: + """Test that entity becomes unavailable when coordinator update fails.""" + entity_id = "sensor.saunum_leil_temperature" + + # Verify entity is initially available + state = hass.states.get(entity_id) + assert state is not None + assert state.state != STATE_UNAVAILABLE + + # Make the next update fail + mock_saunum_client.async_get_data.side_effect = SaunumException("Read error") + + # Move time forward to trigger a coordinator update (60 seconds) + freezer.tick(60) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + # Entity should now be unavailable + state = hass.states.get(entity_id) + assert state is not None + assert state.state == STATE_UNAVAILABLE From a201d193cc87cbcfd56bba05b881fde41af05879 Mon Sep 17 00:00:00 2001 From: Mario Mett <1007649+mettolen@users.noreply.github.com> Date: Fri, 28 Nov 2025 21:49:48 +0000 Subject: [PATCH 2/5] Code review fixes --- homeassistant/components/saunum/icons.json | 9 +++ .../components/saunum/quality_scale.yaml | 2 +- homeassistant/components/saunum/sensor.py | 1 - homeassistant/components/saunum/strings.json | 2 +- .../saunum/snapshots/test_sensor.ambr | 69 +++++++++---------- 5 files changed, 45 insertions(+), 38 deletions(-) create mode 100644 homeassistant/components/saunum/icons.json diff --git a/homeassistant/components/saunum/icons.json b/homeassistant/components/saunum/icons.json new file mode 100644 index 0000000000000..854fcee7ff55d --- /dev/null +++ b/homeassistant/components/saunum/icons.json @@ -0,0 +1,9 @@ +{ + "entity": { + "sensor": { + "heater_elements_active": { + "default": "mdi:radiator" + } + } + } +} diff --git a/homeassistant/components/saunum/quality_scale.yaml b/homeassistant/components/saunum/quality_scale.yaml index 9dcd42d064132..ef3ea07669b18 100644 --- a/homeassistant/components/saunum/quality_scale.yaml +++ b/homeassistant/components/saunum/quality_scale.yaml @@ -63,7 +63,7 @@ rules: entity-disabled-by-default: done entity-translations: done exception-translations: done - icon-translations: todo + icon-translations: done reconfiguration-flow: done repair-issues: status: exempt diff --git a/homeassistant/components/saunum/sensor.py b/homeassistant/components/saunum/sensor.py index f81fe9b902662..2ad10a2b3bd11 100644 --- a/homeassistant/components/saunum/sensor.py +++ b/homeassistant/components/saunum/sensor.py @@ -46,7 +46,6 @@ class LeilSaunaSensorEntityDescription(SensorEntityDescription): LeilSaunaSensorEntityDescription( key="heater_elements_active", translation_key="heater_elements_active", - icon="mdi:radiator", state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.heater_elements_active, ), diff --git a/homeassistant/components/saunum/strings.json b/homeassistant/components/saunum/strings.json index 583b4cb5e66e1..feb1f4e25c35c 100644 --- a/homeassistant/components/saunum/strings.json +++ b/homeassistant/components/saunum/strings.json @@ -43,7 +43,7 @@ "name": "Heater elements active" }, "on_time": { - "name": "On time" + "name": "Total time turned on" } } }, diff --git a/tests/components/saunum/snapshots/test_sensor.ambr b/tests/components/saunum/snapshots/test_sensor.ambr index a50468e73947e..1986e3fdbf223 100644 --- a/tests/components/saunum/snapshots/test_sensor.ambr +++ b/tests/components/saunum/snapshots/test_sensor.ambr @@ -1,11 +1,11 @@ # serializer version: 1 -# name: test_entities[sensor.saunum_leil_heater_elements_active-entry] +# name: test_entities[sensor.saunum_leil_duration-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, 'capabilities': dict({ - 'state_class': , + 'state_class': , }), 'config_entry_id': , 'config_subentry_id': , @@ -13,8 +13,8 @@ 'device_id': , 'disabled_by': None, 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.saunum_leil_heater_elements_active', + 'entity_category': , + 'entity_id': 'sensor.saunum_leil_duration', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -23,41 +23,45 @@ }), 'name': None, 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), }), - 'original_device_class': None, - 'original_icon': 'mdi:radiator', - 'original_name': 'Heater elements active', + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Duration', 'platform': 'saunum', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'heater_elements_active', - 'unique_id': '01K98T2T85R5GN0ZHYV25VFMMA-heater_elements_active', - 'unit_of_measurement': None, + 'translation_key': 'on_time', + 'unique_id': '01K98T2T85R5GN0ZHYV25VFMMA-on_time', + 'unit_of_measurement': , }) # --- -# name: test_entities[sensor.saunum_leil_heater_elements_active-state] +# name: test_entities[sensor.saunum_leil_duration-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'friendly_name': 'Saunum Leil Heater elements active', - 'icon': 'mdi:radiator', - 'state_class': , + 'device_class': 'duration', + 'friendly_name': 'Saunum Leil Duration', + 'state_class': , + 'unit_of_measurement': , }), 'context': , - 'entity_id': 'sensor.saunum_leil_heater_elements_active', + 'entity_id': 'sensor.saunum_leil_duration', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '0', + 'state': '3600', }) # --- -# name: test_entities[sensor.saunum_leil_on_time-entry] +# name: test_entities[sensor.saunum_leil_none-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, 'capabilities': dict({ - 'state_class': , + 'state_class': , }), 'config_entry_id': , 'config_subentry_id': , @@ -65,8 +69,8 @@ 'device_id': , 'disabled_by': None, 'domain': 'sensor', - 'entity_category': , - 'entity_id': 'sensor.saunum_leil_on_time', + 'entity_category': None, + 'entity_id': 'sensor.saunum_leil_none', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -75,36 +79,31 @@ }), 'name': None, 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), }), - 'original_device_class': , + 'original_device_class': None, 'original_icon': None, - 'original_name': 'On time', + 'original_name': None, 'platform': 'saunum', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'on_time', - 'unique_id': '01K98T2T85R5GN0ZHYV25VFMMA-on_time', - 'unit_of_measurement': , + 'translation_key': 'heater_elements_active', + 'unique_id': '01K98T2T85R5GN0ZHYV25VFMMA-heater_elements_active', + 'unit_of_measurement': None, }) # --- -# name: test_entities[sensor.saunum_leil_on_time-state] +# name: test_entities[sensor.saunum_leil_none-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'duration', - 'friendly_name': 'Saunum Leil On time', - 'state_class': , - 'unit_of_measurement': , + 'friendly_name': 'Saunum Leil None', + 'state_class': , }), 'context': , - 'entity_id': 'sensor.saunum_leil_on_time', + 'entity_id': 'sensor.saunum_leil_none', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '3600', + 'state': '0', }) # --- # name: test_entities[sensor.saunum_leil_temperature-entry] From 7193b2676fd2c0635459e4279b8a19768e89a5b0 Mon Sep 17 00:00:00 2001 From: Mario Mett <1007649+mettolen@users.noreply.github.com> Date: Sat, 29 Nov 2025 14:05:49 +0000 Subject: [PATCH 3/5] Code review fixes --- homeassistant/components/saunum/sensor.py | 2 ++ homeassistant/components/saunum/strings.json | 3 --- tests/components/saunum/snapshots/test_sensor.ambr | 12 ++++++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/saunum/sensor.py b/homeassistant/components/saunum/sensor.py index 2ad10a2b3bd11..2d2d4a88f2b91 100644 --- a/homeassistant/components/saunum/sensor.py +++ b/homeassistant/components/saunum/sensor.py @@ -46,6 +46,7 @@ class LeilSaunaSensorEntityDescription(SensorEntityDescription): LeilSaunaSensorEntityDescription( key="heater_elements_active", translation_key="heater_elements_active", + native_unit_of_measurement="heater elements", state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.heater_elements_active, ), @@ -53,6 +54,7 @@ class LeilSaunaSensorEntityDescription(SensorEntityDescription): key="on_time", translation_key="on_time", native_unit_of_measurement=UnitOfTime.SECONDS, + suggested_unit_of_measurement=UnitOfTime.HOURS, device_class=SensorDeviceClass.DURATION, entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, diff --git a/homeassistant/components/saunum/strings.json b/homeassistant/components/saunum/strings.json index feb1f4e25c35c..d6677f1ad6308 100644 --- a/homeassistant/components/saunum/strings.json +++ b/homeassistant/components/saunum/strings.json @@ -36,9 +36,6 @@ } }, "sensor": { - "current_temperature": { - "name": "[%key:component::sensor::entity_component::temperature::name%]" - }, "heater_elements_active": { "name": "Heater elements active" }, diff --git a/tests/components/saunum/snapshots/test_sensor.ambr b/tests/components/saunum/snapshots/test_sensor.ambr index 1986e3fdbf223..27881ed5538fe 100644 --- a/tests/components/saunum/snapshots/test_sensor.ambr +++ b/tests/components/saunum/snapshots/test_sensor.ambr @@ -26,6 +26,9 @@ 'sensor': dict({ 'suggested_display_precision': 2, }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), }), 'original_device_class': , 'original_icon': None, @@ -36,7 +39,7 @@ 'supported_features': 0, 'translation_key': 'on_time', 'unique_id': '01K98T2T85R5GN0ZHYV25VFMMA-on_time', - 'unit_of_measurement': , + 'unit_of_measurement': , }) # --- # name: test_entities[sensor.saunum_leil_duration-state] @@ -45,14 +48,14 @@ 'device_class': 'duration', 'friendly_name': 'Saunum Leil Duration', 'state_class': , - 'unit_of_measurement': , + 'unit_of_measurement': , }), 'context': , 'entity_id': 'sensor.saunum_leil_duration', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '3600', + 'state': '1.0', }) # --- # name: test_entities[sensor.saunum_leil_none-entry] @@ -89,7 +92,7 @@ 'supported_features': 0, 'translation_key': 'heater_elements_active', 'unique_id': '01K98T2T85R5GN0ZHYV25VFMMA-heater_elements_active', - 'unit_of_measurement': None, + 'unit_of_measurement': 'heater elements', }) # --- # name: test_entities[sensor.saunum_leil_none-state] @@ -97,6 +100,7 @@ 'attributes': ReadOnlyDict({ 'friendly_name': 'Saunum Leil None', 'state_class': , + 'unit_of_measurement': 'heater elements', }), 'context': , 'entity_id': 'sensor.saunum_leil_none', From 4842792af1b23bc66b1a06967fd7f5f2c65acde2 Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Mon, 1 Dec 2025 16:19:31 +0100 Subject: [PATCH 4/5] Apply suggestions from code review --- homeassistant/components/saunum/sensor.py | 1 - homeassistant/components/saunum/strings.json | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/saunum/sensor.py b/homeassistant/components/saunum/sensor.py index 2d2d4a88f2b91..7e9a09e051783 100644 --- a/homeassistant/components/saunum/sensor.py +++ b/homeassistant/components/saunum/sensor.py @@ -46,7 +46,6 @@ class LeilSaunaSensorEntityDescription(SensorEntityDescription): LeilSaunaSensorEntityDescription( key="heater_elements_active", translation_key="heater_elements_active", - native_unit_of_measurement="heater elements", state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.heater_elements_active, ), diff --git a/homeassistant/components/saunum/strings.json b/homeassistant/components/saunum/strings.json index d6677f1ad6308..003d48fb734c2 100644 --- a/homeassistant/components/saunum/strings.json +++ b/homeassistant/components/saunum/strings.json @@ -37,7 +37,8 @@ }, "sensor": { "heater_elements_active": { - "name": "Heater elements active" + "name": "Heater elements active", + "unit_of_measurement": "heater elements" }, "on_time": { "name": "Total time turned on" From 847dc662de5c15e153f11a1189905b47a98128d1 Mon Sep 17 00:00:00 2001 From: Joostlek Date: Mon, 1 Dec 2025 16:26:40 +0100 Subject: [PATCH 5/5] Fix --- .../saunum/snapshots/test_sensor.ambr | 102 +++++++++--------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/tests/components/saunum/snapshots/test_sensor.ambr b/tests/components/saunum/snapshots/test_sensor.ambr index 27881ed5538fe..ae5c73d07b542 100644 --- a/tests/components/saunum/snapshots/test_sensor.ambr +++ b/tests/components/saunum/snapshots/test_sensor.ambr @@ -1,11 +1,11 @@ # serializer version: 1 -# name: test_entities[sensor.saunum_leil_duration-entry] +# name: test_entities[sensor.saunum_leil_heater_elements_active-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, 'capabilities': dict({ - 'state_class': , + 'state_class': , }), 'config_entry_id': , 'config_subentry_id': , @@ -13,8 +13,8 @@ 'device_id': , 'disabled_by': None, 'domain': 'sensor', - 'entity_category': , - 'entity_id': 'sensor.saunum_leil_duration', + 'entity_category': None, + 'entity_id': 'sensor.saunum_leil_heater_elements_active', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -23,42 +23,35 @@ }), 'name': None, 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - 'sensor.private': dict({ - 'suggested_unit_of_measurement': , - }), }), - 'original_device_class': , + 'original_device_class': None, 'original_icon': None, - 'original_name': 'Duration', + 'original_name': 'Heater elements active', 'platform': 'saunum', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'on_time', - 'unique_id': '01K98T2T85R5GN0ZHYV25VFMMA-on_time', - 'unit_of_measurement': , + 'translation_key': 'heater_elements_active', + 'unique_id': '01K98T2T85R5GN0ZHYV25VFMMA-heater_elements_active', + 'unit_of_measurement': 'heater elements', }) # --- -# name: test_entities[sensor.saunum_leil_duration-state] +# name: test_entities[sensor.saunum_leil_heater_elements_active-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'duration', - 'friendly_name': 'Saunum Leil Duration', - 'state_class': , - 'unit_of_measurement': , + 'friendly_name': 'Saunum Leil Heater elements active', + 'state_class': , + 'unit_of_measurement': 'heater elements', }), 'context': , - 'entity_id': 'sensor.saunum_leil_duration', + 'entity_id': 'sensor.saunum_leil_heater_elements_active', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '1.0', + 'state': '0', }) # --- -# name: test_entities[sensor.saunum_leil_none-entry] +# name: test_entities[sensor.saunum_leil_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -73,7 +66,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': None, - 'entity_id': 'sensor.saunum_leil_none', + 'entity_id': 'sensor.saunum_leil_temperature', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -82,41 +75,45 @@ }), 'name': None, 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), }), - 'original_device_class': None, + 'original_device_class': , 'original_icon': None, - 'original_name': None, + 'original_name': 'Temperature', 'platform': 'saunum', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'heater_elements_active', - 'unique_id': '01K98T2T85R5GN0ZHYV25VFMMA-heater_elements_active', - 'unit_of_measurement': 'heater elements', + 'translation_key': 'current_temperature', + 'unique_id': '01K98T2T85R5GN0ZHYV25VFMMA-current_temperature', + 'unit_of_measurement': , }) # --- -# name: test_entities[sensor.saunum_leil_none-state] +# name: test_entities[sensor.saunum_leil_temperature-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'friendly_name': 'Saunum Leil None', + 'device_class': 'temperature', + 'friendly_name': 'Saunum Leil Temperature', 'state_class': , - 'unit_of_measurement': 'heater elements', + 'unit_of_measurement': , }), 'context': , - 'entity_id': 'sensor.saunum_leil_none', + 'entity_id': 'sensor.saunum_leil_temperature', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '0', + 'state': '75.0', }) # --- -# name: test_entities[sensor.saunum_leil_temperature-entry] +# name: test_entities[sensor.saunum_leil_total_time_turned_on-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, 'capabilities': dict({ - 'state_class': , + 'state_class': , }), 'config_entry_id': , 'config_subentry_id': , @@ -124,8 +121,8 @@ 'device_id': , 'disabled_by': None, 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.saunum_leil_temperature', + 'entity_category': , + 'entity_id': 'sensor.saunum_leil_total_time_turned_on', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -135,34 +132,37 @@ 'name': None, 'options': dict({ 'sensor': dict({ - 'suggested_display_precision': 1, + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , }), }), - 'original_device_class': , + 'original_device_class': , 'original_icon': None, - 'original_name': 'Temperature', + 'original_name': 'Total time turned on', 'platform': 'saunum', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'current_temperature', - 'unique_id': '01K98T2T85R5GN0ZHYV25VFMMA-current_temperature', - 'unit_of_measurement': , + 'translation_key': 'on_time', + 'unique_id': '01K98T2T85R5GN0ZHYV25VFMMA-on_time', + 'unit_of_measurement': , }) # --- -# name: test_entities[sensor.saunum_leil_temperature-state] +# name: test_entities[sensor.saunum_leil_total_time_turned_on-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'temperature', - 'friendly_name': 'Saunum Leil Temperature', - 'state_class': , - 'unit_of_measurement': , + 'device_class': 'duration', + 'friendly_name': 'Saunum Leil Total time turned on', + 'state_class': , + 'unit_of_measurement': , }), 'context': , - 'entity_id': 'sensor.saunum_leil_temperature', + 'entity_id': 'sensor.saunum_leil_total_time_turned_on', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '75.0', + 'state': '1.0', }) # ---