mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 04:07:08 +00:00
Add EventEntity for Auto Shut Off events in Watergate integration (#135675)
* Add EventEntity for Auto Shut Off events in Watergate integration * Split events into two: volume and duration * Add icons to json. Extract some common translation keys. Simplify tests * Apply suggestions from code review * Fix --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
e7eb173e07
commit
a1a808b843
@ -18,8 +18,9 @@ from homeassistant.components.webhook import (
|
||||
)
|
||||
from homeassistant.const import CONF_IP_ADDRESS, CONF_WEBHOOK_ID, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import AUTO_SHUT_OFF_EVENT_NAME, DOMAIN
|
||||
from .coordinator import WatergateConfigEntry, WatergateDataCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -28,8 +29,10 @@ WEBHOOK_TELEMETRY_TYPE = "telemetry"
|
||||
WEBHOOK_VALVE_TYPE = "valve"
|
||||
WEBHOOK_WIFI_CHANGED_TYPE = "wifi-changed"
|
||||
WEBHOOK_POWER_SUPPLY_CHANGED_TYPE = "power-supply-changed"
|
||||
WEBHOOK_AUTO_SHUT_OFF = "auto-shut-off-report"
|
||||
|
||||
PLATFORMS: list[Platform] = [
|
||||
Platform.EVENT,
|
||||
Platform.SENSOR,
|
||||
Platform.VALVE,
|
||||
]
|
||||
@ -120,6 +123,10 @@ def get_webhook_handler(
|
||||
coordinator_data.networking.rssi = data.rssi
|
||||
elif body_type == WEBHOOK_POWER_SUPPLY_CHANGED_TYPE:
|
||||
coordinator_data.state.power_supply = data.supply
|
||||
elif body_type == WEBHOOK_AUTO_SHUT_OFF:
|
||||
async_dispatcher_send(
|
||||
hass, AUTO_SHUT_OFF_EVENT_NAME.format(data.type.lower()), data
|
||||
)
|
||||
|
||||
coordinator.async_set_updated_data(coordinator_data)
|
||||
|
||||
|
@ -3,3 +3,5 @@
|
||||
DOMAIN = "watergate"
|
||||
|
||||
MANUFACTURER = "Watergate"
|
||||
|
||||
AUTO_SHUT_OFF_EVENT_NAME = "watergate_{}"
|
||||
|
78
homeassistant/components/watergate/event.py
Normal file
78
homeassistant/components/watergate/event.py
Normal file
@ -0,0 +1,78 @@
|
||||
"""Module contains the AutoShutOffEvent class for handling auto shut off events."""
|
||||
|
||||
from watergate_local_api.models.auto_shut_off_report import AutoShutOffReport
|
||||
|
||||
from homeassistant.components.event import EventEntity, EventEntityDescription
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import WatergateConfigEntry
|
||||
from .const import AUTO_SHUT_OFF_EVENT_NAME
|
||||
from .coordinator import WatergateDataCoordinator
|
||||
from .entity import WatergateEntity
|
||||
|
||||
VOLUME_AUTO_SHUT_OFF = "volume_threshold"
|
||||
DURATION_AUTO_SHUT_OFF = "duration_threshold"
|
||||
|
||||
|
||||
DESCRIPTIONS: list[EventEntityDescription] = [
|
||||
EventEntityDescription(
|
||||
translation_key="auto_shut_off_volume",
|
||||
key="auto_shut_off_volume",
|
||||
event_types=[VOLUME_AUTO_SHUT_OFF],
|
||||
),
|
||||
EventEntityDescription(
|
||||
translation_key="auto_shut_off_duration",
|
||||
key="auto_shut_off_duration",
|
||||
event_types=[DURATION_AUTO_SHUT_OFF],
|
||||
),
|
||||
]
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: WatergateConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Event entities from config entry."""
|
||||
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
AutoShutOffEvent(coordinator, description) for description in DESCRIPTIONS
|
||||
)
|
||||
|
||||
|
||||
class AutoShutOffEvent(WatergateEntity, EventEntity):
|
||||
"""Event for Auto Shut Off."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: WatergateDataCoordinator,
|
||||
entity_description: EventEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize Auto Shut Off Entity."""
|
||||
super().__init__(coordinator, entity_description.key)
|
||||
self.entity_description = entity_description
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register the callback for event handling when the entity is added."""
|
||||
await super().async_added_to_hass()
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
AUTO_SHUT_OFF_EVENT_NAME.format(self.event_types[0]),
|
||||
self._async_handle_event,
|
||||
)
|
||||
)
|
||||
|
||||
@callback
|
||||
def _async_handle_event(self, event: AutoShutOffReport) -> None:
|
||||
self._trigger_event(
|
||||
event.type.lower(),
|
||||
{"volume": event.volume, "duration": event.duration},
|
||||
)
|
||||
self.async_write_ha_state()
|
12
homeassistant/components/watergate/icons.json
Normal file
12
homeassistant/components/watergate/icons.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"entity": {
|
||||
"event": {
|
||||
"auto_shut_off_volume": {
|
||||
"default": "mdi:water"
|
||||
},
|
||||
"auto_shut_off_duration": {
|
||||
"default": "mdi:timelapse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -17,10 +17,7 @@ rules:
|
||||
docs-high-level-description: done
|
||||
docs-installation-instructions: done
|
||||
docs-removal-instructions: done
|
||||
entity-event-setup:
|
||||
status: exempt
|
||||
comment: |
|
||||
Entities of this integration does not explicitly subscribe to events.
|
||||
entity-event-setup: done
|
||||
entity-unique-id: done
|
||||
has-entity-name: done
|
||||
runtime-data: done
|
||||
|
@ -19,6 +19,42 @@
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"event": {
|
||||
"auto_shut_off_volume": {
|
||||
"name": "Volume auto shut-off",
|
||||
"state_attributes": {
|
||||
"event_type": {
|
||||
"state": {
|
||||
"volume_threshold": "Volume",
|
||||
"duration_threshold": "Duration"
|
||||
}
|
||||
},
|
||||
"volume": {
|
||||
"name": "[%key:component::watergate::entity::event::auto_shut_off_volume::state_attributes::event_type::state::volume_threshold%]"
|
||||
},
|
||||
"duration": {
|
||||
"name": "[%key:component::watergate::entity::event::auto_shut_off_volume::state_attributes::event_type::state::duration_threshold%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"auto_shut_off_duration": {
|
||||
"name": "Duration auto shut-off",
|
||||
"state_attributes": {
|
||||
"event_type": {
|
||||
"state": {
|
||||
"volume_threshold": "[%key:component::watergate::entity::event::auto_shut_off_volume::state_attributes::event_type::state::volume_threshold%]",
|
||||
"duration_threshold": "[%key:component::watergate::entity::event::auto_shut_off_volume::state_attributes::event_type::state::duration_threshold%]"
|
||||
}
|
||||
},
|
||||
"volume": {
|
||||
"name": "[%key:component::watergate::entity::event::auto_shut_off_volume::state_attributes::event_type::state::volume_threshold%]"
|
||||
},
|
||||
"duration": {
|
||||
"name": "[%key:component::watergate::entity::event::auto_shut_off_volume::state_attributes::event_type::state::duration_threshold%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"water_meter_volume": {
|
||||
"name": "Water meter volume"
|
||||
|
111
tests/components/watergate/snapshots/test_event.ambr
Normal file
111
tests/components/watergate/snapshots/test_event.ambr
Normal file
@ -0,0 +1,111 @@
|
||||
# serializer version: 1
|
||||
# name: test_event[event.sonic_duration_auto_shut_off-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'event_types': list([
|
||||
'duration_threshold',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'event',
|
||||
'entity_category': None,
|
||||
'entity_id': 'event.sonic_duration_auto_shut_off',
|
||||
'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': 'Duration auto shut-off',
|
||||
'platform': 'watergate',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'auto_shut_off_duration',
|
||||
'unique_id': 'a63182948ce2896a.auto_shut_off_duration',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_event[event.sonic_duration_auto_shut_off-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'event_type': None,
|
||||
'event_types': list([
|
||||
'duration_threshold',
|
||||
]),
|
||||
'friendly_name': 'Sonic Duration auto shut-off',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'event.sonic_duration_auto_shut_off',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_event[event.sonic_volume_auto_shut_off-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'event_types': list([
|
||||
'volume_threshold',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'event',
|
||||
'entity_category': None,
|
||||
'entity_id': 'event.sonic_volume_auto_shut_off',
|
||||
'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': 'Volume auto shut-off',
|
||||
'platform': 'watergate',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'auto_shut_off_volume',
|
||||
'unique_id': 'a63182948ce2896a.auto_shut_off_volume',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_event[event.sonic_volume_auto_shut_off-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'event_type': None,
|
||||
'event_types': list([
|
||||
'volume_threshold',
|
||||
]),
|
||||
'friendly_name': 'Sonic Volume auto shut-off',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'event.sonic_volume_auto_shut_off',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
84
tests/components/watergate/test_event.py
Normal file
84
tests/components/watergate/test_event.py
Normal file
@ -0,0 +1,84 @@
|
||||
"""Tests for the Watergate event entity platform."""
|
||||
|
||||
from collections.abc import Generator
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.typing import StateType
|
||||
|
||||
from . import init_integration
|
||||
from .const import MOCK_WEBHOOK_ID
|
||||
|
||||
from tests.common import AsyncMock, MockConfigEntry, patch, snapshot_platform
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_event(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_entry: MockConfigEntry,
|
||||
mock_watergate_client: Generator[AsyncMock],
|
||||
freezer: FrozenDateTimeFactory,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test states of the sensor."""
|
||||
freezer.move_to("2021-01-09 12:00:00+00:00")
|
||||
with patch("homeassistant.components.watergate.PLATFORMS", [Platform.EVENT]):
|
||||
await init_integration(hass, mock_entry)
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_entry.entry_id)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("entity_id", "event_type"),
|
||||
[
|
||||
("sonic_volume_auto_shut_off", "volume_threshold"),
|
||||
("sonic_duration_auto_shut_off", "duration_threshold"),
|
||||
],
|
||||
)
|
||||
async def test_auto_shut_off_webhook(
|
||||
hass: HomeAssistant,
|
||||
hass_client_no_auth: ClientSessionGenerator,
|
||||
mock_entry: MockConfigEntry,
|
||||
mock_watergate_client: Generator[AsyncMock],
|
||||
entity_id: str,
|
||||
event_type: str,
|
||||
) -> None:
|
||||
"""Test if water flow webhook is handled correctly."""
|
||||
await init_integration(hass, mock_entry)
|
||||
|
||||
def assert_state(entity_id: str, expected_state: str):
|
||||
state = hass.states.get(f"event.{entity_id}")
|
||||
assert state.state == str(expected_state)
|
||||
|
||||
assert_state(entity_id, "unknown")
|
||||
|
||||
telemetry_change_data = {
|
||||
"type": "auto-shut-off-report",
|
||||
"data": {
|
||||
"type": event_type,
|
||||
"volume": 1500,
|
||||
"duration": 30,
|
||||
"timestamp": 1730148016,
|
||||
},
|
||||
}
|
||||
client = await hass_client_no_auth()
|
||||
await client.post(f"/api/webhook/{MOCK_WEBHOOK_ID}", json=telemetry_change_data)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
def assert_extra_state(
|
||||
entity_id: str, attribute: str, expected_attribute: StateType
|
||||
):
|
||||
attributes = hass.states.get(f"event.{entity_id}").attributes
|
||||
assert attributes.get(attribute) == expected_attribute
|
||||
|
||||
assert_extra_state(entity_id, "event_type", event_type)
|
||||
assert_extra_state(entity_id, "volume", 1500)
|
||||
assert_extra_state(entity_id, "duration", 30)
|
@ -1,4 +1,4 @@
|
||||
"""Tests for the Watergate valve platform."""
|
||||
"""Tests for the Watergate sensor platform."""
|
||||
|
||||
from collections.abc import Generator
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user