forked from home-assistant/core
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add valve entity to gardena (home-assistant#120160)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
- Loading branch information
Showing
5 changed files
with
190 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
"""Support for switch entities.""" | ||
|
||
from __future__ import annotations | ||
|
||
from typing import Any | ||
|
||
from gardena_bluetooth.const import Valve | ||
|
||
from homeassistant.components.valve import ValveEntity, ValveEntityFeature | ||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.helpers.entity_platform import AddEntitiesCallback | ||
|
||
from .const import DOMAIN | ||
from .coordinator import Coordinator, GardenaBluetoothEntity | ||
|
||
FALLBACK_WATERING_TIME_IN_SECONDS = 60 * 60 | ||
|
||
|
||
async def async_setup_entry( | ||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback | ||
) -> None: | ||
"""Set up switch based on a config entry.""" | ||
coordinator: Coordinator = hass.data[DOMAIN][entry.entry_id] | ||
entities = [] | ||
if GardenaBluetoothValve.characteristics.issubset(coordinator.characteristics): | ||
entities.append(GardenaBluetoothValve(coordinator)) | ||
|
||
async_add_entities(entities) | ||
|
||
|
||
class GardenaBluetoothValve(GardenaBluetoothEntity, ValveEntity): | ||
"""Representation of a valve switch.""" | ||
|
||
_attr_name = None | ||
_attr_is_closed: bool | None = None | ||
_attr_reports_position = False | ||
_attr_supported_features = ValveEntityFeature.OPEN | ValveEntityFeature.CLOSE | ||
|
||
characteristics = { | ||
Valve.state.uuid, | ||
Valve.manual_watering_time.uuid, | ||
Valve.remaining_open_time.uuid, | ||
} | ||
|
||
def __init__( | ||
self, | ||
coordinator: Coordinator, | ||
) -> None: | ||
"""Initialize the switch.""" | ||
super().__init__( | ||
coordinator, {Valve.state.uuid, Valve.manual_watering_time.uuid} | ||
) | ||
self._attr_unique_id = f"{coordinator.address}-{Valve.state.uuid}" | ||
|
||
def _handle_coordinator_update(self) -> None: | ||
self._attr_is_closed = not self.coordinator.get_cached(Valve.state) | ||
super()._handle_coordinator_update() | ||
|
||
async def async_open_valve(self, **kwargs: Any) -> None: | ||
"""Turn the entity on.""" | ||
value = ( | ||
self.coordinator.get_cached(Valve.manual_watering_time) | ||
or FALLBACK_WATERING_TIME_IN_SECONDS | ||
) | ||
await self.coordinator.write(Valve.remaining_open_time, value) | ||
self._attr_is_closed = False | ||
self.async_write_ha_state() | ||
|
||
async def async_close_valve(self, **kwargs: Any) -> None: | ||
"""Turn the entity off.""" | ||
await self.coordinator.write(Valve.remaining_open_time, 0) | ||
self._attr_is_closed = True | ||
self.async_write_ha_state() |
29 changes: 29 additions & 0 deletions
29
tests/components/gardena_bluetooth/snapshots/test_valve.ambr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# serializer version: 1 | ||
# name: test_setup | ||
StateSnapshot({ | ||
'attributes': ReadOnlyDict({ | ||
'friendly_name': 'Mock Title', | ||
'supported_features': <ValveEntityFeature: 3>, | ||
}), | ||
'context': <ANY>, | ||
'entity_id': 'valve.mock_title', | ||
'last_changed': <ANY>, | ||
'last_reported': <ANY>, | ||
'last_updated': <ANY>, | ||
'state': 'closed', | ||
}) | ||
# --- | ||
# name: test_setup.1 | ||
StateSnapshot({ | ||
'attributes': ReadOnlyDict({ | ||
'friendly_name': 'Mock Title', | ||
'supported_features': <ValveEntityFeature: 3>, | ||
}), | ||
'context': <ANY>, | ||
'entity_id': 'valve.mock_title', | ||
'last_changed': <ANY>, | ||
'last_reported': <ANY>, | ||
'last_updated': <ANY>, | ||
'state': 'open', | ||
}) | ||
# --- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
"""Test Gardena Bluetooth valve.""" | ||
|
||
from collections.abc import Awaitable, Callable | ||
from unittest.mock import Mock, call | ||
|
||
from gardena_bluetooth.const import Valve | ||
import pytest | ||
from syrupy.assertion import SnapshotAssertion | ||
|
||
from homeassistant.components.valve import DOMAIN as VALVE_DOMAIN | ||
from homeassistant.const import ( | ||
ATTR_ENTITY_ID, | ||
SERVICE_CLOSE_VALVE, | ||
SERVICE_OPEN_VALVE, | ||
Platform, | ||
) | ||
from homeassistant.core import HomeAssistant | ||
|
||
from . import setup_entry | ||
|
||
from tests.common import MockConfigEntry | ||
|
||
|
||
@pytest.fixture | ||
def mock_switch_chars(mock_read_char_raw): | ||
"""Mock data on device.""" | ||
mock_read_char_raw[Valve.state.uuid] = b"\x00" | ||
mock_read_char_raw[Valve.remaining_open_time.uuid] = ( | ||
Valve.remaining_open_time.encode(0) | ||
) | ||
mock_read_char_raw[Valve.manual_watering_time.uuid] = ( | ||
Valve.manual_watering_time.encode(1000) | ||
) | ||
return mock_read_char_raw | ||
|
||
|
||
async def test_setup( | ||
hass: HomeAssistant, | ||
snapshot: SnapshotAssertion, | ||
mock_entry: MockConfigEntry, | ||
mock_client: Mock, | ||
mock_switch_chars: dict[str, bytes], | ||
scan_step: Callable[[], Awaitable[None]], | ||
) -> None: | ||
"""Test setup creates expected entities.""" | ||
|
||
entity_id = "valve.mock_title" | ||
await setup_entry(hass, mock_entry, [Platform.VALVE]) | ||
assert hass.states.get(entity_id) == snapshot | ||
|
||
mock_switch_chars[Valve.state.uuid] = b"\x01" | ||
await scan_step() | ||
assert hass.states.get(entity_id) == snapshot | ||
|
||
|
||
async def test_switching( | ||
hass: HomeAssistant, | ||
mock_entry: MockConfigEntry, | ||
mock_client: Mock, | ||
mock_switch_chars: dict[str, bytes], | ||
) -> None: | ||
"""Test switching makes correct calls.""" | ||
|
||
entity_id = "valve.mock_title" | ||
await setup_entry(hass, mock_entry, [Platform.VALVE]) | ||
assert hass.states.get(entity_id) | ||
|
||
await hass.services.async_call( | ||
VALVE_DOMAIN, | ||
SERVICE_OPEN_VALVE, | ||
{ATTR_ENTITY_ID: entity_id}, | ||
blocking=True, | ||
) | ||
|
||
await hass.services.async_call( | ||
VALVE_DOMAIN, | ||
SERVICE_CLOSE_VALVE, | ||
{ATTR_ENTITY_ID: entity_id}, | ||
blocking=True, | ||
) | ||
|
||
assert mock_client.write_char.mock_calls == [ | ||
call(Valve.remaining_open_time, 1000), | ||
call(Valve.remaining_open_time, 0), | ||
] |