Modernize/Simplify Twente Milieu (#59632)

This commit is contained in:
Franck Nijhof 2021-11-13 15:34:09 +01:00 committed by GitHub
parent aa89c670eb
commit 28a0ba4df3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 70 additions and 107 deletions

View File

@ -1140,6 +1140,7 @@ omit =
homeassistant/components/tuya/switch.py homeassistant/components/tuya/switch.py
homeassistant/components/tuya/util.py homeassistant/components/tuya/util.py
homeassistant/components/tuya/vacuum.py homeassistant/components/tuya/vacuum.py
homeassistant/components/twentemilieu/__init__.py
homeassistant/components/twentemilieu/const.py homeassistant/components/twentemilieu/const.py
homeassistant/components/twentemilieu/sensor.py homeassistant/components/twentemilieu/sensor.py
homeassistant/components/twilio_call/notify.py homeassistant/components/twilio_call/notify.py

View File

@ -133,6 +133,7 @@ homeassistant.components.tplink.*
homeassistant.components.tractive.* homeassistant.components.tractive.*
homeassistant.components.tradfri.* homeassistant.components.tradfri.*
homeassistant.components.tts.* homeassistant.components.tts.*
homeassistant.components.twentemilieu.*
homeassistant.components.upcloud.* homeassistant.components.upcloud.*
homeassistant.components.uptime.* homeassistant.components.uptime.*
homeassistant.components.uptimerobot.* homeassistant.components.uptimerobot.*

View File

@ -1,10 +1,9 @@
"""Support for Twente Milieu.""" """Support for Twente Milieu."""
from __future__ import annotations from __future__ import annotations
import asyncio from datetime import date, timedelta
from datetime import timedelta
from twentemilieu import TwenteMilieu from twentemilieu import TwenteMilieu, WasteType
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -12,17 +11,9 @@ from homeassistant.const import CONF_ID
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.typing import ConfigType
from .const import ( from .const import CONF_HOUSE_LETTER, CONF_HOUSE_NUMBER, CONF_POST_CODE, DOMAIN, LOGGER
CONF_HOUSE_LETTER,
CONF_HOUSE_NUMBER,
CONF_POST_CODE,
DATA_UPDATE,
DOMAIN,
)
SCAN_INTERVAL = timedelta(seconds=3600) SCAN_INTERVAL = timedelta(seconds=3600)
@ -32,35 +23,6 @@ SERVICE_SCHEMA = vol.Schema({vol.Optional(CONF_ID): cv.string})
PLATFORMS = ["sensor"] PLATFORMS = ["sensor"]
async def _update_twentemilieu(hass: HomeAssistant, unique_id: str | None) -> None:
"""Update Twente Milieu."""
if unique_id is not None:
twentemilieu = hass.data[DOMAIN].get(unique_id)
if twentemilieu is not None:
await twentemilieu.update()
async_dispatcher_send(hass, DATA_UPDATE, unique_id)
else:
await asyncio.wait(
[twentemilieu.update() for twentemilieu in hass.data[DOMAIN].values()]
)
for uid in hass.data[DOMAIN]:
async_dispatcher_send(hass, DATA_UPDATE, uid)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Twente Milieu components."""
async def update(call) -> None:
"""Service call to manually update the data."""
unique_id = call.data.get(CONF_ID)
await _update_twentemilieu(hass, unique_id)
hass.services.async_register(DOMAIN, SERVICE_UPDATE, update, schema=SERVICE_SCHEMA)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Twente Milieu from a config entry.""" """Set up Twente Milieu from a config entry."""
session = async_get_clientsession(hass) session = async_get_clientsession(hass)
@ -71,24 +33,30 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
session=session, session=session,
) )
unique_id = entry.data[CONF_ID] coordinator: DataUpdateCoordinator[
hass.data.setdefault(DOMAIN, {})[unique_id] = twentemilieu dict[WasteType, date | None]
] = DataUpdateCoordinator(
hass,
LOGGER,
name=DOMAIN,
update_interval=SCAN_INTERVAL,
update_method=twentemilieu.update,
)
await coordinator.async_config_entry_first_refresh()
# For backwards compat, set unique ID
if entry.unique_id is None:
hass.config_entries.async_update_entry(entry, unique_id=entry.data[CONF_ID])
hass.data.setdefault(DOMAIN, {})[entry.data[CONF_ID]] = coordinator
hass.config_entries.async_setup_platforms(entry, PLATFORMS) hass.config_entries.async_setup_platforms(entry, PLATFORMS)
async def _interval_update(now=None) -> None:
"""Update Twente Milieu data."""
await _update_twentemilieu(hass, unique_id)
async_track_time_interval(hass, _interval_update, SCAN_INTERVAL)
return True return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload Twente Milieu config entry.""" """Unload Twente Milieu config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
del hass.data[DOMAIN][entry.data[CONF_ID]] del hass.data[DOMAIN][entry.entry_id]
return unload_ok return unload_ok

View File

@ -66,7 +66,8 @@ class TwenteMilieuFlowHandler(ConfigFlow, domain=DOMAIN):
errors["base"] = "invalid_address" errors["base"] = "invalid_address"
return await self._show_setup_form(errors) return await self._show_setup_form(errors)
self._async_abort_entries_match({CONF_ID: unique_id}) await self.async_set_unique_id(str(unique_id))
self._abort_if_unique_id_configured()
return self.async_create_entry( return self.async_create_entry(
title=str(unique_id), title=str(unique_id),

View File

@ -1,8 +1,12 @@
"""Constants for the Twente Milieu integration.""" """Constants for the Twente Milieu integration."""
from datetime import timedelta
import logging
from typing import Final
DOMAIN = "twentemilieu" DOMAIN: Final = "twentemilieu"
DATA_UPDATE = "twentemilieu_update" LOGGER = logging.getLogger(__package__)
SCAN_INTERVAL = timedelta(hours=1)
CONF_POST_CODE = "post_code" CONF_POST_CODE = "post_code"
CONF_HOUSE_NUMBER = "house_number" CONF_HOUSE_NUMBER = "house_number"

View File

@ -2,21 +2,23 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from datetime import date
from twentemilieu import TwenteMilieu, TwenteMilieuConnectionError, WasteType from twentemilieu import WasteType
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ID, DEVICE_CLASS_DATE from homeassistant.const import CONF_ID, DEVICE_CLASS_DATE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from .const import DATA_UPDATE, DOMAIN from .const import DOMAIN
PARALLEL_UPDATES = 1
@dataclass @dataclass
@ -71,55 +73,38 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up Twente Milieu sensor based on a config entry.""" """Set up Twente Milieu sensor based on a config entry."""
twentemilieu = hass.data[DOMAIN][entry.data[CONF_ID]] coordinator = hass.data[DOMAIN][entry.data[CONF_ID]]
try:
await twentemilieu.update()
except TwenteMilieuConnectionError as exception:
raise PlatformNotReady from exception
async_add_entities( async_add_entities(
[ TwenteMilieuSensor(coordinator, description, entry) for description in SENSORS
TwenteMilieuSensor(twentemilieu, entry.data[CONF_ID], description)
for description in SENSORS
],
True,
) )
class TwenteMilieuSensor(SensorEntity): class TwenteMilieuSensor(CoordinatorEntity, SensorEntity):
"""Defines a Twente Milieu sensor.""" """Defines a Twente Milieu sensor."""
entity_description: TwenteMilieuSensorDescription entity_description: TwenteMilieuSensorDescription
_attr_should_poll = False coordinator: DataUpdateCoordinator[dict[WasteType, date | None]]
def __init__( def __init__(
self, self,
twentemilieu: TwenteMilieu, coordinator: DataUpdateCoordinator,
unique_id: str,
description: TwenteMilieuSensorDescription, description: TwenteMilieuSensorDescription,
entry: ConfigEntry,
) -> None: ) -> None:
"""Initialize the Twente Milieu entity.""" """Initialize the Twente Milieu entity."""
super().__init__(coordinator=coordinator)
self.entity_description = description self.entity_description = description
self._twentemilieu = twentemilieu self._attr_unique_id = f"{DOMAIN}_{entry.data[CONF_ID]}_{description.key}"
self._attr_unique_id = f"{DOMAIN}_{unique_id}_{description.key}"
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, unique_id)}, configuration_url="https://www.twentemilieu.nl",
identifiers={(DOMAIN, entry.data[CONF_ID])},
manufacturer="Twente Milieu", manufacturer="Twente Milieu",
name="Twente Milieu", name="Twente Milieu",
) )
async def async_added_to_hass(self) -> None: @property
"""Connect to dispatcher listening for entity data notifications.""" def native_value(self) -> StateType:
self.async_on_remove( """Return the state of the sensor."""
async_dispatcher_connect( if pickup := self.coordinator.data.get(self.entity_description.waste_type):
self.hass, DATA_UPDATE, self.async_schedule_update_ha_state return pickup.isoformat()
) return None
)
async def async_update(self) -> None:
"""Update Twente Milieu entity."""
pickups = await self._twentemilieu.update()
self._attr_native_value = None
if pickup := pickups.get(self.entity_description.waste_type):
self._attr_native_value = pickup.isoformat()

View File

@ -1,11 +0,0 @@
update:
name: Update
description: Update all entities with fresh data from Twente Milieu
fields:
id:
name: ID
description: Specific unique address ID to update
advanced: true
example: 1300012345
selector:
text:

View File

@ -1474,6 +1474,17 @@ no_implicit_optional = true
warn_return_any = true warn_return_any = true
warn_unreachable = true warn_unreachable = true
[mypy-homeassistant.components.twentemilieu.*]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.upcloud.*] [mypy-homeassistant.components.upcloud.*]
check_untyped_defs = true check_untyped_defs = true
disallow_incomplete_defs = true disallow_incomplete_defs = true

View File

@ -74,7 +74,10 @@ async def test_address_already_set_up(
) -> None: ) -> None:
"""Test we abort if address has already been set up.""" """Test we abort if address has already been set up."""
MockConfigEntry( MockConfigEntry(
domain=DOMAIN, data={**FIXTURE_USER_INPUT, CONF_ID: "12345"}, title="12345" domain=DOMAIN,
data={**FIXTURE_USER_INPUT, CONF_ID: "12345"},
title="12345",
unique_id="12345",
).add_to_hass(hass) ).add_to_hass(hass)
aioclient_mock.post( aioclient_mock.post(