Add Avri waste collection sensor (#31134)

* Add Avri waste collection sensor

* Apply black formatting

* Update manifest

* Add requirements

* Add sensor to coverage

* Update import order

* Bump dependency to include todays pickup

* Bump avri version in requirements_all.txt

* Code review comments

* Reduce scan interval to 4 hours

This makes sure that no matter what happens, in
the morning the correct dates have been pulled
without the old ones lingering for too long.

* Better logging

* Made scan interval a timedelta

* Fix import order

* Update homeassistant/components/avri/sensor.py

Co-Authored-By: springstan <46536646+springstan@users.noreply.github.com>

* Update homeassistant/components/avri/sensor.py

Co-Authored-By: springstan <46536646+springstan@users.noreply.github.com>

* Use filter instead of break statement

* Use positive int for house number extension

* Switch voluptuous types for house number and house number extension

* Update homeassistant/components/avri/sensor.py

Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>

* Implement `available`

* Bump avri api

* Code review comments

* Replace `postcode` with `zip_code`

* Update logic for `available`

* Remove variable for delimiter

Co-authored-by: springstan <46536646+springstan@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
Tim van Cann 2020-02-28 07:12:01 +01:00 committed by GitHub
parent 02c170b961
commit 1d962aeb65
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 130 additions and 0 deletions

View file

@ -65,6 +65,7 @@ omit =
homeassistant/components/automatic/device_tracker.py
homeassistant/components/avea/light.py
homeassistant/components/avion/light.py
homeassistant/components/avri/sensor.py
homeassistant/components/azure_event_hub/*
homeassistant/components/azure_service_bus/*
homeassistant/components/baidu/tts.py

View file

@ -41,6 +41,7 @@ homeassistant/components/auth/* @home-assistant/core
homeassistant/components/automatic/* @armills
homeassistant/components/automation/* @home-assistant/core
homeassistant/components/avea/* @pattyland
homeassistant/components/avri/* @timvancann
homeassistant/components/awair/* @danielsjf
homeassistant/components/aws/* @awarecan @robbiet480
homeassistant/components/axis/* @kane610

View file

@ -0,0 +1 @@
"""The avri component."""

View file

@ -0,0 +1,8 @@
{
"domain": "avri",
"name": "Avri",
"documentation": "https://www.home-assistant.io/integrations/avri",
"requirements": ["avri-api==0.1.6"],
"dependencies": [],
"codeowners": ["@timvancann"]
}

View file

@ -0,0 +1,116 @@
"""Support for Avri waste curbside collection pickup."""
from datetime import timedelta
import logging
from avri.api import Avri, AvriException
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import CONF_NAME
from homeassistant.exceptions import PlatformNotReady
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__)
CONF_COUNTRY_CODE = "country_code"
CONF_ZIP_CODE = "zip_code"
CONF_HOUSE_NUMBER = "house_number"
CONF_HOUSE_NUMBER_EXTENSION = "house_number_extension"
DEFAULT_NAME = "avri"
ICON = "mdi:trash-can-outline"
SCAN_INTERVAL = timedelta(hours=4)
DEFAULT_COUNTRY_CODE = "NL"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_ZIP_CODE): cv.string,
vol.Required(CONF_HOUSE_NUMBER): cv.positive_int,
vol.Optional(CONF_HOUSE_NUMBER_EXTENSION): cv.string,
vol.Optional(CONF_COUNTRY_CODE, default=DEFAULT_COUNTRY_CODE): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
}
)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Avri Waste platform."""
client = Avri(
postal_code=config[CONF_ZIP_CODE],
house_nr=config[CONF_HOUSE_NUMBER],
house_nr_extension=config.get(CONF_HOUSE_NUMBER_EXTENSION),
country_code=config[CONF_COUNTRY_CODE],
)
try:
each_upcoming = client.upcoming_of_each()
except AvriException as ex:
raise PlatformNotReady from ex
else:
entities = [
AvriWasteUpcoming(config[CONF_NAME], client, upcoming.name)
for upcoming in each_upcoming
]
add_entities(entities, True)
class AvriWasteUpcoming(Entity):
"""Avri Waste Sensor."""
def __init__(self, name: str, client: Avri, waste_type: str):
"""Initialize the sensor."""
self._waste_type = waste_type
self._name = f"{name}_{self._waste_type}"
self._state = None
self._client = client
self._state_available = False
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def unique_id(self) -> str:
"""Return a unique ID."""
return (
f"{self._waste_type}"
f"-{self._client.country_code}"
f"-{self._client.postal_code}"
f"-{self._client.house_nr}"
f"-{self._client.house_nr_extension}"
)
@property
def state(self):
"""Return the state of the sensor."""
return self._state
@property
def available(self):
"""Return True if entity is available."""
return self._state_available
@property
def icon(self):
"""Icon to use in the frontend."""
return ICON
def update(self):
"""Update device state."""
try:
pickup_events = self._client.upcoming_of_each()
except AvriException as ex:
_LOGGER.error(
"There was an error retrieving upcoming garbage pickups: %s", ex
)
self._state_available = False
self._state = None
else:
self._state_available = True
matched_events = list(
filter(lambda event: event.name == self._waste_type, pickup_events)
)
if not matched_events:
self._state = None
else:
self._state = matched_events[0].day.date()

View file

@ -278,6 +278,9 @@ avea==1.4
# homeassistant.components.avion
# avion==0.10
# homeassistant.components.avri
avri-api==0.1.6
# homeassistant.components.axis
axis==25