Add details to Husqvarna Automower restricted reason sensor (#147678)

Co-authored-by: Norbert Rittel <norbert@rittel.de>
This commit is contained in:
Thomas55555 2025-07-28 20:54:54 +02:00 committed by GitHub
parent dda46e7e0b
commit 7f9be420d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 137 additions and 9 deletions

View File

@ -8,6 +8,7 @@ from operator import attrgetter
from typing import TYPE_CHECKING, Any
from aioautomower.model import (
ExternalReasons,
InactiveReasons,
MowerAttributes,
MowerModes,
@ -190,11 +191,37 @@ RESTRICTED_REASONS: list = [
RestrictedReasons.PARK_OVERRIDE,
RestrictedReasons.SENSOR,
RestrictedReasons.WEEK_SCHEDULE,
ExternalReasons.AMAZON_ALEXA,
ExternalReasons.DEVELOPER_PORTAL,
ExternalReasons.GARDENA_SMART_SYSTEM,
ExternalReasons.GOOGLE_ASSISTANT,
ExternalReasons.HOME_ASSISTANT,
ExternalReasons.IFTTT,
ExternalReasons.IFTTT_APPLETS,
ExternalReasons.IFTTT_CALENDAR_CONNECTION,
ExternalReasons.SMART_ROUTINE,
ExternalReasons.SMART_ROUTINE_FROST_GUARD,
ExternalReasons.SMART_ROUTINE_RAIN_GUARD,
ExternalReasons.SMART_ROUTINE_WILDLIFE_PROTECTION,
]
STATE_NO_WORK_AREA_ACTIVE = "no_work_area_active"
@callback
def _get_restricted_reason(data: MowerAttributes) -> str:
"""Return the restricted reason.
If there is an external reason, return that instead, if it's available.
"""
if (
data.planner.restricted_reason == RestrictedReasons.EXTERNAL
and data.planner.external_reason is not None
):
return data.planner.external_reason
return data.planner.restricted_reason
@callback
def _get_work_area_names(data: MowerAttributes) -> list[str]:
"""Return a list with all work area names."""
@ -400,7 +427,7 @@ MOWER_SENSOR_TYPES: tuple[AutomowerSensorEntityDescription, ...] = (
translation_key="restricted_reason",
device_class=SensorDeviceClass.ENUM,
option_fn=lambda data: RESTRICTED_REASONS,
value_fn=attrgetter("planner.restricted_reason"),
value_fn=_get_restricted_reason,
),
AutomowerSensorEntityDescription(
key="inactive_reason",

View File

@ -242,16 +242,28 @@
"restricted_reason": {
"name": "Restricted reason",
"state": {
"none": "No restrictions",
"week_schedule": "Week schedule",
"park_override": "Park override",
"sensor": "Weather timer",
"all_work_areas_completed": "All work areas completed",
"amazon_alexa": "Amazon Alexa",
"daily_limit": "Daily limit",
"developer_portal": "Developer Portal",
"external": "External",
"fota": "Firmware Over-the-Air update running",
"frost": "Frost",
"all_work_areas_completed": "All work areas completed",
"external": "External",
"not_applicable": "Not applicable"
"gardena_smart_system": "Gardena Smart System",
"google_assistant": "Google Assistant",
"home_assistant": "Home Assistant",
"ifttt_applets": "IFTTT applets",
"ifttt_calendar_connection": "IFTTT calendar connection",
"ifttt": "IFTTT",
"none": "No restrictions",
"not_applicable": "Not applicable",
"park_override": "Park override",
"sensor": "Weather timer",
"smart_routine_frost_guard": "Frost guard",
"smart_routine_rain_guard": "Rain guard",
"smart_routine_wildlife_protection": "Wildlife protection",
"smart_routine": "Generic smart routine",
"week_schedule": "Week schedule"
}
},
"total_charging_time": {

View File

@ -978,6 +978,18 @@
<RestrictedReasons.PARK_OVERRIDE: 'park_override'>,
<RestrictedReasons.SENSOR: 'sensor'>,
<RestrictedReasons.WEEK_SCHEDULE: 'week_schedule'>,
<ExternalReasons.AMAZON_ALEXA: 'amazon_alexa'>,
<ExternalReasons.DEVELOPER_PORTAL: 'developer_portal'>,
<ExternalReasons.GARDENA_SMART_SYSTEM: 'gardena_smart_system'>,
<ExternalReasons.GOOGLE_ASSISTANT: 'google_assistant'>,
<ExternalReasons.HOME_ASSISTANT: 'home_assistant'>,
<ExternalReasons.IFTTT: 'ifttt'>,
<ExternalReasons.IFTTT_APPLETS: 'ifttt_applets'>,
<ExternalReasons.IFTTT_CALENDAR_CONNECTION: 'ifttt_calendar_connection'>,
<ExternalReasons.SMART_ROUTINE: 'smart_routine'>,
<ExternalReasons.SMART_ROUTINE_FROST_GUARD: 'smart_routine_frost_guard'>,
<ExternalReasons.SMART_ROUTINE_RAIN_GUARD: 'smart_routine_rain_guard'>,
<ExternalReasons.SMART_ROUTINE_WILDLIFE_PROTECTION: 'smart_routine_wildlife_protection'>,
]),
}),
'config_entry_id': <ANY>,
@ -1025,6 +1037,18 @@
<RestrictedReasons.PARK_OVERRIDE: 'park_override'>,
<RestrictedReasons.SENSOR: 'sensor'>,
<RestrictedReasons.WEEK_SCHEDULE: 'week_schedule'>,
<ExternalReasons.AMAZON_ALEXA: 'amazon_alexa'>,
<ExternalReasons.DEVELOPER_PORTAL: 'developer_portal'>,
<ExternalReasons.GARDENA_SMART_SYSTEM: 'gardena_smart_system'>,
<ExternalReasons.GOOGLE_ASSISTANT: 'google_assistant'>,
<ExternalReasons.HOME_ASSISTANT: 'home_assistant'>,
<ExternalReasons.IFTTT: 'ifttt'>,
<ExternalReasons.IFTTT_APPLETS: 'ifttt_applets'>,
<ExternalReasons.IFTTT_CALENDAR_CONNECTION: 'ifttt_calendar_connection'>,
<ExternalReasons.SMART_ROUTINE: 'smart_routine'>,
<ExternalReasons.SMART_ROUTINE_FROST_GUARD: 'smart_routine_frost_guard'>,
<ExternalReasons.SMART_ROUTINE_RAIN_GUARD: 'smart_routine_rain_guard'>,
<ExternalReasons.SMART_ROUTINE_WILDLIFE_PROTECTION: 'smart_routine_wildlife_protection'>,
]),
}),
'context': <ANY>,
@ -1953,6 +1977,18 @@
<RestrictedReasons.PARK_OVERRIDE: 'park_override'>,
<RestrictedReasons.SENSOR: 'sensor'>,
<RestrictedReasons.WEEK_SCHEDULE: 'week_schedule'>,
<ExternalReasons.AMAZON_ALEXA: 'amazon_alexa'>,
<ExternalReasons.DEVELOPER_PORTAL: 'developer_portal'>,
<ExternalReasons.GARDENA_SMART_SYSTEM: 'gardena_smart_system'>,
<ExternalReasons.GOOGLE_ASSISTANT: 'google_assistant'>,
<ExternalReasons.HOME_ASSISTANT: 'home_assistant'>,
<ExternalReasons.IFTTT: 'ifttt'>,
<ExternalReasons.IFTTT_APPLETS: 'ifttt_applets'>,
<ExternalReasons.IFTTT_CALENDAR_CONNECTION: 'ifttt_calendar_connection'>,
<ExternalReasons.SMART_ROUTINE: 'smart_routine'>,
<ExternalReasons.SMART_ROUTINE_FROST_GUARD: 'smart_routine_frost_guard'>,
<ExternalReasons.SMART_ROUTINE_RAIN_GUARD: 'smart_routine_rain_guard'>,
<ExternalReasons.SMART_ROUTINE_WILDLIFE_PROTECTION: 'smart_routine_wildlife_protection'>,
]),
}),
'config_entry_id': <ANY>,
@ -2000,6 +2036,18 @@
<RestrictedReasons.PARK_OVERRIDE: 'park_override'>,
<RestrictedReasons.SENSOR: 'sensor'>,
<RestrictedReasons.WEEK_SCHEDULE: 'week_schedule'>,
<ExternalReasons.AMAZON_ALEXA: 'amazon_alexa'>,
<ExternalReasons.DEVELOPER_PORTAL: 'developer_portal'>,
<ExternalReasons.GARDENA_SMART_SYSTEM: 'gardena_smart_system'>,
<ExternalReasons.GOOGLE_ASSISTANT: 'google_assistant'>,
<ExternalReasons.HOME_ASSISTANT: 'home_assistant'>,
<ExternalReasons.IFTTT: 'ifttt'>,
<ExternalReasons.IFTTT_APPLETS: 'ifttt_applets'>,
<ExternalReasons.IFTTT_CALENDAR_CONNECTION: 'ifttt_calendar_connection'>,
<ExternalReasons.SMART_ROUTINE: 'smart_routine'>,
<ExternalReasons.SMART_ROUTINE_FROST_GUARD: 'smart_routine_frost_guard'>,
<ExternalReasons.SMART_ROUTINE_RAIN_GUARD: 'smart_routine_rain_guard'>,
<ExternalReasons.SMART_ROUTINE_WILDLIFE_PROTECTION: 'smart_routine_wildlife_protection'>,
]),
}),
'context': <ANY>,

View File

@ -4,7 +4,13 @@ import datetime
from unittest.mock import AsyncMock, patch
import zoneinfo
from aioautomower.model import MowerAttributes, MowerModes, MowerStates
from aioautomower.model import (
ExternalReasons,
MowerAttributes,
MowerModes,
MowerStates,
RestrictedReasons,
)
from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy.assertion import SnapshotAssertion
@ -123,6 +129,41 @@ async def test_work_area_sensor(
assert state.state == "no_work_area_active"
async def test_restricted_reason_sensor(
hass: HomeAssistant,
mock_automower_client: AsyncMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
values: dict[str, MowerAttributes],
) -> None:
"""Test the work area sensor."""
sensor = "sensor.test_mower_1_restricted_reason"
await setup_integration(hass, mock_config_entry)
state = hass.states.get(sensor)
assert state is not None
assert state.state == RestrictedReasons.WEEK_SCHEDULE
values[TEST_MOWER_ID].planner.restricted_reason = RestrictedReasons.EXTERNAL
values[TEST_MOWER_ID].planner.external_reason = None
mock_automower_client.get_status.return_value = values
freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get(sensor)
assert state.state == RestrictedReasons.EXTERNAL
values[TEST_MOWER_ID].planner.restricted_reason = RestrictedReasons.EXTERNAL
values[
TEST_MOWER_ID
].planner.external_reason = ExternalReasons.SMART_ROUTINE_WILDLIFE_PROTECTION
mock_automower_client.get_status.return_value = values
freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get(sensor)
assert state.state == ExternalReasons.SMART_ROUTINE_WILDLIFE_PROTECTION
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize(
("sensor_to_test"),