From 6e91776d6561a1707842462d4b77cfad28e28ea8 Mon Sep 17 00:00:00 2001 From: Thomas55555 <59625598+Thomas55555@users.noreply.github.com> Date: Thu, 22 Feb 2024 09:26:38 +0100 Subject: [PATCH] Add sensor platform for husqvarna_automower (#110410) * Add sensor platform for husqvarna_automower * Adress review comments * Try to fix test * Improve sensors * Address review * Adapt some values * Add test * Add test for cutting blade usage time * Import TEST_MOWER_ID * Use a parenthesis around the lambda and indent it so it's easier to distinguish the entity description parameters from the lambda --- .../husqvarna_automower/__init__.py | 4 +- .../components/husqvarna_automower/sensor.py | 171 ++++++ .../husqvarna_automower/strings.json | 40 ++ tests/components/husqvarna_automower/const.py | 1 + .../husqvarna_automower/fixtures/mower.json | 1 + .../snapshots/test_sensor.ambr | 574 ++++++++++++++++++ .../husqvarna_automower/test_sensor.py | 98 +++ 7 files changed, 886 insertions(+), 3 deletions(-) create mode 100644 homeassistant/components/husqvarna_automower/sensor.py create mode 100644 tests/components/husqvarna_automower/snapshots/test_sensor.ambr create mode 100644 tests/components/husqvarna_automower/test_sensor.py diff --git a/homeassistant/components/husqvarna_automower/__init__.py b/homeassistant/components/husqvarna_automower/__init__.py index 057c1fcc617..d0e2dd9a3e2 100644 --- a/homeassistant/components/husqvarna_automower/__init__.py +++ b/homeassistant/components/husqvarna_automower/__init__.py @@ -18,9 +18,7 @@ from .coordinator import AutomowerDataUpdateCoordinator _LOGGER = logging.getLogger(__name__) -PLATFORMS: list[Platform] = [ - Platform.LAWN_MOWER, -] +PLATFORMS: list[Platform] = [Platform.LAWN_MOWER, Platform.SENSOR] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/husqvarna_automower/sensor.py b/homeassistant/components/husqvarna_automower/sensor.py new file mode 100644 index 00000000000..970c444737c --- /dev/null +++ b/homeassistant/components/husqvarna_automower/sensor.py @@ -0,0 +1,171 @@ +"""Creates a the sensor entities for the mower.""" +from collections.abc import Callable +from dataclasses import dataclass +import datetime +import logging + +from aioautomower.model import MowerAttributes, MowerModes + +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfLength, UnitOfTime +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .coordinator import AutomowerDataUpdateCoordinator +from .entity import AutomowerBaseEntity + +_LOGGER = logging.getLogger(__name__) + + +@dataclass(frozen=True, kw_only=True) +class AutomowerSensorEntityDescription(SensorEntityDescription): + """Describes Automower sensor entity.""" + + exists_fn: Callable[[MowerAttributes], bool] = lambda _: True + value_fn: Callable[[MowerAttributes], str] + + +SENSOR_TYPES: tuple[AutomowerSensorEntityDescription, ...] = ( + AutomowerSensorEntityDescription( + key="battery_percent", + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.BATTERY, + native_unit_of_measurement=PERCENTAGE, + value_fn=lambda data: data.battery.battery_percent, + ), + AutomowerSensorEntityDescription( + key="mode", + translation_key="mode", + device_class=SensorDeviceClass.ENUM, + options=[option.lower() for option in list(MowerModes)], + value_fn=( + lambda data: data.mower.mode.lower() + if data.mower.mode != MowerModes.UNKNOWN + else None + ), + ), + AutomowerSensorEntityDescription( + key="cutting_blade_usage_time", + translation_key="cutting_blade_usage_time", + state_class=SensorStateClass.TOTAL, + device_class=SensorDeviceClass.DURATION, + native_unit_of_measurement=UnitOfTime.SECONDS, + suggested_unit_of_measurement=UnitOfTime.HOURS, + exists_fn=lambda data: data.statistics.cutting_blade_usage_time is not None, + value_fn=lambda data: data.statistics.cutting_blade_usage_time, + ), + AutomowerSensorEntityDescription( + key="total_charging_time", + translation_key="total_charging_time", + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.TOTAL, + device_class=SensorDeviceClass.DURATION, + native_unit_of_measurement=UnitOfTime.SECONDS, + suggested_unit_of_measurement=UnitOfTime.HOURS, + value_fn=lambda data: data.statistics.total_charging_time, + ), + AutomowerSensorEntityDescription( + key="total_cutting_time", + translation_key="total_cutting_time", + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.TOTAL, + device_class=SensorDeviceClass.DURATION, + native_unit_of_measurement=UnitOfTime.SECONDS, + suggested_unit_of_measurement=UnitOfTime.HOURS, + value_fn=lambda data: data.statistics.total_cutting_time, + ), + AutomowerSensorEntityDescription( + key="total_running_time", + translation_key="total_running_time", + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.TOTAL, + device_class=SensorDeviceClass.DURATION, + native_unit_of_measurement=UnitOfTime.SECONDS, + suggested_unit_of_measurement=UnitOfTime.HOURS, + value_fn=lambda data: data.statistics.total_running_time, + ), + AutomowerSensorEntityDescription( + key="total_searching_time", + translation_key="total_searching_time", + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.TOTAL, + device_class=SensorDeviceClass.DURATION, + native_unit_of_measurement=UnitOfTime.SECONDS, + suggested_unit_of_measurement=UnitOfTime.HOURS, + value_fn=lambda data: data.statistics.total_searching_time, + ), + AutomowerSensorEntityDescription( + key="number_of_charging_cycles", + translation_key="number_of_charging_cycles", + icon="mdi:battery-sync-outline", + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.TOTAL, + value_fn=lambda data: data.statistics.number_of_charging_cycles, + ), + AutomowerSensorEntityDescription( + key="number_of_collisions", + translation_key="number_of_collisions", + icon="mdi:counter", + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.TOTAL, + value_fn=lambda data: data.statistics.number_of_collisions, + ), + AutomowerSensorEntityDescription( + key="total_drive_distance", + translation_key="total_drive_distance", + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.TOTAL, + device_class=SensorDeviceClass.DISTANCE, + native_unit_of_measurement=UnitOfLength.METERS, + suggested_unit_of_measurement=UnitOfLength.KILOMETERS, + value_fn=lambda data: data.statistics.total_drive_distance, + ), + AutomowerSensorEntityDescription( + key="next_start_timestamp", + translation_key="next_start_timestamp", + device_class=SensorDeviceClass.TIMESTAMP, + value_fn=lambda data: data.planner.next_start_dateteime, + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up sensor platform.""" + coordinator: AutomowerDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + async_add_entities( + AutomowerSensorEntity(mower_id, coordinator, description) + for mower_id in coordinator.data + for description in SENSOR_TYPES + if description.exists_fn(coordinator.data[mower_id]) + ) + + +class AutomowerSensorEntity(AutomowerBaseEntity, SensorEntity): + """Defining the Automower Sensors with AutomowerSensorEntityDescription.""" + + entity_description: AutomowerSensorEntityDescription + + def __init__( + self, + mower_id: str, + coordinator: AutomowerDataUpdateCoordinator, + description: AutomowerSensorEntityDescription, + ) -> None: + """Set up AutomowerSensors.""" + super().__init__(mower_id, coordinator) + self.entity_description = description + self._attr_unique_id = f"{mower_id}_{description.key}" + + @property + def native_value(self) -> str | int | datetime.datetime | None: + """Return the state of the sensor.""" + return self.entity_description.value_fn(self.mower_attributes) diff --git a/homeassistant/components/husqvarna_automower/strings.json b/homeassistant/components/husqvarna_automower/strings.json index 6a5b28153b4..a4785d678d8 100644 --- a/homeassistant/components/husqvarna_automower/strings.json +++ b/homeassistant/components/husqvarna_automower/strings.json @@ -22,5 +22,45 @@ "create_entry": { "default": "[%key:common::config_flow::create_entry::authenticated%]" } + }, + "entity": { + "sensor": { + "number_of_charging_cycles": { + "name": "Number of charging cycles" + }, + "number_of_collisions": { + "name": "Number of collisions" + }, + "cutting_blade_usage_time": { + "name": "Cutting blade usage time" + }, + "total_charging_time": { + "name": "Total charging time" + }, + "total_cutting_time": { + "name": "Total cutting time" + }, + "total_running_time": { + "name": "Total running time" + }, + "total_searching_time": { + "name": "Total searching time" + }, + "total_drive_distance": { + "name": "Total drive distance" + }, + "next_start_timestamp": { + "name": "Next start" + }, + "mode": { + "name": "Mode", + "state": { + "main_area": "Main area", + "secondary_area": "Secondary area", + "home": "Home", + "demo": "Demo" + } + } + } } } diff --git a/tests/components/husqvarna_automower/const.py b/tests/components/husqvarna_automower/const.py index 7a00937291a..a8b018fa839 100644 --- a/tests/components/husqvarna_automower/const.py +++ b/tests/components/husqvarna_automower/const.py @@ -1,4 +1,5 @@ """Constants for Husqvarna Automower tests.""" CLIENT_ID = "1234" CLIENT_SECRET = "5678" +TEST_MOWER_ID = "c7233734-b219-4287-a173-08e3643f89f0" USER_ID = "123" diff --git a/tests/components/husqvarna_automower/fixtures/mower.json b/tests/components/husqvarna_automower/fixtures/mower.json index eec43698bf0..0af4926c561 100644 --- a/tests/components/husqvarna_automower/fixtures/mower.json +++ b/tests/components/husqvarna_automower/fixtures/mower.json @@ -125,6 +125,7 @@ "mode": "EVENING_ONLY" }, "statistics": { + "cuttingBladeUsageTime": 123, "numberOfChargingCycles": 1380, "numberOfCollisions": 11396, "totalChargingTime": 4334400, diff --git a/tests/components/husqvarna_automower/snapshots/test_sensor.ambr b/tests/components/husqvarna_automower/snapshots/test_sensor.ambr new file mode 100644 index 00000000000..f03624627bf --- /dev/null +++ b/tests/components/husqvarna_automower/snapshots/test_sensor.ambr @@ -0,0 +1,574 @@ +# serializer version: 1 +# name: test_sensor[sensor.test_mower_1_battery-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.test_mower_1_battery', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Battery', + 'platform': 'husqvarna_automower', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'c7233734-b219-4287-a173-08e3643f89f0_battery_percent', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor[sensor.test_mower_1_battery-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'battery', + 'friendly_name': 'Test Mower 1 Battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.test_mower_1_battery', + 'last_changed': , + 'last_updated': , + 'state': '100', + }) +# --- +# name: test_sensor[sensor.test_mower_1_cutting_blade_usage_time-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.test_mower_1_cutting_blade_usage_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Cutting blade usage time', + 'platform': 'husqvarna_automower', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'cutting_blade_usage_time', + 'unique_id': 'c7233734-b219-4287-a173-08e3643f89f0_cutting_blade_usage_time', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.test_mower_1_cutting_blade_usage_time-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'duration', + 'friendly_name': 'Test Mower 1 Cutting blade usage time', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.test_mower_1_cutting_blade_usage_time', + 'last_changed': , + 'last_updated': , + 'state': '0.034', + }) +# --- +# name: test_sensor[sensor.test_mower_1_mode-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'main_area', + 'demo', + 'secondary_area', + 'home', + 'unknown', + ]), + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.test_mower_1_mode', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Mode', + 'platform': 'husqvarna_automower', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'mode', + 'unique_id': 'c7233734-b219-4287-a173-08e3643f89f0_mode', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor[sensor.test_mower_1_mode-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'enum', + 'friendly_name': 'Test Mower 1 Mode', + 'options': list([ + 'main_area', + 'demo', + 'secondary_area', + 'home', + 'unknown', + ]), + }), + 'context': , + 'entity_id': 'sensor.test_mower_1_mode', + 'last_changed': , + 'last_updated': , + 'state': 'main_area', + }) +# --- +# name: test_sensor[sensor.test_mower_1_next_start-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.test_mower_1_next_start', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Next start', + 'platform': 'husqvarna_automower', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'next_start_timestamp', + 'unique_id': 'c7233734-b219-4287-a173-08e3643f89f0_next_start_timestamp', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor[sensor.test_mower_1_next_start-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'timestamp', + 'friendly_name': 'Test Mower 1 Next start', + }), + 'context': , + 'entity_id': 'sensor.test_mower_1_next_start', + 'last_changed': , + 'last_updated': , + 'state': '2023-06-05T19:00:00+00:00', + }) +# --- +# name: test_sensor[sensor.test_mower_1_number_of_charging_cycles-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.test_mower_1_number_of_charging_cycles', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': 'mdi:battery-sync-outline', + 'original_name': 'Number of charging cycles', + 'platform': 'husqvarna_automower', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'number_of_charging_cycles', + 'unique_id': 'c7233734-b219-4287-a173-08e3643f89f0_number_of_charging_cycles', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor[sensor.test_mower_1_number_of_charging_cycles-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Test Mower 1 Number of charging cycles', + 'icon': 'mdi:battery-sync-outline', + 'state_class': , + }), + 'context': , + 'entity_id': 'sensor.test_mower_1_number_of_charging_cycles', + 'last_changed': , + 'last_updated': , + 'state': '1380', + }) +# --- +# name: test_sensor[sensor.test_mower_1_number_of_collisions-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.test_mower_1_number_of_collisions', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': 'mdi:counter', + 'original_name': 'Number of collisions', + 'platform': 'husqvarna_automower', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'number_of_collisions', + 'unique_id': 'c7233734-b219-4287-a173-08e3643f89f0_number_of_collisions', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor[sensor.test_mower_1_number_of_collisions-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Test Mower 1 Number of collisions', + 'icon': 'mdi:counter', + 'state_class': , + }), + 'context': , + 'entity_id': 'sensor.test_mower_1_number_of_collisions', + 'last_changed': , + 'last_updated': , + 'state': '11396', + }) +# --- +# name: test_sensor[sensor.test_mower_1_total_charging_time-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.test_mower_1_total_charging_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Total charging time', + 'platform': 'husqvarna_automower', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'total_charging_time', + 'unique_id': 'c7233734-b219-4287-a173-08e3643f89f0_total_charging_time', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.test_mower_1_total_charging_time-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'duration', + 'friendly_name': 'Test Mower 1 Total charging time', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.test_mower_1_total_charging_time', + 'last_changed': , + 'last_updated': , + 'state': '1204.000', + }) +# --- +# name: test_sensor[sensor.test_mower_1_total_cutting_time-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.test_mower_1_total_cutting_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Total cutting time', + 'platform': 'husqvarna_automower', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'total_cutting_time', + 'unique_id': 'c7233734-b219-4287-a173-08e3643f89f0_total_cutting_time', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.test_mower_1_total_cutting_time-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'duration', + 'friendly_name': 'Test Mower 1 Total cutting time', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.test_mower_1_total_cutting_time', + 'last_changed': , + 'last_updated': , + 'state': '1165.000', + }) +# --- +# name: test_sensor[sensor.test_mower_1_total_drive_distance-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.test_mower_1_total_drive_distance', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Total drive distance', + 'platform': 'husqvarna_automower', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'total_drive_distance', + 'unique_id': 'c7233734-b219-4287-a173-08e3643f89f0_total_drive_distance', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.test_mower_1_total_drive_distance-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'distance', + 'friendly_name': 'Test Mower 1 Total drive distance', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.test_mower_1_total_drive_distance', + 'last_changed': , + 'last_updated': , + 'state': '1780.272', + }) +# --- +# name: test_sensor[sensor.test_mower_1_total_running_time-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.test_mower_1_total_running_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Total running time', + 'platform': 'husqvarna_automower', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'total_running_time', + 'unique_id': 'c7233734-b219-4287-a173-08e3643f89f0_total_running_time', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.test_mower_1_total_running_time-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'duration', + 'friendly_name': 'Test Mower 1 Total running time', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.test_mower_1_total_running_time', + 'last_changed': , + 'last_updated': , + 'state': '1268.000', + }) +# --- +# name: test_sensor[sensor.test_mower_1_total_searching_time-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.test_mower_1_total_searching_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Total searching time', + 'platform': 'husqvarna_automower', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'total_searching_time', + 'unique_id': 'c7233734-b219-4287-a173-08e3643f89f0_total_searching_time', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor[sensor.test_mower_1_total_searching_time-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'duration', + 'friendly_name': 'Test Mower 1 Total searching time', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.test_mower_1_total_searching_time', + 'last_changed': , + 'last_updated': , + 'state': '103.000', + }) +# --- diff --git a/tests/components/husqvarna_automower/test_sensor.py b/tests/components/husqvarna_automower/test_sensor.py new file mode 100644 index 00000000000..1775caac7f8 --- /dev/null +++ b/tests/components/husqvarna_automower/test_sensor.py @@ -0,0 +1,98 @@ +"""Tests for sensor platform.""" +from datetime import timedelta +from unittest.mock import AsyncMock, patch + +from aioautomower.model import MowerModes +from aioautomower.utils import mower_list_to_dictionary_dataclass +from freezegun.api import FrozenDateTimeFactory +from syrupy import SnapshotAssertion + +from homeassistant.components.husqvarna_automower.const import DOMAIN +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from . import setup_integration +from .const import TEST_MOWER_ID + +from tests.common import ( + MockConfigEntry, + async_fire_time_changed, + load_json_value_fixture, +) + + +async def test_sensor_unknown_states( + hass: HomeAssistant, + mock_automower_client: AsyncMock, + mock_config_entry: MockConfigEntry, + freezer: FrozenDateTimeFactory, +) -> None: + """Test a sensor which returns unknown.""" + values = mower_list_to_dictionary_dataclass( + load_json_value_fixture("mower.json", DOMAIN) + ) + await setup_integration(hass, mock_config_entry) + state = hass.states.get("sensor.test_mower_1_mode") + assert state is not None + assert state.state == "main_area" + + values[TEST_MOWER_ID].mower.mode = MowerModes.UNKNOWN + mock_automower_client.get_status.return_value = values + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + state = hass.states.get("sensor.test_mower_1_mode") + assert state.state == "unknown" + + +async def test_cutting_blade_usage_time_sensor( + hass: HomeAssistant, + mock_automower_client: AsyncMock, + mock_config_entry: MockConfigEntry, + freezer: FrozenDateTimeFactory, +) -> None: + """Test if this sensor is only added, if data is available.""" + + await setup_integration(hass, mock_config_entry) + state = hass.states.get("sensor.test_mower_1_cutting_blade_usage_time") + assert state is not None + assert state.state == "0.034" + + entry = hass.config_entries.async_entries(DOMAIN)[0] + await hass.config_entries.async_remove(entry.entry_id) + await hass.async_block_till_done() + values = mower_list_to_dictionary_dataclass( + load_json_value_fixture("mower.json", DOMAIN) + ) + + delattr(values[TEST_MOWER_ID].statistics, "cutting_blade_usage_time") + mock_automower_client.get_status.return_value = values + await setup_integration(hass, mock_config_entry) + state = hass.states.get("sensor.test_mower_1_cutting_blade_usage_time") + assert state is None + + +async def test_sensor( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + mock_automower_client: AsyncMock, + mock_config_entry: MockConfigEntry, + snapshot: SnapshotAssertion, +) -> None: + """Test states of the sensors.""" + with patch( + "homeassistant.components.husqvarna_automower.PLATFORMS", + [Platform.SENSOR], + ): + await setup_integration(hass, mock_config_entry) + entity_entries = er.async_entries_for_config_entry( + entity_registry, mock_config_entry.entry_id + ) + + assert entity_entries + for entity_entry in entity_entries: + assert hass.states.get(entity_entry.entity_id) == snapshot( + name=f"{entity_entry.entity_id}-state" + ) + assert entity_entry == snapshot(name=f"{entity_entry.entity_id}-entry")