This repository has been archived by the owner on Aug 25, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f81ce27
commit 29736b0
Showing
1 changed file
with
225 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
#!/usr/bin/env python3 | ||
""" | ||
Sensor component for Afvalinfo | ||
Author: Johnny Visser | ||
ToDo: Add huisnummer toevoeging | ||
""" | ||
|
||
import voluptuous as vol | ||
from datetime import datetime, date, timedelta | ||
from dateutil.relativedelta import relativedelta | ||
import urllib.error | ||
from babel import Locale | ||
from babel.dates import format_date, format_datetime, format_time | ||
import re | ||
|
||
from .const.const import ( | ||
MIN_TIME_BETWEEN_UPDATES, | ||
_LOGGER, | ||
CONF_CITY, | ||
CONF_LOCATION, | ||
CONF_POSTCODE, | ||
CONF_STREET_NUMBER, | ||
CONF_STREET_NUMBER_SUFFIX, | ||
CONF_DATE_FORMAT, | ||
CONF_TIMESPAN_IN_DAYS, | ||
CONF_LOCALE, | ||
CONF_ID, | ||
SENSOR_PREFIX, | ||
ATTR_LAST_UPDATE, | ||
ATTR_HIDDEN, | ||
ATTR_DAYS_UNTIL_COLLECTION_DATE, | ||
ATTR_IS_COLLECTION_DATE_TODAY, | ||
ATTR_YEAR_MONTH_DAY_DATE, | ||
SENSOR_TYPES, | ||
) | ||
|
||
from .location.trashapi import TrashApiAfval | ||
from .sensortomorrow import AfvalInfoTomorrowSensor | ||
from .sensortoday import AfvalInfoTodaySensor | ||
|
||
from homeassistant.components.sensor import PLATFORM_SCHEMA | ||
import homeassistant.helpers.config_validation as cv | ||
from homeassistant.const import CONF_RESOURCES | ||
from homeassistant.util import Throttle | ||
from homeassistant.helpers.entity import Entity | ||
|
||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( | ||
{ | ||
vol.Required(CONF_RESOURCES, default=[]): vol.All( | ||
cv.ensure_list, [vol.In(SENSOR_TYPES)] | ||
), | ||
vol.Optional(CONF_CITY, default=""): cv.string, | ||
vol.Optional(CONF_LOCATION, default="sliedrecht"): cv.string, | ||
vol.Required(CONF_POSTCODE, default="3361AB"): cv.string, | ||
vol.Required(CONF_STREET_NUMBER, default="1"): cv.string, | ||
vol.Optional(CONF_STREET_NUMBER_SUFFIX, default=""): cv.string, | ||
vol.Optional(CONF_DATE_FORMAT, default = "%d-%m-%Y"): cv.string, | ||
vol.Optional(CONF_TIMESPAN_IN_DAYS, default="365"): cv.string, | ||
vol.Optional(CONF_LOCALE, default = "en"): cv.string, | ||
vol.Optional(CONF_ID, default = ""): cv.string, | ||
} | ||
) | ||
|
||
|
||
def setup_platform(hass, config, add_entities, discovery_info=None): | ||
_LOGGER.debug("Setup Afvalinfo sensor") | ||
|
||
location = config.get(CONF_CITY).lower().strip() | ||
if len(location) == 0: | ||
location = config.get(CONF_LOCATION).lower().strip() | ||
postcode = config.get(CONF_POSTCODE).strip() | ||
street_number = config.get(CONF_STREET_NUMBER) | ||
street_number_suffix = config.get(CONF_STREET_NUMBER_SUFFIX) | ||
date_format = config.get(CONF_DATE_FORMAT).strip() | ||
timespan_in_days = config.get(CONF_TIMESPAN_IN_DAYS) | ||
locale = config.get(CONF_LOCALE) | ||
id_name = config.get(CONF_ID) | ||
|
||
try: | ||
resourcesMinusTodayAndTomorrow = config[CONF_RESOURCES].copy() | ||
if "trash_type_today" in resourcesMinusTodayAndTomorrow: | ||
resourcesMinusTodayAndTomorrow.remove("trash_type_today") | ||
if "trash_type_tomorrow" in resourcesMinusTodayAndTomorrow: | ||
resourcesMinusTodayAndTomorrow.remove("trash_type_tomorrow") | ||
|
||
data = AfvalinfoData(location, postcode, street_number, street_number_suffix, resourcesMinusTodayAndTomorrow) | ||
except urllib.error.HTTPError as error: | ||
_LOGGER.error(error.reason) | ||
return False | ||
|
||
entities = [] | ||
|
||
for resource in config[CONF_RESOURCES]: | ||
sensor_type = resource.lower() | ||
|
||
#if sensor_type not in SENSOR_TYPES: | ||
if sensor_type.title().lower() != "trash_type_today" and sensor_type.title().lower() != "trash_type_tomorrow": | ||
entities.append(AfvalinfoSensor(data, sensor_type, date_format, timespan_in_days, locale, id_name)) | ||
|
||
#Add sensor -trash_type_today | ||
if sensor_type.title().lower() == "trash_type_today": | ||
today = AfvalInfoTodaySensor(data, sensor_type, entities, id_name) | ||
entities.append(today) | ||
#Add sensor -trash_type_tomorrow | ||
if sensor_type.title().lower() == "trash_type_tomorrow": | ||
tomorrow = AfvalInfoTomorrowSensor(data, sensor_type, entities, id_name) | ||
entities.append(tomorrow) | ||
|
||
add_entities(entities) | ||
|
||
|
||
class AfvalinfoData(object): | ||
def __init__(self, location, postcode, street_number, street_number_suffix, resources): | ||
self.data = None | ||
self.location = location | ||
self.postcode = postcode | ||
self.street_number = street_number | ||
self.street_number_suffix = street_number_suffix | ||
self.resources = resources | ||
|
||
@Throttle(MIN_TIME_BETWEEN_UPDATES) | ||
def update(self): | ||
_LOGGER.debug("Updating Waste collection dates") | ||
trashapi = ["aa en hunze", "aalsmeer", "aalten", "achtkarspelen", "alblasserdam", "albrandswaard", "alkmaar", "almelo", "almere", "alphen aan den rijn", "alphen-chaam", "altena", "ameland", "amersfoort", "amstelveen", "apeldoorn", "arnhem", "assen", "asten", "baarle-nassau", "baarn", "barendrecht", "barneveld", "beek", "beekdaelen", "beemster", "beesel", "berg en dal", "bergeijk", "bergen op zoom", "bergen", "berkelland", "bernheze", "best", "beuningen", "beverwijk", "bladel", "blaricum", "bloemendaal", "bodegraven-reeuwijk", "boekel", "borger-odoorn", "borne", "borsele", "boxmeer", "boxtel", "breda", "brielle", "bronckhorst", "brummen", "brunssum", "bunnik", "bunschoten", "buren", "capelle aan den ijssel", "castricum", "coevorden", "cranendonck", "cuijk", "culemborg", "dalfsen", "dantumadeel f", "de bilt", "de friese meren", "de ronde venen", "de wolden", "delft", "den haag", "den helder", "deurne", "deventer", "diemen", "dinkelland", "doesburg", "doetinchem", "dongen", "dordrecht", "drechterland", "drimmelen", "dronten", "druten", "duiven", "echt-susteren", "ede", "eemnes", "eemsdelta", "eersel", "eijsden-margraten", "eindhoven", "elburg", "emmen", "enkhuizen", "enschede", "epe", "ermelo", "etten-leur", "geertruidenberg", "geldrop-mierlo", "gemert-bakel", "gilze en rijen", "goeree-overflakkee", "goes", "goirle", "gooise meren", "gorinchem", "gouda", "grave", "groningen", "gulpen-wittem", "haaksbergen", "haarlem", "haarlemmermeer", "halderberge", "hardenberg", "harderwijk", "hardinxveld-giessendam", "harlingen", "hattem", "heemskerk", "heemstede", "heerde", "heerenveen", "heerhugowaard", "heerlen", "heeze-leende", "heiloo", "hellendoorn", "hellevoetsluis", "helmond", "hendrik-ido-ambacht", "hengelo", "het hogeland", "heumen", "heusden", "hillegom", "hilvarenbeek", "hilversum", "hoeksche waard", "hof van twente", "hollands kroon", "hoogeveen", "hoorn", "horst aan de maas", "houten", "huizen", "hulst", "ijsselstein", "kaag en braassem", "kampen", "kapelle", "katwijk", "kerkrade", "koggenland", "krimpen aan den ijssel", "krimpenerwaard", "laarbeek", "landerd", "landgraaf", "langedijk", "lansingerland", "laren", "leeuwarden", "leiden", "leiderdorp", "leidschendam-voorburg", "lelystad", "leudal", "leusden", "lingewaard", "lisse", "lochem", "loon op zand", "lopik", "losser", "maasdriel", "maasgouw", "maastricht", "medemblik", "meerssen", "meierijstad", "meppel", "middelburg", "midden-delfland", "midden-drenthe", "midden-groningen", "mill en sint hubert", "moerdijk", "molenlanden", "montferland", "montfoort", "mook en middelaar", "neder-betuwe", "nieuwegein", "nieuwkoop", "nijkerk", "nijmegen", "nissewaard", "noardeast fryslan", "noord-beveland", "noordenveld", "noordoostpolder", "noordwijk", "nuenen", "nunspeet", "oirschot", "oisterwijk", "oldambt", "oldebroek", "oldenzaal", "olst-wijhe", "ommen", "oost gelre", "oosterhout", "ooststellingwerf", "opmeer", "opsterland", "oss", "oude ijsselstreek", "oude pekela", "oudewater", "overbetuwe", "papendrecht", "peel en maas", "pijnacker-nootdorp", "purmerend", "putten", "raalte", "reimerswaal", "renkum", "renswoude", "reusel-de mierden", "rheden", "rhenen", "ridderkerk", "rijssen-holten", "rijswijk", "roerdalen", "roermond", "roosendaal", "rotterdam rozenburg", "rotterdam", "rucphen", "s-hertogenbosch", "schagen", "scherpenzeel", "schiedam", "schouwen-duiveland", "simpelveld", "sint anthonis", "sint-michielsgestel", "sittard-geleen", "sliedrecht", "sluis", "smallingerland", "soest", "someren", "son en breugel", "stadskanaal", "staphorst", "stede broec", "steenwijkerland", "stein", "stichtse vecht", "terneuzen", "terschelling", "teylingen", "tholen", "tiel", "tietjerksteradeel", "tilburg", "tubbergen", "twenterand", "tynaarlo", "uden", "uitgeest", "urk", "utrecht", "utrechtse heuvelrug", "vaals", "valkenburg aan de geul", "valkenswaard", "veendam", "veenendaal", "veere", "veldhoven", "velsen", "venlo", "venray", "vijfheerenlanden", "vlaardingen", "vlissingen", "voerendaal", "voorschoten", "voorst", "vught", "waadhoeke", "waalre", "waalwijk", "waddinxveen", "wageningen", "wassenaar", "waterland", "weesp", "west betuwe", "west maas en waal", "westerkwartier", "westerveld", "westervoort", "westerwolde", "westland", "weststellingwerf", "westvoorne", "wierden", "wijchen", "wijdemeren", "wijk bij duurstede", "winterswijk", "woensdrecht", "woerden", "wormerland", "woudenberg", "zaanstad", "zaltbommel", "zandvoort", "zeewolde", "zeist", "zevenaar", "zoetermeer", "zoeterwoude", "zuidplas", "zuidwest-friesland", "zundert", "zutphen", "zwartewaterland", "zwijndrecht", "zwolle"] | ||
if self.location in trashapi: | ||
self.data = TrashApiAfval().get_data( | ||
self.location, self.postcode, self.street_number, self.street_number_suffix, self.resources | ||
) | ||
|
||
class AfvalinfoSensor(Entity): | ||
def __init__(self, data, sensor_type, date_format, timespan_in_days, locale, id_name): | ||
self.data = data | ||
self.type = sensor_type | ||
self.date_format = date_format | ||
self.timespan_in_days = timespan_in_days | ||
self.locale = locale | ||
self._name = SENSOR_PREFIX + (id_name + " " if len(id_name) > 0 else "") + SENSOR_TYPES[sensor_type][0] | ||
self._icon = SENSOR_TYPES[sensor_type][1] | ||
self._hidden = False | ||
self._state = None | ||
self._last_update = None | ||
self._days_until_collection_date = None | ||
self._is_collection_date_today = False | ||
self._year_month_day_date = None | ||
|
||
@property | ||
def name(self): | ||
return self._name | ||
|
||
@property | ||
def icon(self): | ||
return self._icon | ||
|
||
@property | ||
def state(self): | ||
return self._state | ||
|
||
@property | ||
def device_state_attributes(self): | ||
return {ATTR_YEAR_MONTH_DAY_DATE: self._year_month_day_date, ATTR_LAST_UPDATE: self._last_update, ATTR_HIDDEN: self._hidden, ATTR_DAYS_UNTIL_COLLECTION_DATE: self._days_until_collection_date, ATTR_IS_COLLECTION_DATE_TODAY: self._is_collection_date_today} | ||
|
||
@Throttle(MIN_TIME_BETWEEN_UPDATES) | ||
def update(self): | ||
self.data.update() | ||
waste_data = self.data.data | ||
|
||
try: | ||
if waste_data: | ||
if self.type in waste_data: | ||
collection_date = datetime.strptime( | ||
waste_data[self.type], "%Y-%m-%d" | ||
).date() | ||
|
||
# Date in date format "%Y-%m-%d" | ||
self._year_month_day_date = str(collection_date) | ||
|
||
if collection_date: | ||
# Set the values of the sensor | ||
self._last_update = datetime.today().strftime("%d-%m-%Y %H:%M") | ||
|
||
# Is the collection date today? | ||
self._is_collection_date_today = date.today() == collection_date | ||
|
||
# Days until collection date | ||
delta = collection_date - date.today() | ||
self._days_until_collection_date = delta.days | ||
|
||
# Only show the value if the date is lesser than or equal to (today + timespan_in_days) | ||
if collection_date <= date.today() + relativedelta(days=int(self.timespan_in_days)): | ||
#if the date does not contain a named day or month, return the date as normal | ||
if (self.date_format.find('a') == -1 and self.date_format.find('A') == -1 | ||
and self.date_format.find('b') == -1 and self.date_format.find('B') == -1): | ||
self._state = collection_date.strftime(self.date_format) | ||
#else convert the named values to the locale names | ||
else: | ||
edited_date_format = self.date_format.replace('%a', 'EEE') | ||
edited_date_format = edited_date_format.replace('%A', 'EEEE') | ||
edited_date_format = edited_date_format.replace('%b', 'MMM') | ||
edited_date_format = edited_date_format.replace('%B', 'MMMM') | ||
|
||
#half babel, half date string... something like EEEE 04-MMMM-2020 | ||
half_babel_half_date = collection_date.strftime(edited_date_format) | ||
|
||
#replace the digits with qquoted digits 01 --> '01' | ||
half_babel_half_date = re.sub(r"(\d+)", r"'\1'", half_babel_half_date) | ||
#transform the EEE, EEEE etc... to a real locale date, with babel | ||
locale_date = format_date(collection_date, half_babel_half_date, locale=self.locale) | ||
|
||
self._state = locale_date | ||
else: | ||
self._hidden = True | ||
else: | ||
raise ValueError() | ||
else: | ||
raise ValueError() | ||
else: | ||
raise ValueError() | ||
except ValueError: | ||
self._state = None | ||
self._hidden = True | ||
self._days_until_collection_date = None | ||
self._year_month_day_date = None | ||
self._is_collection_date_today = False | ||
self._last_update = datetime.today().strftime("%d-%m-%Y %H:%M") |