Add lawn mower support to HomeKit (#140438)

Add lawn mower support to homekit
This commit is contained in:
Paul Bottein 2025-03-13 21:27:00 +01:00 committed by GitHub
parent 474d427b87
commit cdead8661d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 114 additions and 0 deletions

View File

@ -15,6 +15,7 @@ from pyhap.service import Service
from pyhap.util import callback as pyhap_callback
from homeassistant.components.cover import CoverDeviceClass, CoverEntityFeature
from homeassistant.components.lawn_mower import LawnMowerEntityFeature
from homeassistant.components.media_player import MediaPlayerDeviceClass
from homeassistant.components.remote import RemoteEntityFeature
from homeassistant.components.sensor import SensorDeviceClass
@ -250,6 +251,13 @@ def get_accessory( # noqa: C901
elif state.domain == "vacuum":
a_type = "Vacuum"
elif (
state.domain == "lawn_mower"
and features & LawnMowerEntityFeature.DOCK
and features & LawnMowerEntityFeature.START_MOWING
):
a_type = "LawnMower"
elif state.domain == "remote" and features & RemoteEntityFeature.ACTIVITY:
a_type = "ActivityRemote"

View File

@ -106,6 +106,7 @@ SUPPORTED_DOMAINS = [
"sensor",
"switch",
"vacuum",
"lawn_mower",
"water_heater",
VALVE_DOMAIN,
]
@ -123,6 +124,7 @@ DEFAULT_DOMAINS = [
REMOTE_DOMAIN,
"switch",
"vacuum",
"lawn_mower",
"water_heater",
]

View File

@ -16,6 +16,12 @@ from pyhap.const import (
from homeassistant.components import button, input_button
from homeassistant.components.input_select import ATTR_OPTIONS, SERVICE_SELECT_OPTION
from homeassistant.components.lawn_mower import (
DOMAIN as LAWN_MOWER_DOMAIN,
SERVICE_DOCK,
SERVICE_START_MOWING,
LawnMowerActivity,
)
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.components.vacuum import (
DOMAIN as VACUUM_DOMAIN,
@ -218,6 +224,29 @@ class Vacuum(Switch):
self.char_on.set_value(current_state)
@TYPES.register("LawnMower")
class LawnMower(Switch):
"""Generate a Switch accessory."""
def set_state(self, value: bool) -> None:
"""Move switch state to value if call came from HomeKit."""
_LOGGER.debug("%s: Set switch state to %s", self.entity_id, value)
state = self.hass.states.get(self.entity_id)
assert state
service = SERVICE_START_MOWING if value else SERVICE_DOCK
self.async_call_service(
LAWN_MOWER_DOMAIN, service, {ATTR_ENTITY_ID: self.entity_id}
)
@callback
def async_update_state(self, new_state: State) -> None:
"""Update switch state after state changed."""
current_state = new_state.state in (LawnMowerActivity.MOWING, STATE_ON)
_LOGGER.debug("%s: Set current state to %s", self.entity_id, current_state)
self.char_on.set_value(current_state)
class ValveBase(HomeAccessory):
"""Valve base class."""

View File

@ -12,6 +12,7 @@ from homeassistant.components.homekit.const import (
TYPE_VALVE,
)
from homeassistant.components.homekit.type_switches import (
LawnMower,
Outlet,
SelectSwitch,
Switch,
@ -19,6 +20,13 @@ from homeassistant.components.homekit.type_switches import (
Valve,
ValveSwitch,
)
from homeassistant.components.lawn_mower import (
DOMAIN as LAWN_MOWER_DOMAIN,
SERVICE_DOCK,
SERVICE_START_MOWING,
LawnMowerActivity,
LawnMowerEntityFeature,
)
from homeassistant.components.select import ATTR_OPTIONS
from homeassistant.components.vacuum import (
DOMAIN as VACUUM_DOMAIN,
@ -383,6 +391,73 @@ async def test_vacuum_set_state_without_returnhome_and_start_support(
assert events[-1].data[ATTR_VALUE] is None
async def test_lawn_mower_set_state(
hass: HomeAssistant, hk_driver, events: list[Event]
) -> None:
"""Test if Lawn mower accessory and HA are updated accordingly."""
entity_id = "lawn_mower.mower"
hass.states.async_set(
entity_id,
None,
{
ATTR_SUPPORTED_FEATURES: LawnMowerEntityFeature.DOCK
| LawnMowerEntityFeature.START_MOWING
},
)
await hass.async_block_till_done()
acc = LawnMower(hass, hk_driver, "LawnMower", entity_id, 2, None)
acc.run()
await hass.async_block_till_done()
assert acc.aid == 2
assert acc.category == 8 # Switch
assert acc.char_on.value == 0
hass.states.async_set(
entity_id,
LawnMowerActivity.MOWING,
{
ATTR_SUPPORTED_FEATURES: LawnMowerEntityFeature.DOCK
| LawnMowerEntityFeature.START_MOWING
},
)
await hass.async_block_till_done()
assert acc.char_on.value == 1
hass.states.async_set(
entity_id,
LawnMowerActivity.DOCKED,
{
ATTR_SUPPORTED_FEATURES: LawnMowerEntityFeature.DOCK
| LawnMowerEntityFeature.START_MOWING
},
)
await hass.async_block_till_done()
assert acc.char_on.value == 0
# Set from HomeKit
call_turn_on = async_mock_service(hass, LAWN_MOWER_DOMAIN, SERVICE_START_MOWING)
call_turn_off = async_mock_service(hass, LAWN_MOWER_DOMAIN, SERVICE_DOCK)
acc.char_on.client_update_value(1)
await hass.async_block_till_done()
assert acc.char_on.value == 1
assert call_turn_on
assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id
assert len(events) == 1
assert events[-1].data[ATTR_VALUE] is None
acc.char_on.client_update_value(0)
await hass.async_block_till_done()
assert acc.char_on.value == 0
assert call_turn_off
assert call_turn_off[0].data[ATTR_ENTITY_ID] == entity_id
assert len(events) == 2
assert events[-1].data[ATTR_VALUE] is None
async def test_reset_switch(
hass: HomeAssistant, hk_driver, events: list[Event]
) -> None: