diff --git a/.coveragerc b/.coveragerc index fe5242327dc..94b7deea82b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -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 diff --git a/CODEOWNERS b/CODEOWNERS index 411c615e857..d72697f65c0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -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 diff --git a/homeassistant/components/avri/__init__.py b/homeassistant/components/avri/__init__.py new file mode 100644 index 00000000000..4d99b2ed0e4 --- /dev/null +++ b/homeassistant/components/avri/__init__.py @@ -0,0 +1 @@ +"""The avri component.""" diff --git a/homeassistant/components/avri/manifest.json b/homeassistant/components/avri/manifest.json new file mode 100644 index 00000000000..4ff77ccd2f6 --- /dev/null +++ b/homeassistant/components/avri/manifest.json @@ -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"] +} diff --git a/homeassistant/components/avri/sensor.py b/homeassistant/components/avri/sensor.py new file mode 100644 index 00000000000..a221147f065 --- /dev/null +++ b/homeassistant/components/avri/sensor.py @@ -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() diff --git a/requirements_all.txt b/requirements_all.txt index b7f02fec5ca..cf22961e506 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -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