mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Add a switch to opt-in to/opt-out of the next Ridwell pickup (#62293)
* Add buttons to opt into/out of the next Ridwell pickup * Buttons finished * Coverage * better name * Move to switch * Clean up * Coverage * Use correct exception
This commit is contained in:
parent
afdc570d70
commit
ebfe9aa384
@ -887,6 +887,7 @@ omit =
|
|||||||
homeassistant/components/rest/switch.py
|
homeassistant/components/rest/switch.py
|
||||||
homeassistant/components/ridwell/__init__.py
|
homeassistant/components/ridwell/__init__.py
|
||||||
homeassistant/components/ridwell/sensor.py
|
homeassistant/components/ridwell/sensor.py
|
||||||
|
homeassistant/components/ridwell/switch.py
|
||||||
homeassistant/components/ring/camera.py
|
homeassistant/components/ring/camera.py
|
||||||
homeassistant/components/ripple/sensor.py
|
homeassistant/components/ripple/sensor.py
|
||||||
homeassistant/components/rocketchat/notify.py
|
homeassistant/components/rocketchat/notify.py
|
||||||
|
@ -12,14 +12,25 @@ from homeassistant.config_entries import ConfigEntry
|
|||||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||||
from homeassistant.helpers import aiohttp_client
|
from homeassistant.helpers import aiohttp_client, entity_registry as er
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.entity import EntityDescription
|
||||||
|
from homeassistant.helpers.update_coordinator import (
|
||||||
|
CoordinatorEntity,
|
||||||
|
DataUpdateCoordinator,
|
||||||
|
UpdateFailed,
|
||||||
|
)
|
||||||
|
|
||||||
from .const import DATA_ACCOUNT, DATA_COORDINATOR, DOMAIN, LOGGER
|
from .const import (
|
||||||
|
DATA_ACCOUNT,
|
||||||
|
DATA_COORDINATOR,
|
||||||
|
DOMAIN,
|
||||||
|
LOGGER,
|
||||||
|
SENSOR_TYPE_NEXT_PICKUP,
|
||||||
|
)
|
||||||
|
|
||||||
DEFAULT_UPDATE_INTERVAL = timedelta(hours=1)
|
DEFAULT_UPDATE_INTERVAL = timedelta(hours=1)
|
||||||
|
|
||||||
PLATFORMS: list[Platform] = [Platform.SENSOR]
|
PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.SWITCH]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
@ -82,3 +93,43 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
hass.data[DOMAIN].pop(entry.entry_id)
|
||||||
|
|
||||||
return unload_ok
|
return unload_ok
|
||||||
|
|
||||||
|
|
||||||
|
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
"""Migrate an old config entry."""
|
||||||
|
version = entry.version
|
||||||
|
|
||||||
|
LOGGER.debug("Migrating from version %s", version)
|
||||||
|
|
||||||
|
# 1 -> 2: Update unique ID of existing, single sensor entity to be consistent with
|
||||||
|
# common format for platforms going forward:
|
||||||
|
if version == 1:
|
||||||
|
version = entry.version = 2
|
||||||
|
|
||||||
|
ent_reg = er.async_get(hass)
|
||||||
|
[entity_entry] = [
|
||||||
|
e for e in ent_reg.entities.values() if e.config_entry_id == entry.entry_id
|
||||||
|
]
|
||||||
|
new_unique_id = f"{entity_entry.unique_id}_{SENSOR_TYPE_NEXT_PICKUP}"
|
||||||
|
ent_reg.async_update_entity(entity_entry.entity_id, new_unique_id=new_unique_id)
|
||||||
|
|
||||||
|
LOGGER.info("Migration to version %s successful", version)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class RidwellEntity(CoordinatorEntity):
|
||||||
|
"""Define a base Ridwell entity."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
coordinator: DataUpdateCoordinator,
|
||||||
|
account: RidwellAccount,
|
||||||
|
description: EntityDescription,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the sensor."""
|
||||||
|
super().__init__(coordinator)
|
||||||
|
|
||||||
|
self._account = account
|
||||||
|
self._attr_unique_id = f"{account.account_id}_{description.key}"
|
||||||
|
self.entity_description = description
|
||||||
|
@ -32,7 +32,7 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
|
|||||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
"""Handle a config flow for WattTime."""
|
"""Handle a config flow for WattTime."""
|
||||||
|
|
||||||
VERSION = 1
|
VERSION = 2
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
|
@ -7,3 +7,5 @@ LOGGER = logging.getLogger(__package__)
|
|||||||
|
|
||||||
DATA_ACCOUNT = "account"
|
DATA_ACCOUNT = "account"
|
||||||
DATA_COORDINATOR = "coordinator"
|
DATA_COORDINATOR = "coordinator"
|
||||||
|
|
||||||
|
SENSOR_TYPE_NEXT_PICKUP = "next_pickup"
|
||||||
|
@ -7,49 +7,60 @@ from typing import Any
|
|||||||
|
|
||||||
from aioridwell.model import RidwellAccount, RidwellPickupEvent
|
from aioridwell.model import RidwellAccount, RidwellPickupEvent
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
from homeassistant.components.sensor import (
|
||||||
|
SensorDeviceClass,
|
||||||
|
SensorEntity,
|
||||||
|
SensorEntityDescription,
|
||||||
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import StateType
|
from homeassistant.helpers.typing import StateType
|
||||||
from homeassistant.helpers.update_coordinator import (
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||||
CoordinatorEntity,
|
|
||||||
DataUpdateCoordinator,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .const import DATA_ACCOUNT, DATA_COORDINATOR, DOMAIN
|
from . import RidwellEntity
|
||||||
|
from .const import DATA_ACCOUNT, DATA_COORDINATOR, DOMAIN, SENSOR_TYPE_NEXT_PICKUP
|
||||||
|
|
||||||
ATTR_CATEGORY = "category"
|
ATTR_CATEGORY = "category"
|
||||||
ATTR_PICKUP_STATE = "pickup_state"
|
ATTR_PICKUP_STATE = "pickup_state"
|
||||||
ATTR_PICKUP_TYPES = "pickup_types"
|
ATTR_PICKUP_TYPES = "pickup_types"
|
||||||
ATTR_QUANTITY = "quantity"
|
ATTR_QUANTITY = "quantity"
|
||||||
|
|
||||||
|
SENSOR_DESCRIPTION = SensorEntityDescription(
|
||||||
|
key=SENSOR_TYPE_NEXT_PICKUP,
|
||||||
|
name="Ridwell Pickup",
|
||||||
|
device_class=SensorDeviceClass.DATE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up WattTime sensors based on a config entry."""
|
"""Set up Ridwell sensors based on a config entry."""
|
||||||
accounts = hass.data[DOMAIN][entry.entry_id][DATA_ACCOUNT]
|
accounts = hass.data[DOMAIN][entry.entry_id][DATA_ACCOUNT]
|
||||||
coordinator = hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR]
|
coordinator = hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR]
|
||||||
|
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
[RidwellSensor(coordinator, account) for account in accounts.values()]
|
[
|
||||||
|
RidwellSensor(coordinator, account, SENSOR_DESCRIPTION)
|
||||||
|
for account in accounts.values()
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RidwellSensor(CoordinatorEntity, SensorEntity):
|
class RidwellSensor(RidwellEntity, SensorEntity):
|
||||||
"""Define a Ridwell pickup sensor."""
|
"""Define a Ridwell pickup sensor."""
|
||||||
|
|
||||||
_attr_device_class = SensorDeviceClass.DATE
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, coordinator: DataUpdateCoordinator, account: RidwellAccount
|
self,
|
||||||
|
coordinator: DataUpdateCoordinator,
|
||||||
|
account: RidwellAccount,
|
||||||
|
description: SensorEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the sensor."""
|
"""Initialize."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator, account, description)
|
||||||
|
|
||||||
self._account = account
|
self._attr_name = f"{description.name} ({account.address['street1']})"
|
||||||
self._attr_name = f"Ridwell Pickup ({account.address['street1']})"
|
|
||||||
self._attr_unique_id = account.account_id
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def extra_state_attributes(self) -> Mapping[str, Any]:
|
def extra_state_attributes(self) -> Mapping[str, Any]:
|
||||||
|
71
homeassistant/components/ridwell/switch.py
Normal file
71
homeassistant/components/ridwell/switch.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
"""Support for Ridwell buttons."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from aioridwell.errors import RidwellError
|
||||||
|
from aioridwell.model import EventState, RidwellPickupEvent
|
||||||
|
|
||||||
|
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
|
from . import RidwellEntity
|
||||||
|
from .const import DATA_ACCOUNT, DATA_COORDINATOR, DOMAIN
|
||||||
|
|
||||||
|
SWITCH_TYPE_OPT_IN = "opt_in"
|
||||||
|
|
||||||
|
SWITCH_DESCRIPTION = SwitchEntityDescription(
|
||||||
|
key=SWITCH_TYPE_OPT_IN,
|
||||||
|
name="Opt-In to Next Pickup",
|
||||||
|
icon="mdi:calendar-check",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
|
) -> None:
|
||||||
|
"""Set up Ridwell sensors based on a config entry."""
|
||||||
|
accounts = hass.data[DOMAIN][entry.entry_id][DATA_ACCOUNT]
|
||||||
|
coordinator = hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR]
|
||||||
|
|
||||||
|
async_add_entities(
|
||||||
|
[
|
||||||
|
RidwellSwitch(coordinator, account, SWITCH_DESCRIPTION)
|
||||||
|
for account in accounts.values()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RidwellSwitch(RidwellEntity, SwitchEntity):
|
||||||
|
"""Define a Ridwell button."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self) -> bool:
|
||||||
|
"""Return True if entity is on."""
|
||||||
|
event: RidwellPickupEvent = self.coordinator.data[self._account.account_id]
|
||||||
|
return event.state == EventState.SCHEDULED
|
||||||
|
|
||||||
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
|
"""Turn the switch off."""
|
||||||
|
event: RidwellPickupEvent = self.coordinator.data[self._account.account_id]
|
||||||
|
|
||||||
|
try:
|
||||||
|
await event.async_opt_out()
|
||||||
|
except RidwellError as err:
|
||||||
|
raise HomeAssistantError(f"Error while opting out: {err}") from err
|
||||||
|
|
||||||
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
|
"""Turn the switch on."""
|
||||||
|
event: RidwellPickupEvent = self.coordinator.data[self._account.account_id]
|
||||||
|
|
||||||
|
try:
|
||||||
|
await event.async_opt_in()
|
||||||
|
except RidwellError as err:
|
||||||
|
raise HomeAssistantError(f"Error while opting in: {err}") from err
|
||||||
|
|
||||||
|
await self.coordinator.async_request_refresh()
|
Loading…
x
Reference in New Issue
Block a user