Replace Guardian disable_ap
and enable_ap
services with a switch (#75034)
* Starter buttons * Ready to go * Replace Guardian `disable_ap` and `enable_ap` services with a switch * Clean up how actions are stored * Make similar to buttons * Remove service definitions * Docstring * Docstring * flake8 * Add repairs item * Add a repairs issue to notify of removed entity * Add entity replacement strategy * Fix repairs import * Update deprecation version * Remove breaking change * Include future breaking change version * Naming
This commit is contained in:
parent
1f410e884a
commit
13d3f4c3b2
7 changed files with 220 additions and 50 deletions
|
@ -238,11 +238,25 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
@call_with_data
|
@call_with_data
|
||||||
async def async_disable_ap(call: ServiceCall, data: GuardianData) -> None:
|
async def async_disable_ap(call: ServiceCall, data: GuardianData) -> None:
|
||||||
"""Disable the onboard AP."""
|
"""Disable the onboard AP."""
|
||||||
|
async_log_deprecated_service_call(
|
||||||
|
hass,
|
||||||
|
call,
|
||||||
|
"switch.turn_off",
|
||||||
|
f"switch.guardian_valve_controller_{entry.data[CONF_UID]}_onboard_ap",
|
||||||
|
"2022.12.0",
|
||||||
|
)
|
||||||
await data.client.wifi.disable_ap()
|
await data.client.wifi.disable_ap()
|
||||||
|
|
||||||
@call_with_data
|
@call_with_data
|
||||||
async def async_enable_ap(call: ServiceCall, data: GuardianData) -> None:
|
async def async_enable_ap(call: ServiceCall, data: GuardianData) -> None:
|
||||||
"""Enable the onboard AP."""
|
"""Enable the onboard AP."""
|
||||||
|
async_log_deprecated_service_call(
|
||||||
|
hass,
|
||||||
|
call,
|
||||||
|
"switch.turn_on",
|
||||||
|
f"switch.guardian_valve_controller_{entry.data[CONF_UID]}_onboard_ap",
|
||||||
|
"2022.12.0",
|
||||||
|
)
|
||||||
await data.client.wifi.enable_ap()
|
await data.client.wifi.enable_ap()
|
||||||
|
|
||||||
@call_with_data
|
@call_with_data
|
||||||
|
|
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
|
DOMAIN as BINARY_SENSOR_DOMAIN,
|
||||||
BinarySensorDeviceClass,
|
BinarySensorDeviceClass,
|
||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
BinarySensorEntityDescription,
|
BinarySensorEntityDescription,
|
||||||
|
@ -27,7 +28,11 @@ from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SIGNAL_PAIRED_SENSOR_COORDINATOR_ADDED,
|
SIGNAL_PAIRED_SENSOR_COORDINATOR_ADDED,
|
||||||
)
|
)
|
||||||
from .util import GuardianDataUpdateCoordinator
|
from .util import (
|
||||||
|
EntityDomainReplacementStrategy,
|
||||||
|
GuardianDataUpdateCoordinator,
|
||||||
|
async_finish_entity_domain_replacements,
|
||||||
|
)
|
||||||
|
|
||||||
ATTR_CONNECTED_CLIENTS = "connected_clients"
|
ATTR_CONNECTED_CLIENTS = "connected_clients"
|
||||||
|
|
||||||
|
@ -79,6 +84,21 @@ async def async_setup_entry(
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Guardian switches based on a config entry."""
|
"""Set up Guardian switches based on a config entry."""
|
||||||
data: GuardianData = hass.data[DOMAIN][entry.entry_id]
|
data: GuardianData = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
uid = entry.data[CONF_UID]
|
||||||
|
|
||||||
|
async_finish_entity_domain_replacements(
|
||||||
|
hass,
|
||||||
|
entry,
|
||||||
|
(
|
||||||
|
EntityDomainReplacementStrategy(
|
||||||
|
BINARY_SENSOR_DOMAIN,
|
||||||
|
f"{uid}_ap_enabled",
|
||||||
|
f"switch.guardian_valve_controller_{uid}_onboard_ap",
|
||||||
|
"2022.12.0",
|
||||||
|
remove_old_entity=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def add_new_paired_sensor(uid: str) -> None:
|
def add_new_paired_sensor(uid: str) -> None:
|
||||||
|
|
|
@ -1,26 +1,4 @@
|
||||||
# Describes the format for available Elexa Guardians services
|
# Describes the format for available Elexa Guardians services
|
||||||
disable_ap:
|
|
||||||
name: Disable AP
|
|
||||||
description: Disable the device's onboard access point.
|
|
||||||
fields:
|
|
||||||
device_id:
|
|
||||||
name: Valve Controller
|
|
||||||
description: The valve controller whose AP should be disabled
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
device:
|
|
||||||
integration: guardian
|
|
||||||
enable_ap:
|
|
||||||
name: Enable AP
|
|
||||||
description: Enable the device's onboard access point.
|
|
||||||
fields:
|
|
||||||
device_id:
|
|
||||||
name: Valve Controller
|
|
||||||
description: The valve controller whose AP should be enabled
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
device:
|
|
||||||
integration: guardian
|
|
||||||
pair_sensor:
|
pair_sensor:
|
||||||
name: Pair Sensor
|
name: Pair Sensor
|
||||||
description: Add a new paired sensor to the valve controller.
|
description: Add a new paired sensor to the valve controller.
|
||||||
|
@ -39,6 +17,28 @@ pair_sensor:
|
||||||
example: 5410EC688BCF
|
example: 5410EC688BCF
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
|
reboot:
|
||||||
|
name: Reboot
|
||||||
|
description: Reboot the device.
|
||||||
|
fields:
|
||||||
|
device_id:
|
||||||
|
name: Valve Controller
|
||||||
|
description: The valve controller to reboot
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
device:
|
||||||
|
integration: guardian
|
||||||
|
reset_valve_diagnostics:
|
||||||
|
name: Reset Valve Diagnostics
|
||||||
|
description: Fully (and irrecoverably) reset all valve diagnostics.
|
||||||
|
fields:
|
||||||
|
device_id:
|
||||||
|
name: Valve Controller
|
||||||
|
description: The valve controller whose diagnostics should be reset
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
device:
|
||||||
|
integration: guardian
|
||||||
unpair_sensor:
|
unpair_sensor:
|
||||||
name: Unpair Sensor
|
name: Unpair Sensor
|
||||||
description: Remove a paired sensor from the valve controller.
|
description: Remove a paired sensor from the valve controller.
|
||||||
|
|
|
@ -25,7 +25,18 @@
|
||||||
"step": {
|
"step": {
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"title": "The {deprecated_service} service is being removed",
|
"title": "The {deprecated_service} service is being removed",
|
||||||
"description": "Update any automations or scripts that use this service to instead use the `{alternate_service}` service with a target entity ID of `{alternate_target}`. Then, click SUBMIT below to mark this issue as resolved."
|
"description": "Update any automations or scripts that use this service to instead use the `{alternate_service}` service with a target entity ID of `{alternate_target}`."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"replaced_old_entity": {
|
||||||
|
"title": "The {old_entity_id} entity will be removed",
|
||||||
|
"fix_flow": {
|
||||||
|
"step": {
|
||||||
|
"confirm": {
|
||||||
|
"title": "The {old_entity_id} entity will be removed",
|
||||||
|
"description": "This entity has been replaced by `{replacement_entity_id}`."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +1,86 @@
|
||||||
"""Switches for the Elexa Guardian integration."""
|
"""Switches for the Elexa Guardian integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Awaitable, Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from aioguardian import Client
|
||||||
from aioguardian.errors import GuardianError
|
from aioguardian.errors import GuardianError
|
||||||
|
|
||||||
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
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.entity import EntityCategory
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import GuardianData, ValveControllerEntity, ValveControllerEntityDescription
|
from . import GuardianData, ValveControllerEntity, ValveControllerEntityDescription
|
||||||
from .const import API_VALVE_STATUS, DOMAIN
|
from .const import API_VALVE_STATUS, API_WIFI_STATUS, DOMAIN
|
||||||
|
|
||||||
ATTR_AVG_CURRENT = "average_current"
|
ATTR_AVG_CURRENT = "average_current"
|
||||||
|
ATTR_CONNECTED_CLIENTS = "connected_clients"
|
||||||
ATTR_INST_CURRENT = "instantaneous_current"
|
ATTR_INST_CURRENT = "instantaneous_current"
|
||||||
ATTR_INST_CURRENT_DDT = "instantaneous_current_ddt"
|
ATTR_INST_CURRENT_DDT = "instantaneous_current_ddt"
|
||||||
|
ATTR_STATION_CONNECTED = "station_connected"
|
||||||
ATTR_TRAVEL_COUNT = "travel_count"
|
ATTR_TRAVEL_COUNT = "travel_count"
|
||||||
|
|
||||||
|
SWITCH_KIND_ONBOARD_AP = "onboard_ap"
|
||||||
SWITCH_KIND_VALVE = "valve"
|
SWITCH_KIND_VALVE = "valve"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SwitchDescriptionMixin:
|
||||||
|
"""Define an entity description mixin for Guardian switches."""
|
||||||
|
|
||||||
|
off_action: Callable[[Client], Awaitable]
|
||||||
|
on_action: Callable[[Client], Awaitable]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ValveControllerSwitchDescription(
|
class ValveControllerSwitchDescription(
|
||||||
SwitchEntityDescription, ValveControllerEntityDescription
|
SwitchEntityDescription, ValveControllerEntityDescription, SwitchDescriptionMixin
|
||||||
):
|
):
|
||||||
"""Describe a Guardian valve controller switch."""
|
"""Describe a Guardian valve controller switch."""
|
||||||
|
|
||||||
|
|
||||||
|
async def _async_disable_ap(client: Client) -> None:
|
||||||
|
"""Disable the onboard AP."""
|
||||||
|
await client.wifi.disable_ap()
|
||||||
|
|
||||||
|
|
||||||
|
async def _async_enable_ap(client: Client) -> None:
|
||||||
|
"""Enable the onboard AP."""
|
||||||
|
await client.wifi.enable_ap()
|
||||||
|
|
||||||
|
|
||||||
|
async def _async_close_valve(client: Client) -> None:
|
||||||
|
"""Close the valve."""
|
||||||
|
await client.valve.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def _async_open_valve(client: Client) -> None:
|
||||||
|
"""Open the valve."""
|
||||||
|
await client.valve.open()
|
||||||
|
|
||||||
|
|
||||||
VALVE_CONTROLLER_DESCRIPTIONS = (
|
VALVE_CONTROLLER_DESCRIPTIONS = (
|
||||||
|
ValveControllerSwitchDescription(
|
||||||
|
key=SWITCH_KIND_ONBOARD_AP,
|
||||||
|
name="Onboard AP",
|
||||||
|
icon="mdi:wifi",
|
||||||
|
entity_category=EntityCategory.CONFIG,
|
||||||
|
api_category=API_WIFI_STATUS,
|
||||||
|
off_action=_async_disable_ap,
|
||||||
|
on_action=_async_enable_ap,
|
||||||
|
),
|
||||||
ValveControllerSwitchDescription(
|
ValveControllerSwitchDescription(
|
||||||
key=SWITCH_KIND_VALVE,
|
key=SWITCH_KIND_VALVE,
|
||||||
name="Valve controller",
|
name="Valve controller",
|
||||||
icon="mdi:water",
|
icon="mdi:water",
|
||||||
api_category=API_VALVE_STATUS,
|
api_category=API_VALVE_STATUS,
|
||||||
|
off_action=_async_close_valve,
|
||||||
|
on_action=_async_open_valve,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -53,9 +98,7 @@ async def async_setup_entry(
|
||||||
|
|
||||||
|
|
||||||
class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
|
class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
|
||||||
"""Define a switch to open/close the Guardian valve."""
|
"""Define a switch related to a Guardian valve controller."""
|
||||||
|
|
||||||
entity_description: ValveControllerSwitchDescription
|
|
||||||
|
|
||||||
ON_STATES = {
|
ON_STATES = {
|
||||||
"start_opening",
|
"start_opening",
|
||||||
|
@ -64,6 +107,8 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
|
||||||
"opened",
|
"opened",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entity_description: ValveControllerSwitchDescription
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
entry: ConfigEntry,
|
entry: ConfigEntry,
|
||||||
|
@ -73,12 +118,20 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
super().__init__(entry, data.valve_controller_coordinators, description)
|
super().__init__(entry, data.valve_controller_coordinators, description)
|
||||||
|
|
||||||
self._attr_is_on = True
|
|
||||||
self._client = data.client
|
self._client = data.client
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_update_from_latest_data(self) -> None:
|
def _async_update_from_latest_data(self) -> None:
|
||||||
"""Update the entity."""
|
"""Update the entity."""
|
||||||
|
if self.entity_description.key == SWITCH_KIND_ONBOARD_AP:
|
||||||
|
self._attr_extra_state_attributes.update(
|
||||||
|
{
|
||||||
|
ATTR_CONNECTED_CLIENTS: self.coordinator.data.get("ap_clients"),
|
||||||
|
ATTR_STATION_CONNECTED: self.coordinator.data["station_connected"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self._attr_is_on = self.coordinator.data["ap_enabled"]
|
||||||
|
elif self.entity_description.key == SWITCH_KIND_VALVE:
|
||||||
self._attr_is_on = self.coordinator.data["state"] in self.ON_STATES
|
self._attr_is_on = self.coordinator.data["state"] in self.ON_STATES
|
||||||
self._attr_extra_state_attributes.update(
|
self._attr_extra_state_attributes.update(
|
||||||
{
|
{
|
||||||
|
@ -92,23 +145,27 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the valve off (closed)."""
|
"""Turn the switch off."""
|
||||||
try:
|
try:
|
||||||
async with self._client:
|
async with self._client:
|
||||||
await self._client.valve.close()
|
await self.entity_description.off_action(self._client)
|
||||||
except GuardianError as err:
|
except GuardianError as err:
|
||||||
raise HomeAssistantError(f"Error while closing the valve: {err}") from err
|
raise HomeAssistantError(
|
||||||
|
f'Error while turning "{self.entity_id}" off: {err}'
|
||||||
|
) from err
|
||||||
|
|
||||||
self._attr_is_on = False
|
self._attr_is_on = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the valve on (open)."""
|
"""Turn the switch on."""
|
||||||
try:
|
try:
|
||||||
async with self._client:
|
async with self._client:
|
||||||
await self._client.valve.open()
|
await self.entity_description.on_action(self._client)
|
||||||
except GuardianError as err:
|
except GuardianError as err:
|
||||||
raise HomeAssistantError(f"Error while opening the valve: {err}") from err
|
raise HomeAssistantError(
|
||||||
|
f'Error while turning "{self.entity_id}" on: {err}'
|
||||||
|
) from err
|
||||||
|
|
||||||
self._attr_is_on = True
|
self._attr_is_on = True
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
|
@ -23,12 +23,23 @@
|
||||||
"fix_flow": {
|
"fix_flow": {
|
||||||
"step": {
|
"step": {
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"description": "Update any automations or scripts that use this service to instead use the `{alternate_service}` service with a target entity ID of `{alternate_target}`. Then, click SUBMIT below to mark this issue as resolved.",
|
"description": "Update any automations or scripts that use this service to instead use the `{alternate_service}` service with a target entity ID of `{alternate_target}`.",
|
||||||
"title": "The {deprecated_service} service is being removed"
|
"title": "The {deprecated_service} service is being removed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"title": "The {deprecated_service} service is being removed"
|
"title": "The {deprecated_service} service is being removed"
|
||||||
|
},
|
||||||
|
"replaced_old_entity": {
|
||||||
|
"fix_flow": {
|
||||||
|
"step": {
|
||||||
|
"confirm": {
|
||||||
|
"description": "This entity has been replaced by `{replacement_entity_id}`.",
|
||||||
|
"title": "The {old_entity_id} entity will be removed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "The {old_entity_id} entity will be removed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,8 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections.abc import Awaitable, Callable
|
from collections.abc import Awaitable, Callable, Iterable
|
||||||
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
|
@ -11,16 +12,72 @@ from aioguardian.errors import GuardianError
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.helpers import entity_registry
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
from .const import LOGGER
|
from .const import DOMAIN, LOGGER
|
||||||
|
|
||||||
DEFAULT_UPDATE_INTERVAL = timedelta(seconds=30)
|
DEFAULT_UPDATE_INTERVAL = timedelta(seconds=30)
|
||||||
|
|
||||||
SIGNAL_REBOOT_REQUESTED = "guardian_reboot_requested_{0}"
|
SIGNAL_REBOOT_REQUESTED = "guardian_reboot_requested_{0}"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EntityDomainReplacementStrategy:
|
||||||
|
"""Define an entity replacement."""
|
||||||
|
|
||||||
|
old_domain: str
|
||||||
|
old_unique_id: str
|
||||||
|
replacement_entity_id: str
|
||||||
|
breaks_in_ha_version: str
|
||||||
|
remove_old_entity: bool = True
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_finish_entity_domain_replacements(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: ConfigEntry,
|
||||||
|
entity_replacement_strategies: Iterable[EntityDomainReplacementStrategy],
|
||||||
|
) -> None:
|
||||||
|
"""Remove old entities and create a repairs issue with info on their replacement."""
|
||||||
|
ent_reg = entity_registry.async_get(hass)
|
||||||
|
for strategy in entity_replacement_strategies:
|
||||||
|
try:
|
||||||
|
[registry_entry] = [
|
||||||
|
registry_entry
|
||||||
|
for registry_entry in ent_reg.entities.values()
|
||||||
|
if registry_entry.config_entry_id == entry.entry_id
|
||||||
|
and registry_entry.domain == strategy.old_domain
|
||||||
|
and registry_entry.unique_id == strategy.old_unique_id
|
||||||
|
]
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
old_entity_id = registry_entry.entity_id
|
||||||
|
translation_key = "replaced_old_entity"
|
||||||
|
|
||||||
|
async_create_issue(
|
||||||
|
hass,
|
||||||
|
DOMAIN,
|
||||||
|
f"{translation_key}_{old_entity_id}",
|
||||||
|
breaks_in_ha_version=strategy.breaks_in_ha_version,
|
||||||
|
is_fixable=True,
|
||||||
|
is_persistent=True,
|
||||||
|
severity=IssueSeverity.WARNING,
|
||||||
|
translation_key=translation_key,
|
||||||
|
translation_placeholders={
|
||||||
|
"old_entity_id": old_entity_id,
|
||||||
|
"replacement_entity_id": strategy.replacement_entity_id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if strategy.remove_old_entity:
|
||||||
|
LOGGER.info('Removing old entity: "%s"', old_entity_id)
|
||||||
|
ent_reg.async_remove(old_entity_id)
|
||||||
|
|
||||||
|
|
||||||
class GuardianDataUpdateCoordinator(DataUpdateCoordinator[dict]):
|
class GuardianDataUpdateCoordinator(DataUpdateCoordinator[dict]):
|
||||||
"""Define an extended DataUpdateCoordinator with some Guardian goodies."""
|
"""Define an extended DataUpdateCoordinator with some Guardian goodies."""
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue