Refactor Husqvarna Automower (#117938)

This commit is contained in:
Thomas55555 2024-05-24 10:54:19 +02:00 committed by GitHub
parent bc72f82776
commit 13385912d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 120 additions and 256 deletions

View File

@ -1,6 +1,7 @@
"""The constants for the Husqvarna Automower integration.""" """The constants for the Husqvarna Automower integration."""
DOMAIN = "husqvarna_automower" DOMAIN = "husqvarna_automower"
EXECUTION_TIME_DELAY = 5
NAME = "Husqvarna Automower" NAME = "Husqvarna Automower"
OAUTH2_AUTHORIZE = "https://api.authentication.husqvarnagroup.dev/v1/oauth2/authorize" OAUTH2_AUTHORIZE = "https://api.authentication.husqvarnagroup.dev/v1/oauth2/authorize"
OAUTH2_TOKEN = "https://api.authentication.husqvarnagroup.dev/v1/oauth2/token" OAUTH2_TOKEN = "https://api.authentication.husqvarnagroup.dev/v1/oauth2/token"

View File

@ -12,13 +12,13 @@ from aioautomower.session import AutomowerSession
from homeassistant.components.number import NumberEntity, NumberEntityDescription from homeassistant.components.number import NumberEntity, NumberEntityDescription
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE, EntityCategory from homeassistant.const import PERCENTAGE, EntityCategory, Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN from .const import DOMAIN, EXECUTION_TIME_DELAY
from .coordinator import AutomowerDataUpdateCoordinator from .coordinator import AutomowerDataUpdateCoordinator
from .entity import AutomowerControlEntity from .entity import AutomowerControlEntity
@ -52,10 +52,6 @@ async def async_set_work_area_cutting_height(
await coordinator.api.commands.set_cutting_height_workarea( await coordinator.api.commands.set_cutting_height_workarea(
mower_id, int(cheight), work_area_id mower_id, int(cheight), work_area_id
) )
# As there are no updates from the websocket regarding work area changes,
# we need to wait 5s and then poll the API.
await asyncio.sleep(5)
await coordinator.async_request_refresh()
async def async_set_cutting_height( async def async_set_cutting_height(
@ -189,6 +185,7 @@ class AutomowerWorkAreaNumberEntity(AutomowerControlEntity, NumberEntity):
) -> None: ) -> None:
"""Set up AutomowerNumberEntity.""" """Set up AutomowerNumberEntity."""
super().__init__(mower_id, coordinator) super().__init__(mower_id, coordinator)
self.coordinator = coordinator
self.entity_description = description self.entity_description = description
self.work_area_id = work_area_id self.work_area_id = work_area_id
self._attr_unique_id = f"{mower_id}_{work_area_id}_{description.key}" self._attr_unique_id = f"{mower_id}_{work_area_id}_{description.key}"
@ -221,6 +218,11 @@ class AutomowerWorkAreaNumberEntity(AutomowerControlEntity, NumberEntity):
raise HomeAssistantError( raise HomeAssistantError(
f"Command couldn't be sent to the command queue: {exception}" f"Command couldn't be sent to the command queue: {exception}"
) from exception ) from exception
else:
# As there are no updates from the websocket regarding work area changes,
# we need to wait 5s and then poll the API.
await asyncio.sleep(EXECUTION_TIME_DELAY)
await self.coordinator.async_request_refresh()
@callback @callback
@ -238,10 +240,13 @@ def async_remove_entities(
for work_area_id in _work_areas: for work_area_id in _work_areas:
uid = f"{mower_id}_{work_area_id}_cutting_height_work_area" uid = f"{mower_id}_{work_area_id}_cutting_height_work_area"
active_work_areas.add(uid) active_work_areas.add(uid)
for entity_entry in er.async_entries_for_config_entry( for entity_entry in er.async_entries_for_config_entry(
entity_reg, config_entry.entry_id entity_reg, config_entry.entry_id
):
if (
entity_entry.domain == Platform.NUMBER
and (split := entity_entry.unique_id.split("_"))[0] == mower_id
and split[-1] == "area"
and entity_entry.unique_id not in active_work_areas
): ):
if entity_entry.unique_id.split("_")[0] == mower_id: entity_reg.async_remove(entity_entry.entity_id)
if entity_entry.unique_id.endswith("cutting_height_work_area"):
if entity_entry.unique_id not in active_work_areas:
entity_reg.async_remove(entity_entry.entity_id)

View File

@ -1,4 +1,4 @@
"""Creates a the sensor entities for the mower.""" """Creates the sensor entities for the mower."""
from collections.abc import Callable from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass

View File

@ -15,12 +15,13 @@ from aioautomower.model import (
from homeassistant.components.switch import SwitchEntity from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN from .const import DOMAIN, EXECUTION_TIME_DELAY
from .coordinator import AutomowerDataUpdateCoordinator from .coordinator import AutomowerDataUpdateCoordinator
from .entity import AutomowerControlEntity from .entity import AutomowerControlEntity
@ -40,7 +41,6 @@ ERROR_STATES = [
MowerStates.STOPPED, MowerStates.STOPPED,
MowerStates.OFF, MowerStates.OFF,
] ]
EXECUTION_TIME = 5
async def async_setup_entry( async def async_setup_entry(
@ -172,7 +172,7 @@ class AutomowerStayOutZoneSwitchEntity(AutomowerControlEntity, SwitchEntity):
else: else:
# As there are no updates from the websocket regarding stay out zone changes, # As there are no updates from the websocket regarding stay out zone changes,
# we need to wait until the command is executed and then poll the API. # we need to wait until the command is executed and then poll the API.
await asyncio.sleep(EXECUTION_TIME) await asyncio.sleep(EXECUTION_TIME_DELAY)
await self.coordinator.async_request_refresh() await self.coordinator.async_request_refresh()
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
@ -188,7 +188,7 @@ class AutomowerStayOutZoneSwitchEntity(AutomowerControlEntity, SwitchEntity):
else: else:
# As there are no updates from the websocket regarding stay out zone changes, # As there are no updates from the websocket regarding stay out zone changes,
# we need to wait until the command is executed and then poll the API. # we need to wait until the command is executed and then poll the API.
await asyncio.sleep(EXECUTION_TIME) await asyncio.sleep(EXECUTION_TIME_DELAY)
await self.coordinator.async_request_refresh() await self.coordinator.async_request_refresh()
@ -211,7 +211,8 @@ def async_remove_entities(
entity_reg, config_entry.entry_id entity_reg, config_entry.entry_id
): ):
if ( if (
(split := entity_entry.unique_id.split("_"))[0] == mower_id entity_entry.domain == Platform.SWITCH
and (split := entity_entry.unique_id.split("_"))[0] == mower_id
and split[-1] == "zones" and split[-1] == "zones"
and entity_entry.unique_id not in active_zones and entity_entry.unique_id not in active_zones
): ):

View File

@ -4,6 +4,7 @@ from collections.abc import Generator
import time import time
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
from aioautomower.session import AutomowerSession, _MowerCommands
from aioautomower.utils import mower_list_to_dictionary_dataclass from aioautomower.utils import mower_list_to_dictionary_dataclass
from aiohttp import ClientWebSocketResponse from aiohttp import ClientWebSocketResponse
import pytest import pytest
@ -82,20 +83,18 @@ async def setup_credentials(hass: HomeAssistant) -> None:
@pytest.fixture @pytest.fixture
def mock_automower_client() -> Generator[AsyncMock, None, None]: def mock_automower_client() -> Generator[AsyncMock, None, None]:
"""Mock a Husqvarna Automower client.""" """Mock a Husqvarna Automower client."""
mower_dict = mower_list_to_dictionary_dataclass(
load_json_value_fixture("mower.json", DOMAIN)
)
mock = AsyncMock(spec=AutomowerSession)
mock.auth = AsyncMock(side_effect=ClientWebSocketResponse)
mock.commands = AsyncMock(spec_set=_MowerCommands)
mock.get_status.return_value = mower_dict
with patch( with patch(
"homeassistant.components.husqvarna_automower.AutomowerSession", "homeassistant.components.husqvarna_automower.AutomowerSession",
autospec=True, return_value=mock,
) as mock_client: ):
client = mock_client.return_value yield mock
client.get_status.return_value = mower_list_to_dictionary_dataclass(
load_json_value_fixture("mower.json", DOMAIN)
)
async def websocket_connect() -> ClientWebSocketResponse:
"""Mock listen."""
return ClientWebSocketResponse
client.auth = AsyncMock(side_effect=websocket_connect)
client.commands = AsyncMock()
yield client

View File

@ -1,5 +1,5 @@
# serializer version: 1 # serializer version: 1
# name: test_sensor[binary_sensor.test_mower_1_charging-entry] # name: test_binary_sensor_snapshot[binary_sensor.test_mower_1_charging-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -32,7 +32,7 @@
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
# name: test_sensor[binary_sensor.test_mower_1_charging-state] # name: test_binary_sensor_snapshot[binary_sensor.test_mower_1_charging-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'device_class': 'battery_charging', 'device_class': 'battery_charging',
@ -41,11 +41,12 @@
'context': <ANY>, 'context': <ANY>,
'entity_id': 'binary_sensor.test_mower_1_charging', 'entity_id': 'binary_sensor.test_mower_1_charging',
'last_changed': <ANY>, 'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>, 'last_updated': <ANY>,
'state': 'off', 'state': 'off',
}) })
# --- # ---
# name: test_sensor[binary_sensor.test_mower_1_leaving_dock-entry] # name: test_binary_sensor_snapshot[binary_sensor.test_mower_1_leaving_dock-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -78,7 +79,7 @@
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
# name: test_sensor[binary_sensor.test_mower_1_leaving_dock-state] # name: test_binary_sensor_snapshot[binary_sensor.test_mower_1_leaving_dock-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'friendly_name': 'Test Mower 1 Leaving dock', 'friendly_name': 'Test Mower 1 Leaving dock',
@ -86,11 +87,12 @@
'context': <ANY>, 'context': <ANY>,
'entity_id': 'binary_sensor.test_mower_1_leaving_dock', 'entity_id': 'binary_sensor.test_mower_1_leaving_dock',
'last_changed': <ANY>, 'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>, 'last_updated': <ANY>,
'state': 'off', 'state': 'off',
}) })
# --- # ---
# name: test_sensor[binary_sensor.test_mower_1_returning_to_dock-entry] # name: test_binary_sensor_snapshot[binary_sensor.test_mower_1_returning_to_dock-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -123,145 +125,7 @@
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
# name: test_sensor[binary_sensor.test_mower_1_returning_to_dock-state] # name: test_binary_sensor_snapshot[binary_sensor.test_mower_1_returning_to_dock-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Test Mower 1 Returning to dock',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.test_mower_1_returning_to_dock',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_snapshot_binary_sensor[binary_sensor.test_mower_1_charging-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.test_mower_1_charging',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.BATTERY_CHARGING: 'battery_charging'>,
'original_icon': None,
'original_name': 'Charging',
'platform': 'husqvarna_automower',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'c7233734-b219-4287-a173-08e3643f89f0_battery_charging',
'unit_of_measurement': None,
})
# ---
# name: test_snapshot_binary_sensor[binary_sensor.test_mower_1_charging-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery_charging',
'friendly_name': 'Test Mower 1 Charging',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.test_mower_1_charging',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_snapshot_binary_sensor[binary_sensor.test_mower_1_leaving_dock-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.test_mower_1_leaving_dock',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Leaving dock',
'platform': 'husqvarna_automower',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'leaving_dock',
'unique_id': 'c7233734-b219-4287-a173-08e3643f89f0_leaving_dock',
'unit_of_measurement': None,
})
# ---
# name: test_snapshot_binary_sensor[binary_sensor.test_mower_1_leaving_dock-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Test Mower 1 Leaving dock',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.test_mower_1_leaving_dock',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_snapshot_binary_sensor[binary_sensor.test_mower_1_returning_to_dock-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.test_mower_1_returning_to_dock',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Returning to dock',
'platform': 'husqvarna_automower',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'returning_to_dock',
'unique_id': 'c7233734-b219-4287-a173-08e3643f89f0_returning_to_dock',
'unit_of_measurement': None,
})
# ---
# name: test_snapshot_binary_sensor[binary_sensor.test_mower_1_returning_to_dock-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'friendly_name': 'Test Mower 1 Returning to dock', 'friendly_name': 'Test Mower 1 Returning to dock',

View File

@ -1,5 +1,5 @@
# serializer version: 1 # serializer version: 1
# name: test_snapshot_number[number.test_mower_1_back_lawn_cutting_height-entry] # name: test_number_snapshot[number.test_mower_1_back_lawn_cutting_height-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -37,7 +37,7 @@
'unit_of_measurement': '%', 'unit_of_measurement': '%',
}) })
# --- # ---
# name: test_snapshot_number[number.test_mower_1_back_lawn_cutting_height-state] # name: test_number_snapshot[number.test_mower_1_back_lawn_cutting_height-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'friendly_name': 'Test Mower 1 Back lawn cutting height', 'friendly_name': 'Test Mower 1 Back lawn cutting height',
@ -55,7 +55,7 @@
'state': '25', 'state': '25',
}) })
# --- # ---
# name: test_snapshot_number[number.test_mower_1_cutting_height-entry] # name: test_number_snapshot[number.test_mower_1_cutting_height-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -93,7 +93,7 @@
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
# name: test_snapshot_number[number.test_mower_1_cutting_height-state] # name: test_number_snapshot[number.test_mower_1_cutting_height-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'friendly_name': 'Test Mower 1 Cutting height', 'friendly_name': 'Test Mower 1 Cutting height',
@ -110,7 +110,7 @@
'state': '4', 'state': '4',
}) })
# --- # ---
# name: test_snapshot_number[number.test_mower_1_front_lawn_cutting_height-entry] # name: test_number_snapshot[number.test_mower_1_front_lawn_cutting_height-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -148,7 +148,7 @@
'unit_of_measurement': '%', 'unit_of_measurement': '%',
}) })
# --- # ---
# name: test_snapshot_number[number.test_mower_1_front_lawn_cutting_height-state] # name: test_number_snapshot[number.test_mower_1_front_lawn_cutting_height-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'friendly_name': 'Test Mower 1 Front lawn cutting height', 'friendly_name': 'Test Mower 1 Front lawn cutting height',
@ -166,7 +166,7 @@
'state': '50', 'state': '50',
}) })
# --- # ---
# name: test_snapshot_number[number.test_mower_1_my_lawn_cutting_height-entry] # name: test_number_snapshot[number.test_mower_1_my_lawn_cutting_height-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -204,7 +204,7 @@
'unit_of_measurement': '%', 'unit_of_measurement': '%',
}) })
# --- # ---
# name: test_snapshot_number[number.test_mower_1_my_lawn_cutting_height-state] # name: test_number_snapshot[number.test_mower_1_my_lawn_cutting_height-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'friendly_name': 'Test Mower 1 My lawn cutting height ', 'friendly_name': 'Test Mower 1 My lawn cutting height ',

View File

@ -1,5 +1,5 @@
# serializer version: 1 # serializer version: 1
# name: test_sensor[sensor.test_mower_1_battery-entry] # name: test_sensor_snapshot[sensor.test_mower_1_battery-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -34,7 +34,7 @@
'unit_of_measurement': '%', 'unit_of_measurement': '%',
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_battery-state] # name: test_sensor_snapshot[sensor.test_mower_1_battery-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'device_class': 'battery', 'device_class': 'battery',
@ -50,7 +50,7 @@
'state': '100', 'state': '100',
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_cutting_blade_usage_time-entry] # name: test_sensor_snapshot[sensor.test_mower_1_cutting_blade_usage_time-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -88,7 +88,7 @@
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>, 'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_cutting_blade_usage_time-state] # name: test_sensor_snapshot[sensor.test_mower_1_cutting_blade_usage_time-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'device_class': 'duration', 'device_class': 'duration',
@ -104,7 +104,7 @@
'state': '0.034', 'state': '0.034',
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_error-entry] # name: test_sensor_snapshot[sensor.test_mower_1_error-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -283,7 +283,7 @@
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_error-state] # name: test_sensor_snapshot[sensor.test_mower_1_error-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'device_class': 'enum', 'device_class': 'enum',
@ -442,7 +442,7 @@
'state': 'no_error', 'state': 'no_error',
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_mode-entry] # name: test_sensor_snapshot[sensor.test_mower_1_mode-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -483,7 +483,7 @@
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_mode-state] # name: test_sensor_snapshot[sensor.test_mower_1_mode-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'device_class': 'enum', 'device_class': 'enum',
@ -504,7 +504,7 @@
'state': 'main_area', 'state': 'main_area',
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_next_start-entry] # name: test_sensor_snapshot[sensor.test_mower_1_next_start-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -537,7 +537,7 @@
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_next_start-state] # name: test_sensor_snapshot[sensor.test_mower_1_next_start-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'device_class': 'timestamp', 'device_class': 'timestamp',
@ -551,7 +551,7 @@
'state': '2023-06-05T19:00:00+00:00', 'state': '2023-06-05T19:00:00+00:00',
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_number_of_charging_cycles-entry] # name: test_sensor_snapshot[sensor.test_mower_1_number_of_charging_cycles-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -586,7 +586,7 @@
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_number_of_charging_cycles-state] # name: test_sensor_snapshot[sensor.test_mower_1_number_of_charging_cycles-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'friendly_name': 'Test Mower 1 Number of charging cycles', 'friendly_name': 'Test Mower 1 Number of charging cycles',
@ -600,7 +600,7 @@
'state': '1380', 'state': '1380',
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_number_of_collisions-entry] # name: test_sensor_snapshot[sensor.test_mower_1_number_of_collisions-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -635,7 +635,7 @@
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_number_of_collisions-state] # name: test_sensor_snapshot[sensor.test_mower_1_number_of_collisions-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'friendly_name': 'Test Mower 1 Number of collisions', 'friendly_name': 'Test Mower 1 Number of collisions',
@ -649,7 +649,7 @@
'state': '11396', 'state': '11396',
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_restricted_reason-entry] # name: test_sensor_snapshot[sensor.test_mower_1_restricted_reason-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -695,7 +695,7 @@
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_restricted_reason-state] # name: test_sensor_snapshot[sensor.test_mower_1_restricted_reason-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'device_class': 'enum', 'device_class': 'enum',
@ -721,7 +721,7 @@
'state': 'week_schedule', 'state': 'week_schedule',
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_total_charging_time-entry] # name: test_sensor_snapshot[sensor.test_mower_1_total_charging_time-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -759,7 +759,7 @@
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>, 'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_total_charging_time-state] # name: test_sensor_snapshot[sensor.test_mower_1_total_charging_time-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'device_class': 'duration', 'device_class': 'duration',
@ -775,7 +775,7 @@
'state': '1204.000', 'state': '1204.000',
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_total_cutting_time-entry] # name: test_sensor_snapshot[sensor.test_mower_1_total_cutting_time-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -813,7 +813,7 @@
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>, 'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_total_cutting_time-state] # name: test_sensor_snapshot[sensor.test_mower_1_total_cutting_time-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'device_class': 'duration', 'device_class': 'duration',
@ -829,7 +829,7 @@
'state': '1165.000', 'state': '1165.000',
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_total_drive_distance-entry] # name: test_sensor_snapshot[sensor.test_mower_1_total_drive_distance-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -867,7 +867,7 @@
'unit_of_measurement': <UnitOfLength.KILOMETERS: 'km'>, 'unit_of_measurement': <UnitOfLength.KILOMETERS: 'km'>,
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_total_drive_distance-state] # name: test_sensor_snapshot[sensor.test_mower_1_total_drive_distance-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'device_class': 'distance', 'device_class': 'distance',
@ -883,7 +883,7 @@
'state': '1780.272', 'state': '1780.272',
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_total_running_time-entry] # name: test_sensor_snapshot[sensor.test_mower_1_total_running_time-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -921,7 +921,7 @@
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>, 'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_total_running_time-state] # name: test_sensor_snapshot[sensor.test_mower_1_total_running_time-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'device_class': 'duration', 'device_class': 'duration',
@ -937,7 +937,7 @@
'state': '1268.000', 'state': '1268.000',
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_total_searching_time-entry] # name: test_sensor_snapshot[sensor.test_mower_1_total_searching_time-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -975,7 +975,7 @@
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>, 'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
}) })
# --- # ---
# name: test_sensor[sensor.test_mower_1_total_searching_time-state] # name: test_sensor_snapshot[sensor.test_mower_1_total_searching_time-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'device_class': 'duration', 'device_class': 'duration',

View File

@ -1,5 +1,5 @@
# serializer version: 1 # serializer version: 1
# name: test_switch[switch.test_mower_1_avoid_danger_zone-entry] # name: test_switch_snapshot[switch.test_mower_1_avoid_danger_zone-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -32,7 +32,7 @@
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
# name: test_switch[switch.test_mower_1_avoid_danger_zone-state] # name: test_switch_snapshot[switch.test_mower_1_avoid_danger_zone-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'friendly_name': 'Test Mower 1 Avoid Danger Zone', 'friendly_name': 'Test Mower 1 Avoid Danger Zone',
@ -45,7 +45,7 @@
'state': 'off', 'state': 'off',
}) })
# --- # ---
# name: test_switch[switch.test_mower_1_avoid_springflowers-entry] # name: test_switch_snapshot[switch.test_mower_1_avoid_springflowers-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -78,7 +78,7 @@
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
# name: test_switch[switch.test_mower_1_avoid_springflowers-state] # name: test_switch_snapshot[switch.test_mower_1_avoid_springflowers-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'friendly_name': 'Test Mower 1 Avoid Springflowers', 'friendly_name': 'Test Mower 1 Avoid Springflowers',
@ -91,7 +91,7 @@
'state': 'on', 'state': 'on',
}) })
# --- # ---
# name: test_switch[switch.test_mower_1_enable_schedule-entry] # name: test_switch_snapshot[switch.test_mower_1_enable_schedule-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
}), }),
@ -124,7 +124,7 @@
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
# name: test_switch[switch.test_mower_1_enable_schedule-state] # name: test_switch_snapshot[switch.test_mower_1_enable_schedule-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'friendly_name': 'Test Mower 1 Enable schedule', 'friendly_name': 'Test Mower 1 Enable schedule',

View File

@ -59,14 +59,14 @@ async def test_binary_sensor_states(
assert state.state == "on" assert state.state == "on"
async def test_snapshot_binary_sensor( async def test_binary_sensor_snapshot(
hass: HomeAssistant, hass: HomeAssistant,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
mock_automower_client: AsyncMock, mock_automower_client: AsyncMock,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
) -> None: ) -> None:
"""Test states of the binary sensors.""" """Snapshot test states of the binary sensors."""
with patch( with patch(
"homeassistant.components.husqvarna_automower.PLATFORMS", "homeassistant.components.husqvarna_automower.PLATFORMS",
[Platform.BINARY_SENSOR], [Platform.BINARY_SENSOR],

View File

@ -20,7 +20,7 @@ async def test_device_tracker_snapshot(
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
) -> None: ) -> None:
"""Test device tracker with a snapshot.""" """Snapshot test of the device tracker."""
with patch( with patch(
"homeassistant.components.husqvarna_automower.PLATFORMS", "homeassistant.components.husqvarna_automower.PLATFORMS",
[Platform.DEVICE_TRACKER], [Platform.DEVICE_TRACKER],

View File

@ -70,19 +70,16 @@ async def test_lawn_mower_commands(
) -> None: ) -> None:
"""Test lawn_mower commands.""" """Test lawn_mower commands."""
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)
getattr( getattr(
mock_automower_client.commands, aioautomower_command mock_automower_client.commands, aioautomower_command
).side_effect = ApiException("Test error") ).side_effect = ApiException("Test error")
with pytest.raises(
with pytest.raises(HomeAssistantError) as exc_info: HomeAssistantError,
match="Command couldn't be sent to the command queue: Test error",
):
await hass.services.async_call( await hass.services.async_call(
domain="lawn_mower", domain="lawn_mower",
service=service, service=service,
service_data={"entity_id": "lawn_mower.test_mower_1"}, service_data={"entity_id": "lawn_mower.test_mower_1"},
blocking=True, blocking=True,
) )
assert (
str(exc_info.value)
== "Command couldn't be sent to the command queue: Test error"
)

View File

@ -36,10 +36,13 @@ async def test_number_commands(
blocking=True, blocking=True,
) )
mocked_method = mock_automower_client.commands.set_cutting_height mocked_method = mock_automower_client.commands.set_cutting_height
assert len(mocked_method.mock_calls) == 1 mocked_method.assert_called_once_with(TEST_MOWER_ID, 3)
mocked_method.side_effect = ApiException("Test error") mocked_method.side_effect = ApiException("Test error")
with pytest.raises(HomeAssistantError) as exc_info: with pytest.raises(
HomeAssistantError,
match="Command couldn't be sent to the command queue: Test error",
):
await hass.services.async_call( await hass.services.async_call(
domain="number", domain="number",
service="set_value", service="set_value",
@ -47,10 +50,6 @@ async def test_number_commands(
service_data={"value": "3"}, service_data={"value": "3"},
blocking=True, blocking=True,
) )
assert (
str(exc_info.value)
== "Command couldn't be sent to the command queue: Test error"
)
assert len(mocked_method.mock_calls) == 2 assert len(mocked_method.mock_calls) == 2
@ -78,13 +77,16 @@ async def test_number_workarea_commands(
service_data={"value": "75"}, service_data={"value": "75"},
blocking=True, blocking=True,
) )
assert len(mocked_method.mock_calls) == 1 mocked_method.assert_called_once_with(TEST_MOWER_ID, 75, 123456)
state = hass.states.get(entity_id) state = hass.states.get(entity_id)
assert state.state is not None assert state.state is not None
assert state.state == "75" assert state.state == "75"
mocked_method.side_effect = ApiException("Test error") mocked_method.side_effect = ApiException("Test error")
with pytest.raises(HomeAssistantError) as exc_info: with pytest.raises(
HomeAssistantError,
match="Command couldn't be sent to the command queue: Test error",
):
await hass.services.async_call( await hass.services.async_call(
domain="number", domain="number",
service="set_value", service="set_value",
@ -92,10 +94,6 @@ async def test_number_workarea_commands(
service_data={"value": "75"}, service_data={"value": "75"},
blocking=True, blocking=True,
) )
assert (
str(exc_info.value)
== "Command couldn't be sent to the command queue: Test error"
)
assert len(mocked_method.mock_calls) == 2 assert len(mocked_method.mock_calls) == 2
@ -125,14 +123,14 @@ async def test_workarea_deleted(
@pytest.mark.usefixtures("entity_registry_enabled_by_default") @pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_snapshot_number( async def test_number_snapshot(
hass: HomeAssistant, hass: HomeAssistant,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
mock_automower_client: AsyncMock, mock_automower_client: AsyncMock,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
) -> None: ) -> None:
"""Test states of the number entity.""" """Snapshot tests of the number entities."""
with patch( with patch(
"homeassistant.components.husqvarna_automower.PLATFORMS", "homeassistant.components.husqvarna_automower.PLATFORMS",
[Platform.NUMBER], [Platform.NUMBER],

View File

@ -82,10 +82,14 @@ async def test_select_commands(
blocking=True, blocking=True,
) )
mocked_method = mock_automower_client.commands.set_headlight_mode mocked_method = mock_automower_client.commands.set_headlight_mode
mocked_method.assert_called_once_with(TEST_MOWER_ID, service.upper())
assert len(mocked_method.mock_calls) == 1 assert len(mocked_method.mock_calls) == 1
mocked_method.side_effect = ApiException("Test error") mocked_method.side_effect = ApiException("Test error")
with pytest.raises(HomeAssistantError) as exc_info: with pytest.raises(
HomeAssistantError,
match="Command couldn't be sent to the command queue: Test error",
):
await hass.services.async_call( await hass.services.async_call(
domain="select", domain="select",
service="select_option", service="select_option",
@ -95,8 +99,4 @@ async def test_select_commands(
}, },
blocking=True, blocking=True,
) )
assert (
str(exc_info.value)
== "Command couldn't be sent to the command queue: Test error"
)
assert len(mocked_method.mock_calls) == 2 assert len(mocked_method.mock_calls) == 2

View File

@ -144,14 +144,14 @@ async def test_error_sensor(
assert state.state == expected_state assert state.state == expected_state
async def test_sensor( async def test_sensor_snapshot(
hass: HomeAssistant, hass: HomeAssistant,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
mock_automower_client: AsyncMock, mock_automower_client: AsyncMock,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
) -> None: ) -> None:
"""Test states of the sensors.""" """Snapshot test of the sensors."""
with patch( with patch(
"homeassistant.components.husqvarna_automower.PLATFORMS", "homeassistant.components.husqvarna_automower.PLATFORMS",
[Platform.SENSOR], [Platform.SENSOR],

View File

@ -79,20 +79,19 @@ async def test_switch_commands(
blocking=True, blocking=True,
) )
mocked_method = getattr(mock_automower_client.commands, aioautomower_command) mocked_method = getattr(mock_automower_client.commands, aioautomower_command)
assert len(mocked_method.mock_calls) == 1 mocked_method.assert_called_once_with(TEST_MOWER_ID)
mocked_method.side_effect = ApiException("Test error") mocked_method.side_effect = ApiException("Test error")
with pytest.raises(HomeAssistantError) as exc_info: with pytest.raises(
HomeAssistantError,
match="Command couldn't be sent to the command queue: Test error",
):
await hass.services.async_call( await hass.services.async_call(
domain="switch", domain="switch",
service=service, service=service,
service_data={"entity_id": "switch.test_mower_1_enable_schedule"}, service_data={"entity_id": "switch.test_mower_1_enable_schedule"},
blocking=True, blocking=True,
) )
assert (
str(exc_info.value)
== "Command couldn't be sent to the command queue: Test error"
)
assert len(mocked_method.mock_calls) == 2 assert len(mocked_method.mock_calls) == 2
@ -172,14 +171,14 @@ async def test_zones_deleted(
) == (current_entries - 1) ) == (current_entries - 1)
async def test_switch( async def test_switch_snapshot(
hass: HomeAssistant, hass: HomeAssistant,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
mock_automower_client: AsyncMock, mock_automower_client: AsyncMock,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
) -> None: ) -> None:
"""Test states of the switch.""" """Snapshot tests of the switches."""
with patch( with patch(
"homeassistant.components.husqvarna_automower.PLATFORMS", "homeassistant.components.husqvarna_automower.PLATFORMS",
[Platform.SWITCH], [Platform.SWITCH],