diff --git a/custom_components/afvalwijzer/manifest.json b/custom_components/afvalwijzer/manifest.json index ce7eefe..0feabc5 100644 --- a/custom_components/afvalwijzer/manifest.json +++ b/custom_components/afvalwijzer/manifest.json @@ -3,7 +3,7 @@ "name": "Afvalwijzer", "documentation": "https://github.com/xirixiz/Home-Assistant-Sensor-Afvalwijzer", "requirements": [ - "bs4==0.0.1" + "afvaldienst==0.3.0" ], "dependencies": [], "codeowners": [ diff --git a/custom_components/afvalwijzer/sensor.py b/custom_components/afvalwijzer/sensor.py index c92e0e1..0354cb3 100644 --- a/custom_components/afvalwijzer/sensor.py +++ b/custom_components/afvalwijzer/sensor.py @@ -1,34 +1,16 @@ """ @ Authors : Bram van Dartel -@ Date : 10/12/2019 +@ Date : 28/01/2020 @ Description : Afvalwijzer Json/Scraper Sensor - It queries mijnafvalwijzer.nl or afvalstoffendienstkalender.nl. -sensor: - - platform: afvalwijzer - url: mijnafvalwijzer.nl (optional, default mijnafvalwijzer.nl) - postcode: 1111AA - huisnummer: 1 - toevoeging: A (optional) - label_geen: 'Bla' (optional, default Geen) - -23-02-2019 - Back to JSON release instead of scraper -23-02-2019 - Move scraper url, cleanup, and some minor doc fixes -24-02-2019 - Scraper debug log url fix -25-02-2019 - Update to new custom_sensor location -07-03-2019 - Make compatible for afvalstoffendienstkalender.nl as well -25-03-2019 - Remove Python 3.7.x f-strings, back to old format for beteer compatibility -26-04-2019 - Make compatible with hass 0.92 -22-09-2019 - Add bs4 as a requirement in manifest.json (for hassio) -10-12-2019 - Fix whitespace bug +28-01-2020 - Rebuild from scratch! Use Python library! Breaking changes! """ -VERSION = '3.0.12' +VERSION = '4.0.0' -import logging +from Afvaldienst import Afvaldienst from datetime import date, datetime, timedelta - -import bs4 -import requests +import logging import homeassistant.helpers.config_validation as cv import voluptuous as vol @@ -44,78 +26,59 @@ ICON = 'mdi:delete-empty' SENSOR_PREFIX = 'trash_' -CONST_URL = 'url' -CONST_POSTCODE = 'postcode' -CONST_HUISNUMMER = 'huisnummer' -CONST_TOEVOEGING = 'toevoeging' -CONST_LABEL_NONE = 'label_geen' +CONST_PROVIDER = 'provider' +CONST_ZIPCODE = 'zipcode' +CONST_HOUSENUMBER = 'housenumber' +CONST_SUFFIX = 'suffix' +CONST_LABEL = 'default_label' -SCAN_INTERVAL = timedelta(seconds=30) +SCAN_INTERVAL = timedelta(seconds=5) MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=3600) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONST_URL, default="mijnafvalwijzer.nl"): cv.string, - vol.Required(CONST_POSTCODE): cv.string, - vol.Required(CONST_HUISNUMMER): cv.string, - vol.Optional(CONST_TOEVOEGING, default=""): cv.string, - vol.Optional(CONST_LABEL_NONE, default="Geen"): cv.string, + vol.Optional(CONST_PROVIDER, default="mijnafvalwijzer"): cv.string, + vol.Required(CONST_ZIPCODE): cv.string, + vol.Required(CONST_HOUSENUMBER): cv.string, + vol.Optional(CONST_SUFFIX, default=""): cv.string, + vol.Optional(CONST_LABEL, default="Geen"): cv.string, }) - def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the sensor platform.""" - # Setup JSON request (add sensor/devices) - url = config.get(CONST_URL) - postcode = config.get(CONST_POSTCODE) - huisnummer = config.get(CONST_HUISNUMMER) - toevoeging = config.get(CONST_TOEVOEGING) - - if None in (postcode, huisnummer): - logger.error("Postcode or huisnummer not set!") - - url = ("https://json.{0}/?method=postcodecheck&postcode={1}&street=&huisnummer={2}&toevoeging={3}&platform=phone&langs=nl&").format(url,postcode,huisnummer,toevoeging) - logger.debug("Json request url: {}".format(url)) - response = requests.get(url) - - if response.status_code != requests.codes.ok: - logger.exception("Error doing API request") - else: - logger.debug("API request ok {}".format(response.status_code)) - - json_obj = response.json() - json_data = (json_obj['data']['ophaaldagen']['data'] + json_obj['data']['ophaaldagenNext']['data']) - - # Get unique trash shortname(s) - uniqueTrashShortNames = [] - allTrashNames = ['firstdate', 'firstwastetype', 'today', 'tomorrow', 'next'] - uniqueTrashShortNames.extend(allTrashNames) - sensors = [] + provider = config.get(CONST_PROVIDER) + zipcode = config.get(CONST_ZIPCODE) + housenumber = config.get(CONST_HOUSENUMBER) + suffix = config.get(CONST_SUFFIX) - for item in json_data: - element = item["nameType"] - if element not in uniqueTrashShortNames: - uniqueTrashShortNames.append(element) + afvaldienst = Afvaldienst(provider, zipcode, housenumber, suffix) - logger.debug("uniqueTrashShortNames succesfully added: {}".format(uniqueTrashShortNames)) + # Get trash types to create sensors from + trashTypesDefault = afvaldienst.trash_type_list + trashTypesAdditional = afvaldienst.trash_schedule_today_json + afvaldienst.trash_schedule_tomorrow_json + afvaldienst.trash_schedule_next_days_json + for item in trashTypesAdditional: + trashTypesDefault.append(item['key']) - data = (TrashCollectionSchedule(url, allTrashNames, config)) + fetch_trash_data = (TrashSchedule(afvaldienst, config)) - for name in uniqueTrashShortNames: - sensors.append(TrashCollectionSensor(name, data, config)) + # Setup sensors + sensors = [] + for name in trashTypesDefault: + sensors.append(TrashSensor(hass, name, fetch_trash_data, afvaldienst, config)) add_devices(sensors) - logger.debug("Object succesfully added as sensor(s): {}".format(sensors)) - -class TrashCollectionSensor(Entity): +class TrashSensor(Entity): """Representation of a Sensor.""" - def __init__(self, name, data, config): + def __init__(self, hass, name, fetch_trash_data, afvaldienst, config): """Initialize the sensor.""" + self._hass = hass self._name = name - self.data = data + self.fetch_trash_data = fetch_trash_data + self._afvaldienst = afvaldienst + self.attributes = {} self.config = config - self._state = self.config.get(CONST_LABEL_NONE) + self._state = self.config.get(CONST_LABEL) @property def name(self): @@ -132,121 +95,38 @@ def icon(self): """Set the default sensor icon.""" return ICON + @property + def device_state_attributes(self): + """Return the state attributes of the sensor.""" + return self.attributes + def update(self): """Fetch new state data for the sensor.""" - self.data.update() - self._state = self.config.get(CONST_LABEL_NONE) + self.fetch_trash_data.update() + self._state = self.config.get(CONST_LABEL) - for item in self.data.data: - logger.debug("Update called for item: {}".format(item)) + for item in self.fetch_trash_data.trash_schedule_default: + attributes = {} + attributes['next_pickup_in_days'] = item['days_remaining'] if item['key'] == self._name: - self._state = item['value'] + self.attributes = attributes + + for item in self.fetch_trash_data.trash_schedule_additional: + if item['key'] == self._name: + if item['value'] != 'None': + self._state = item['value'] -class TrashCollectionSchedule(object): +class TrashSchedule(object): """Fetch new state data for the sensor.""" - def __init__(self, url, allTrashNames, config): + def __init__(self, afvaldienst, config): """Fetch vars.""" - self._url = url - self._allTrashNames = allTrashNames - self.data = None + self._afvaldienst = afvaldienst self._config = config @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): """Fetch new state data for the sensor.""" - response = requests.get(self._url) - json_obj = response.json() - json_data = (json_obj['data']['ophaaldagen']['data'] + json_obj['data']['ophaaldagenNext']['data']) - - today = datetime.today().strftime('%Y-%m-%d') - dateConvert = datetime.strptime(today, '%Y-%m-%d') + timedelta(days=1) - tomorrow = datetime.strftime(dateConvert, '%Y-%m-%d') - - trashType = {} - trashNext = {} - trashToday = {} - trashTomorrow = {} - multiTrashToday = [] - multiTrashTomorrow = [] - trashSchedule = [] - - # Some date count functions for next - def d(s): - [year, month, day] = map(int, s.split('-')) - return date(year, month, day) - - def days(start, end): - return (d(end) - d(start)).days - - # Collect upcoming trash pickup dates for unique trash - for name in self._allTrashNames: - for item in json_data: - name = item["nameType"] - dateConvert = datetime.strptime(item['date'], '%Y-%m-%d').strftime('%d-%m-%Y') - - if name not in trashType: - if item['date'] >= today: - trash = {} - trashType[name] = item["nameType"] - trash['key'] = item['nameType'] - trash['value'] = dateConvert - trashSchedule.append(trash) - - if item['date'] > today: - if len(trashNext) == 0: - trashType[name] = "next" - trashNext['key'] = "next" - trashNext['value'] = (days(today, item['date'])) - trashSchedule.append(trashNext) - - if item['date'] == today: - trashType[name] = "today" - trashToday['key'] = "today" - trashSchedule.append(trashToday) - multiTrashToday.append(item['nameType']) - if len(multiTrashToday) != 0: - trashToday['value'] = ', '.join(multiTrashToday).strip() - - if item['date'] == tomorrow: - trashType[name] = "tomorrow" - trashTomorrow['key'] = "tomorrow" - trashSchedule.append(trashTomorrow) - multiTrashTomorrow.append(item['nameType']) - if len(multiTrashTomorrow) != 0: - trashTomorrow['value'] = ', '.join(multiTrashTomorrow).strip() - - # Setup scraper request - url = self._config.get(CONST_URL) - postcode = self._config.get(CONST_POSTCODE) - huisnummer = self._config.get(CONST_HUISNUMMER) - toevoeging = self._config.get(CONST_TOEVOEGING) - scraper_url = ("https://www.{0}/nl/{1}/{2}/{3}").format(url, postcode, huisnummer, toevoeging) - logger.debug("Scraper request url: {}".format(scraper_url)) - scraper_response = requests.get(scraper_url) - - if scraper_response.status_code != requests.codes.ok: - logger.exception("Error doing scrape request") - else: - logger.debug("Scrape request ok {}".format(scraper_response.status_code)) - - scraper_data = bs4.BeautifulSoup(scraper_response.text, "html.parser") - - # Append firstDate and firstWasteType - trashFirstDate = {} - trashFirstDate['key'] = 'firstdate' - trashFirstDate['value'] = scraper_data.find('p', attrs={'class':'firstDate'}).text - trashSchedule.append(trashFirstDate) - logger.debug("Data succesfully added {}".format(trashFirstDate)) - - firstWasteType = {} - firstWasteType['key'] = 'firstwastetype' - firstWasteType['value'] = scraper_data.find('p', attrs={'class':'firstWasteType'}).text - trashSchedule.append(firstWasteType) - logger.debug("Data succesfully added {}".format(firstWasteType)) - - # Return collected data - logger.debug("trashSchedule content {}".format(trashSchedule)) - - self.data = trashSchedule + self.trash_schedule_default = self._afvaldienst.trash_schedulefull_json + self.trash_schedule_additional = self._afvaldienst.trash_schedule_today_json + self._afvaldienst.trash_schedule_tomorrow_json + self._afvaldienst.trash_schedule_next_days_json \ No newline at end of file diff --git a/custom_components/sensor/mijnafvalwijzer-json-beta.py b/custom_components/sensor/mijnafvalwijzer-json-beta.py deleted file mode 100644 index 6713f68..0000000 --- a/custom_components/sensor/mijnafvalwijzer-json-beta.py +++ /dev/null @@ -1,228 +0,0 @@ -""" -@ Authors : Bram van Dartel -@ Date : 05/01/2019 -@ Description : MijnAfvalwijzer Sensor - It queries mijnafvalwijzer.nl. - -- platform: mijnafvalwijzer - postcode: POSTAL_CODE - huisnummer: HOUSE_NUMBER - toevoeging: a, b, c - label_none: none, geen -""" - -VERSION = '2.0.0' - -from datetime import datetime, timedelta, date -import voluptuous as vol -import requests -import sys -import logging -import json - -from homeassistant.util import Throttle -from homeassistant.helpers.entity import Entity -from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import (CONF_NAME) -import homeassistant.helpers.config_validation as cv - -_LOGGER = logging.getLogger(__name__) - -DEFAULT_NAME = 'mijnafvalwijzer' -DEFAULT_TIMEOUT = 10 -DEFAULT_LABEL = 'Geen' - -DOMAIN = 'mijnafvalwijzer' -ICON = 'mdi:delete-empty' -SENSOR_PREFIX = 'trash_' - -CONST_POSTCODE = 'postcode' -CONST_HUISNUMMER = 'huisnummer' -CONST_TOEVOEGING = 'toevoeging' -CONST_LABEL_NONE = 'label_geen' - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Required(CONST_POSTCODE): cv.string, - vol.Required(CONST_HUISNUMMER): cv.string, - vol.Optional(CONST_TOEVOEGING, default=''): cv.string, - vol.Optional(CONST_LABEL_NONE, default=DEFAULT_LABEL): cv.string, -}) - -SCAN_INTERVAL = timedelta(seconds=10) -MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60) - - -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the mijnafvalwijzer sensor platform.""" - - # Request JSON - postcode = config.get(CONST_POSTCODE) - huisnummer = config.get(CONST_HUISNUMMER) - toevoeging = config.get(CONST_TOEVOEGING) - - if None in (postcode, huisnummer): - _LOGGER.error("Mijnafvalwijzer - postcode or huisnummer not set in HomeAssistant config") - return False - else: - _LOGGER.debug("Initializing mijnafvalwijzer sensor postcode: %s, huisnummer: %s", postcode, huisnummer) - - url = ("https://json.mijnafvalwijzer.nl/?method=postcodecheck&postcode={0}&street=&huisnummer={1}&toevoeging={2}&platform=phone&langs=nl&").format(postcode, huisnummer, toevoeging) - response = requests.get(url, timeout=DEFAULT_TIMEOUT) - - if response.status_code != requests.codes.ok: - _LOGGER.exception("Error doing API request") - else: - _LOGGER.debug("API request ok %d", response.status_code) - - json_obj = response.json() - json_data = (json_obj['data']['ophaaldagen']['data'] + json_obj['data']['ophaaldagenNext']['data']) - - if len(json_data) == 0: - _LOGGER.error("JSON object doens't contain data") - else: - _LOGGER.debug("JSON object contains data") - - # Remove unused elements from json object - for x in json_data: - if 'type' in x: - del x['type'] - _LOGGER.debug("Removed type element from JSON object") - - - # Fetch trash types - size=len(json_data) - uniqueTrashNames = ['today', 'tomorrow', 'next'] - today = datetime.today().strftime("%Y-%m-%d") - dateConvert = datetime.strptime(today, "%Y-%m-%d") + timedelta(days=1) - tomorrow = datetime.strftime(dateConvert, "%Y-%m-%d") - trashSchedule = [] - devices = [] - - for i in range(0,size,1): - if(json_data[i]['nameType'] not in uniqueTrashNames): - if json_data[i]['date'] >= today: - uniqueTrashNames.append(json_data[i]['nameType']) - trashSchedule.append(json_data[i]) - - _LOGGER.debug("uniqueTrashNames succesfully added: %s", uniqueTrashNames) - _LOGGER.debug("trashSchedule succesfully added: %s", trashSchedule) - - data = (TrashCollectionSchedule(config, json_data, trashSchedule, today, tomorrow)) - - for item in uniqueTrashNames: - devices.append(TrashCollectionSensor(item, data)) - add_devices(devices) - - _LOGGER.debug("JSON object succesfully added: %s", devices) - -class TrashCollectionSensor(Entity): - """Representation of a Sensor.""" - - def __init__(self, name, data): - """Initialize the sensor.""" - self._state = None - self._name = name - self._data = data - - @property - def name(self): - """Return the name of the sensor.""" - return SENSOR_PREFIX + self._name - - @property - def state(self): - """Return the state of the sensor.""" - return self._state - - @property - def icon(self): - """Set the default sensor icon.""" - return ICON - - def update(self): - """Fetch new state data for the sensor.""" - self._data.update() - for x in self._data._data: - if x['nameType'] == self._name: - self._state = x['date'] - - _LOGGER.debug("Update called for mijnafvalwijzer") - - -class TrashCollectionSchedule(object): - """Fetch new state data for the sensor.""" - - def __init__(self, config, json_data, trashSchedule, today, tomorrow): - """Fetch vars.""" - self._data = None - self._config = config - self._json_data = json_data - self._trashSchedule = trashSchedule - self._today = today - self._tomorrow = tomorrow - - @Throttle(MIN_TIME_BETWEEN_UPDATES) - def update(self): - """Fetch new state data for the sensor.""" - # Append Today data - trashToday = {} - multiTrashToday = [] - today_out = [x for x in self._trashSchedule if x['date'] == self._today] - _LOGGER.debug("Trash Today: %s", today_out) - if len(today_out) == 0: - trashToday['nameType'] = 'today' - trashToday['date'] = 'None' - self._trashSchedule.append(trashToday) - _LOGGER.debug("Today contains no data, skipping") - else: - for x in today_out: - trashToday['nameType'] = 'today' - multiTrashToday.append(x['nameType']) - self._trashSchedule.append(trashToday) - trashToday['date'] = ', '.join(multiTrashToday) - _LOGGER.debug("Today data succesfully added %s", trashToday) - - # Append Tomorrow data - trashTomorrow = {} - multiTrashTomorrow = [] - tomorrow_out = [x for x in self._trashSchedule if x['date'] == self._tomorrow] - _LOGGER.debug("Trash Tomorrow: %s", tomorrow_out) - if len(tomorrow_out) == 0: - trashTomorrow['nameType'] = 'tomorrow' - trashTomorrow['date'] = 'None' - self._trashSchedule.append(trashTomorrow) - _LOGGER.debug("Tomorrow contains no data, skipping") - else: - for x in tomorrow_out: - trashTomorrow['nameType'] = 'tomorrow' - multiTrashTomorrow.append(x['nameType']) - self._trashSchedule.append(trashTomorrow) - trashTomorrow['date'] = ', '.join(multiTrashTomorrow) - _LOGGER.debug("Tomorrow data succesfully added %s", trashTomorrow) - - # Append next pickup in days - trashNext = {} - next_out = [x for x in self._trashSchedule if x['date'] > self._today] - _LOGGER.debug("Trash Next: %s", next_out) - def d(s): - [year, month, day] = map(int, s.split('-')) - return date(year, month, day) - def days(start, end): - return (d(end) - d(start)).days - - if len(next_out) == 0: - trashNext['nameType'] = 'next' - trashNext['date'] = 'None' - self._trashSchedule.append(trashNext) - _LOGGER.debug("Next contains no data, skupping") - else: - dateFormat = datetime.strptime(next_out[0]['date'], "%Y-%m-%d") - dateConvert = dateFormat.strftime("%Y-%m-%d") - if len(trashNext) == 0: - trashNext['nameType'] = 'next' - trashNext['date'] = (days(self._today, dateConvert)) - self._trashSchedule.append(trashNext) - _LOGGER.debug("Next data succesfully added %s", trashNext) - - _LOGGER.debug("trashSchedule content %s", self._trashSchedule) - self._data = self._trashSchedule diff --git a/custom_components/sensor/mijnafvalwijzer_scraper.py b/custom_components/sensor/mijnafvalwijzer_scraper.py deleted file mode 100644 index b50fcfa..0000000 --- a/custom_components/sensor/mijnafvalwijzer_scraper.py +++ /dev/null @@ -1,338 +0,0 @@ -""" -@ Authors : Bram van Dartel -@ Date : 13/01/2019 -@ Description : MijnAfvalwijzer Scrape Sensor - It queries mijnafvalwijzer.nl. -""" -VERSION = '2.0.4' - -import itertools -import logging -import re -from datetime import date, datetime, timedelta - -import requests - -import bs4 -import homeassistant.helpers.config_validation as cv -import voluptuous as vol -from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import CONF_NAME -from homeassistant.helpers.entity import Entity -from homeassistant.util import Throttle - -_LOGGER = logging.getLogger(__name__) - -DEFAULT_NAME = 'mijnafvalwijzer' -DOMAIN = 'mijnafvalwijzer' -ICON = 'mdi:delete-empty' -SENSOR_PREFIX = 'trash_' - -CONST_POSTCODE = "postcode" -CONST_HUISNUMMER = "huisnummer" -CONST_TOEVOEGING = "toevoeging" -CONST_LABEL_NONE = "label_geen" - -SCAN_INTERVAL = timedelta(seconds=30) -MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=3600) - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Required(CONST_POSTCODE): cv.string, - vol.Required(CONST_HUISNUMMER): cv.string, - vol.Optional(CONST_TOEVOEGING, default=""): cv.string, - vol.Optional(CONST_LABEL_NONE, default="Geen"): cv.string, -}) - - -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the sensor platform.""" - postcode = config.get(CONST_POSTCODE) - huisnummer = config.get(CONST_HUISNUMMER) - toevoeging = config.get(CONST_TOEVOEGING) - url = ("https://www.mijnafvalwijzer.nl/nl/{0}/{1}/{2}").format(postcode, huisnummer, toevoeging) - - response = requests.get(url) - if response.status_code != requests.codes.ok: - _LOGGER.exception("Error doing API request") - else: - _LOGGER.debug("API request ok %d", response.status_code) - - soup = bs4.BeautifulSoup(response.text, "html.parser") - if len(soup) == 0: - _LOGGER.error("Respons doesn't contain data") - else: - _LOGGER.debug("Respons contains data") - - # Get trash shortname - trashShortNames = [] - uniqueTrashShortNames = [] - defaultTrashNames = ['today', 'tomorrow', 'next'] - uniqueTrashShortNames.extend(defaultTrashNames) - sensors = [] - try: - for element in soup.select('a[href*="#waste"] p[class]'): - trashShortNames.extend(element["class"]) - for element in trashShortNames: - if element not in uniqueTrashShortNames: - uniqueTrashShortNames.append(element) - except IndexError: - return 'Error, empty reply.' - - _LOGGER.debug("trashShortNames succesfully added: %s", trashShortNames) - _LOGGER.debug("uniqueTrashShortNames succesfully added: %s", uniqueTrashShortNames) - - data = (TrashCollectionSchedule(url, defaultTrashNames ,config)) - - try: - for name in uniqueTrashShortNames: - sensors.append(TrashCollectionSensor(name, data, config)) - add_devices(sensors) - except IndexError: - return 'Error, empty reply.' - - _LOGGER.debug("Object succesfully added as sensor(s): %s", sensors) - - -class TrashCollectionSensor(Entity): - """Representation of a Sensor.""" - def __init__(self, name, data, config): - """Initialize the sensor.""" - self.config = config - self._state = self.config.get(CONST_LABEL_NONE) - self._name = name - self.data = data - - @property - def name(self): - """Return the name of the sensor.""" - return SENSOR_PREFIX + self._name - - @property - def state(self): - """Return the state of the sensor.""" - return self._state - - @property - def icon(self): - """Set the default sensor icon.""" - return ICON - - def update(self): - """Fetch new state data for the sensor.""" - self.data.update() - self._state = self.config.get(CONST_LABEL_NONE) - - _LOGGER.debug("Update called for mijnafvalwijzer...") - - try: - for item in self.data.data: - if item['key'] == self._name: - self._state = item['value'] - except IndexError: - return 'Error, empty reply.' - - -class TrashCollectionSchedule(object): - """Fetch new state data for the sensor.""" - def __init__(self, url, defaultTrashNames, config): - """Fetch vars.""" - self.url = url - self.data = None - self.defaultTrashNames = defaultTrashNames - self.config = config - - @Throttle(MIN_TIME_BETWEEN_UPDATES) - def update(self): - """Fetch new state data for the sensor.""" - response = requests.get(self.url) - if response.status_code != requests.codes.ok: - _LOGGER.exception("Error doing API request") - else: - _LOGGER.debug("API request ok %d", response.status_code) - - soup = bs4.BeautifulSoup(response.text, "html.parser") - if len(soup) == 0: - _LOGGER.error("Respons doesn't contain data") - else: - _LOGGER.debug("Respons contains data") - - today = datetime.today().strftime("%d-%m-%Y") - today_date = datetime.strptime(today, "%d-%m-%Y") - dateConvert = datetime.strptime(today, "%d-%m-%Y") + timedelta(days=1) - tomorrow = datetime.strftime(dateConvert, "%d-%m-%Y") - tomorrow_date = datetime.strptime(tomorrow, "%d-%m-%Y") - labelNone = self.config.get(CONST_LABEL_NONE) - - # Convert month to month number function - def _get_month_number(month): - if month == 'januari': - return '01' - elif month == 'februari': - return '02' - elif month == 'maart': - return '03' - elif month == 'april': - return '04' - elif month == 'mei': - return '05' - elif month == 'juni': - return '06' - elif month == 'juli': - return '07' - elif month == 'augustus': - return '08' - elif month == 'september': - return '09' - elif month == 'oktober': - return '10' - elif month == 'november': - return '11' - elif month == 'december': - return '12' - else: - return '00' - - # Get current year - for item in soup.select('[class="ophaaldagen"]'): - year_id = item["id"] - year = re.sub('jaar-','',year_id) - _LOGGER.debug("Year found: %s", year) - - # Get trash dump - trashDump = [] - trashSchedule = [] - json_data = [] - try: - for data in soup.select('a[href*="#waste"] p[class]'): - element = data["class"] - for item in element: - x = item - name = data.get_text() - trashDump.append(name) - trashDump.append(x) - _LOGGER.debug("Trash scraped from website: %s", trashDump) - except IndexError: - return 'Error, empty reply.' - - # Get trash dates and generate dictionairy - uniqueTrashDates = [i.split('\n', 1) for i in trashDump] - uniqueTrashDates = list(itertools.chain.from_iterable(uniqueTrashDates)) - uniqueTrashDates = [uniqueTrashDates[i:i+3]for i in range(0,len(uniqueTrashDates),3)] - _LOGGER.debug("Trash dates conversion output from scraped website data: %s", uniqueTrashDates) - - try: - for item in uniqueTrashDates: - split_date = item[0].split(' ') - day = split_date[1] - month_name = split_date[2] - month = _get_month_number(month_name) - _LOGGER.debug("Converting month name: %s to month number %s", month_name, month) - trashDump = {} - trashDump['key'] = item[2] - trashDump['description'] = item[1] - trashDump['value'] = day + '-' + month + '-' + year - json_data.append(trashDump) - _LOGGER.debug("New generated dictionairy with converted dates: %s", json_data) - except IndexError: - return 'Error, empty reply.' - - - # Append first upcoming unique trash item with pickup date - uniqueTrashNames = [] - uniqueTrashNames.extend(self.defaultTrashNames) - try: - for item in json_data: - key = item['key'] - description = item['description'] - value = item['value'] - value_date = datetime.strptime(item['value'], "%d-%m-%Y") - if value_date >= today_date: - if key not in uniqueTrashNames: - trash = {} - trash['key'] = key - trash['description'] = description - trash['value'] = value - uniqueTrashNames.append(key) - trashSchedule.append(trash) - _LOGGER.debug("New dictionairy with update data: %s", trashSchedule) - except IndexError: - return 'Error, empty reply.' - - - # Collect data - today_out = [x for x in trashSchedule if datetime.strptime(x['value'], "%d-%m-%Y") == today_date] - _LOGGER.debug("Trash Today: %s", today_out) - tomorrow_out = [x for x in trashSchedule if datetime.strptime(x['value'], "%d-%m-%Y") == tomorrow_date] - _LOGGER.debug("Trash Tomorrow: %s", tomorrow_out) - next_out = [x for x in trashSchedule if datetime.strptime(x['value'], "%d-%m-%Y") > today_date] - _LOGGER.debug("Trash Next Pickup Day: %s", next_out) - - # Append Today data - trashToday = {} - multiTrashToday = [] - if len(today_out) == 0: - trashToday['key'] = 'today' - trashToday['description'] = 'Trash Today' - trashToday['value'] = labelNone - trashSchedule.append(trashToday) - _LOGGER.debug("Today contains no data, skipping...") - else: - try: - for x in today_out: - trashToday['key'] = 'today' - trashToday['description'] = 'Trash Today' - multiTrashToday.append(x['key']) - trashSchedule.append(trashToday) - trashToday['value'] = ', '.join(multiTrashToday) - _LOGGER.debug("Today data succesfully added %s", trashToday) - except IndexError: - return 'Error, empty reply.' - - - # Append Tomorrow data - trashTomorrow = {} - multiTrashTomorrow = [] - if len(tomorrow_out) == 0: - trashTomorrow['key'] = 'tomorrow' - trashTomorrow['description'] = 'Trash Tomorrow' - trashTomorrow['value'] = labelNone - trashSchedule.append(trashTomorrow) - _LOGGER.debug("Tomorrow contains no data, skipping...") - else: - try: - for x in tomorrow_out: - trashTomorrow['key'] = 'tomorrow' - trashTomorrow['description'] = 'Trash Tomorrow' - multiTrashTomorrow.append(x['key']) - trashSchedule.append(trashTomorrow) - trashTomorrow['value'] = ', '.join(multiTrashTomorrow) - _LOGGER.debug("Today data succesfully added %s", trashTomorrow) - except IndexError: - return 'Error, empty reply.' - - - # Append next pickup in days - trashNext = {} - ## Amount of days between two dates function - def d(s): - [year, month, day] = map(int, s.split('-')) - return date(day, month, year) - def days(start, end): - return (d(end) - d(start)).days - - if len(next_out) == 0: - trashNext['key'] = 'next' - trashNext['value'] = labelNone - trashSchedule.append(trashNext) - _LOGGER.debug("Next contains no data, skipping...") - else: - if len(trashNext) == 0: - trashNext['key'] = 'next' - trashNext['value'] = (days(today, next_out[0]['value'])) - trashSchedule.append(trashNext) - _LOGGER.debug("Next data succesfully added %s", trashNext) - - - # Return collected data - _LOGGER.debug("trashSchedule content %s", trashSchedule) - self.data = trashSchedule diff --git a/custom_updater.json b/custom_updater.json index 122339e..a71e1cd 100644 --- a/custom_updater.json +++ b/custom_updater.json @@ -1,6 +1,6 @@ { "sensor.afvalwijzer": { - "version": "3.0.12", + "version": "4.0.0", "local_location": "/custom_components/afvalwijzer/__init__.py", "remote_location": "https://raw.githubusercontent.com/xirixiz/Home-Assistant-Sensor-Afvalwijzer/master/custom_components/afvalwijzer/__init__.py", "visit_repo": "https://github.com/xirixiz/Home-Assistant-Sensor-Afvalwijzer/", diff --git a/info.md b/info.md new file mode 100644 index 0000000..daf0307 --- /dev/null +++ b/info.md @@ -0,0 +1,12 @@ + +## Example configuration + +###### SENSOR +```yaml +- platform: afvalwijzer # Required + provider: mijnafvalwijzer # Optional - mijnafvalwijzer (default) or afvalstoffendienstkalender + zipcode: postcode # Required + housenumber: huisnummer # Required + suffix: toevoeging # Optional + default_label: label # Optional - Default is 'Geen' +``` \ No newline at end of file diff --git a/readme.md b/readme.md index c8801d9..a4c1dbd 100644 --- a/readme.md +++ b/readme.md @@ -58,14 +58,21 @@ Here's an example of my own Home Asisstant config: https://github.com/xirixiz/ho ###### SENSOR ```yaml -- platform: afvalwijzer - url: 'mijnafvalwijzer.nl' - postcode: '1111AA' - huisnummer: '1' - toevoeging: 'A' - label_geen: 'Geen' +- platform: afvalwijzer # Required + provider: mijnafvalwijzer # Optional - mijnafvalwijzer (default) or afvalstoffendienstkalender + zipcode: postcode # Required + housenumber: huisnummer # Required + suffix: toevoeging # Optional + default_label: label # Optional - Default is 'Geen' ``` - + +```yaml +- platform: afvalwijzer # Required + provider: mijnafvalwijzer # Optional - mijnafvalwijzer (default) or afvalstoffendienstkalender + zipcode: 1111AA # Required + housenumber: 11 # Required +``` + ###### INPUT BOOLEAN (FOR AUTOMATION) ```yaml input_boolean: diff --git a/standalone_bash/mijnafvalwijzer_standalone.sh b/standalone_bash/mijnafvalwijzer_standalone.sh deleted file mode 100755 index 1257b3a..0000000 --- a/standalone_bash/mijnafvalwijzer_standalone.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash - -##### Functions -function usage { - echo -e "\nusage: $0 [--postcode --huisnummer ][--output ][--debug][--help]" - echo -e "" - echo -e " General parameters:" - echo -e " --postcode specify postcal code." - echo -e " --huisnummer specify house number." - echo -e " --output specify output file. Default: mijnafvalwijzer.json" - echo -e " --debug debug mode." - echo -e " -? help." - exit 0 -} - -function commands_check { - for i; do - if command -v $i >/dev/null 2>&1; then - true - else - echo "This script requires '$i'" - false - fi - done || exit 1 -} - -##### Posistional params -while [ $# -gt 0 ]; do - case $1 in - --postcode ) shift && export POSTCODE="$1" ;; - --huisnummer ) shift && export HUISNUMMER="$1" ;; - --output ) shift && export OUTPUT="$1" ;; - --debug ) DEBUG=debug ;; - -? | --help ) usage && exit 0 ;; - * ) echo -e "\nError: Unknown option: $1\n" >&2 && exit 1 ;; - esac - shift -done - -##### Main -if [[ ! -z $DEBUG ]]; then set -x; fi -if [[ -z $POSTCODE ]]; then echo "Postcode missing!"; usage; fi -if [[ -z $HUISNUMMER ]]; then echo "Huisnummer missing!"; usage; fi -if [[ -z $OUTPUT ]]; then export OUTPUT="mijnafvalwijzer.json"; fi - -commands_check curl jq - -curl -Ssl "https://json.mijnafvalwijzer.nl/?method=postcodecheck&postcode=${POSTCODE}&street=&huisnummer=${HUISNUMMER}" | jq -rc ".data.ophaaldagen.data" > ${OUTPUT} -[ -s ${OUTPUT} ] || curl -Ssl "http://json.mijnafvalwijzer.nl/?method=postcodecheck&postcode=${POSTCODE}&street=&huisnummer=${HUISNUMMER}" | jq -rc ".data.ophaaldagenNext.data" > ${OUTPUT} - - -ALL_TRASH_TYPES=$(cat ${OUTPUT} | jq -rc ". | unique_by(.type)[].type") -FUTURE_TRASH_TYPES=$(cat ${OUTPUT} | jq -rc "[.[] | select(.date >= \"$(date +%Y-%m-%d)\")] | unique_by(.type)[].type") - -for TRASH_TYPE in ${FUTURE_TRASH_TYPES}; do - cat ${OUTPUT} | jq -rc "[.[] | select(.date >= \"$(date +%Y-%m-%d)\" and .type == \"${TRASH_TYPE}\")][0]" -done - - -#ALL_TRASH_TYPES=$(curl -Ssl "http://json.mijnafvalwijzer.nl/?method=postcodecheck&postcode=${POSTCODE}&street=&huisnummer=${HUISNUMMER}" | jq -rc ".data.ophaaldagen.data | unique_by(.type)[].type") -#FUTURE_TRASH_TYPES=$(curl -Ssl "http://json.mijnafvalwijzer.nl/?method=postcodecheck&postcode=${POSTCODE}&street=&huisnummer=${HUISNUMMER}" | jq -rc "[.data.ophaaldagen.data[] | select(.date >= \"$(date +%Y-%m-%d)\")] | unique_by(.type)[].type") - -#for TRASH_TYPE in ${FUTURE_TRASH_TYPES}; do -# curl -Ssl "http://json.mijnafvalwijzer.nl/?method=postcodecheck&postcode=${POSTCODE}&street=&huisnummer=${HUISNUMMER}" | jq -rc "[.data.ophaaldagen.data[] | select(.date >= \"$(date +%Y-%m-%d)\" and .type == \"${TRASH_TYPE}\")][0]" -#done diff --git a/standalone_bash/readme.md b/standalone_bash/readme.md deleted file mode 100644 index a724163..0000000 --- a/standalone_bash/readme.md +++ /dev/null @@ -1,2 +0,0 @@ -###### BASH JSON OUTPUT -Simple json output from mijnafvalwijzer.nl diff --git a/standalone_python_json/mijnafvalwijzer_standalone.py b/standalone_python_json/mijnafvalwijzer_standalone.py deleted file mode 100755 index 6671e2e..0000000 --- a/standalone_python_json/mijnafvalwijzer_standalone.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python -""" -@ Author : Bram van Dartel -@ Date : 28/03/2018 -@ Description : MijnAfvalwijzer to JSON -""" - -import requests -import json -import argparse -import datetime - -def get_args(): - parser = argparse.ArgumentParser( - description='MijnAfvalwijzer JSON parser for Home Assistant.') - parser.add_argument( - '-p', '--postcode', type=str, help='Postcode', required=True) - parser.add_argument( - '-n', '--huisnummer', type=str, help='Huisnummer', required=True) - parser.add_argument( - '-t', '--toevoeging', type=str, help='Toevoeging', required=False, default="") - args = parser.parse_args() - postcode = args.postcode - huisnummer = args.huisnummer - toevoeging = args.toevoeging - return postcode, huisnummer, toevoeging - -postcode, huisnummer, toevoeging = get_args() - -url = ("https://json.mijnafvalwijzer.nl/?method=postcodecheck&postcode={0}&street=&huisnummer={1}&toevoeging={2}&platform=phone&langs=nl&").format(postcode,huisnummer,toevoeging) -response = requests.get(url) -json_obj = response.json() -json_data = json_obj['data']['ophaaldagen']['data'] -json_data_next = json_obj['data']['ophaaldagenNext']['data'] -today = datetime.date.today().strftime("%Y-%m-%d") -countType = 1 - -trashType = {} -trashTotal = [] - -# Collect legend -for item in json_data or json_data_next: - name = item["nameType"] - if name not in trashType: - trash = {} - trashType[name] = item["nameType"] - trash[countType] = item["nameType"] - countType +=1 - trashTotal.append(trash) -print(trashTotal) - -# Reset values -trashType = {} -trashTotal = [] - -# Collect legend -for item in json_data or json_data_next: - name = item["nameType"] - d = datetime.datetime.strptime(item['date'], "%Y-%m-%d") - dateConvert = d.strftime("%Y-%m-%d") - if name not in trashType: - if item['date'] > today: - trash = {} - trashType[name] = item["nameType"] - trash["name_type"] = item["nameType"] - trash["pickup_date"] = dateConvert - trashTotal.append(trash) -print(trashTotal) diff --git a/standalone_python_json/mijnafvalwijzer_standalone_json.py b/standalone_python_json/mijnafvalwijzer_standalone_json.py deleted file mode 100755 index 4a66f13..0000000 --- a/standalone_python_json/mijnafvalwijzer_standalone_json.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python -""" -@ Author : Bram van Dartel -@ Date : 28/03/2018 -@ Description : MijnAfvalwijzer to JSON -""" - -import requests -import json -import argparse -import datetime - -def get_args(): - parser = argparse.ArgumentParser( - description='MijnAfvalwijzer JSON parser for Home Assistant.') - parser.add_argument( - '-p', '--postcode', type=str, help='Postcode', required=True) - parser.add_argument( - '-n', '--huisnummer', type=str, help='Huisnummer', required=True) - parser.add_argument( - '-t', '--toevoeging', type=str, help='Toevoeging', required=False, default="") - args = parser.parse_args() - postcode = args.postcode - huisnummer = args.huisnummer - toevoeging = args.toevoeging - return postcode, huisnummer, toevoeging - -postcode, huisnummer, toevoeging = get_args() - -url = ("https://json.mijnafvalwijzer.nl/?method=postcodecheck&postcode={0}&street=&huisnummer={1}&toevoeging={2}&platform=phone&langs=nl&").format(postcode,huisnummer,toevoeging) -response = requests.get(url) -json_obj = response.json() -json_data = json_obj['data']['ophaaldagen']['data'] -json_data_next = json_obj['data']['ophaaldagenNext']['data'] - -trashType = {} -trashDate = {} -trashTotal = {} - -# Collect legend -for item in json_data: - name = item["nameType"] - if name not in trashType: - trashType[name] = item["nameType"] - -# Collect days -for item in json_data: - d = datetime.datetime.strptime(item['date'], "%Y-%m-%d") - dateConvert = d.strftime("%d/%m/%Y") - if dateConvert not in trashDate: - trashDate[dateConvert] = item["nameType"] - -# Beautify -trashType = {"legend": trashType} -trashDate = {"days": trashDate} -trashTotal.update(trashType) -trashTotal.update(trashDate) - -print(json.dumps(trashTotal, indent=4)) diff --git a/standalone_python_json/readme.md b/standalone_python_json/readme.md deleted file mode 100644 index d7e32ee..0000000 --- a/standalone_python_json/readme.md +++ /dev/null @@ -1,97 +0,0 @@ -Thanks to the input of https://github.com/RolfKoenders/smarthome - -Can be used with - mijnafvalwijzer_standalone_json.py - -###### NOTIFY TRASH - SENSOR -```yaml -- platform: rest - resource: https://raw.githubusercontent.com/xxxx/xxxx/master/mijnafvalwijzer.json - name: Trash_Today - scan_interval: 3600 - value_template: > - {% set now = as_timestamp(now()) %} - {% set today = now | timestamp_custom("%d/%m/%Y") %} - {% set containerType = value_json.days[ today ] %} - {% if containerType | trim != "" %} - {% set trash = value_json.legend[ containerType ] %} - {{ trash }} - {% else %} - Geen - {% endif %} - -- platform: rest - resource: https://raw.githubusercontent.com/xxxx/xxxx/master/mijnafvalwijzer.json - name: Trash_Tomorrow - scan_interval: 3600 - value_template: > - {% set now = as_timestamp(now()) %} - {% set oneDay = 86400 %} - {% set nextDay = now + oneDay %} - {% set tomorrow = nextDay | timestamp_custom("%d/%m/%Y") %} - {% set containerType = value_json.days[ tomorrow ] %} - {% if containerType | trim != "" %} - {% set trash = value_json.legend[ containerType ] %} - {{ trash }} - {% else %} - Geen - {% endif %} -``` - -###### NOTIFY TRASH - AUTOMATION -```yaml -- alias: 'Notify of which container will be pickedup today' - initial_state: true - hide_entity: false - trigger: - - platform: time - at: '07:00:00' - condition: - - condition: state - entity_id: input_boolean.notify_trash - state: 'on' - - condition: template - value_template: "{{ states('sensor.trash_today') != 'Geen' }}" - - condition: template - value_template: "{{ states.sensor.trash_today.state | trim != '' }}" - action: - - service: notify.family - data_template: - message: 'Vandaag kan de {{ states.sensor.trash_today.state }} container aan de straat.' - -- alias: 'Notify of which container will be pickedup tomorrow' - initial_state: true - hide_entity: false - trigger: - - platform: time - at: '20:00:00' - condition: - - condition: state - entity_id: input_boolean.notify_trash - state: 'on' - - condition: template - value_template: "{{ states('sensor.trash_tomorrow') != 'Geen' }}" - - condition: template - value_template: "{{ states.sensor.trash_tomorrow.state | trim != '' }}" - action: - - service: notify.family - data_template: - message: 'Morgen kan de {{ states.sensor.trash_tomorrow.state }} container aan de straat.' -``` - -###### NOTIFY TRASH - CUSTOMIZATION -```yaml -sensor.trash_today: - friendly_name: 'Vandaag' - icon: mdi:delete -sensor.trash_tomorrow: - friendly_name: 'Morgen' - icon: mdi:delete -``` - -###### NOTIFY TRASH - INPUT BOOLEAN -```yaml -notify_trash: - name: 'Notificaties' - initial: 'on' - icon: mdi:bell-ring -``` diff --git a/standalone_python_scraper/mijnafvalwijzer_scraper.py b/standalone_python_scraper/mijnafvalwijzer_scraper.py deleted file mode 100644 index 66068b8..0000000 --- a/standalone_python_scraper/mijnafvalwijzer_scraper.py +++ /dev/null @@ -1,361 +0,0 @@ -""" -@ Authors : Bram van Dartel -@ Date : 20/02/2019 -@ Description : MijnAfvalwijzer Scrape Sensor - It queries mijnafvalwijzer.nl. - -sensor: - - platform: mijnafvalwijzer - postcode: 1111AA - huisnummer: 1 - toevoeging: A - label_geen: 'Geen' - -20190220 - Bugfix: mijnafvalwijzer added a ton of spaces in the output. -""" - -VERSION = '2.0.7' - -import itertools -import logging -import re -from datetime import date, datetime, timedelta - -import requests - -import bs4 -import homeassistant.helpers.config_validation as cv -import voluptuous as vol -from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import CONF_NAME -from homeassistant.helpers.entity import Entity -from homeassistant.util import Throttle - -logger = logging.getLogger(__name__) - -DEFAULT_NAME = 'mijnafvalwijzer' -DOMAIN = 'mijnafvalwijzer' -ICON = 'mdi:delete-empty' -SENSOR_PREFIX = 'trash_' - -CONST_POSTCODE = "postcode" -CONST_HUISNUMMER = "huisnummer" -CONST_TOEVOEGING = "toevoeging" -CONST_LABEL_NONE = "label_geen" - -SCAN_INTERVAL = timedelta(seconds=30) -MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=3600) - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Required(CONST_POSTCODE): cv.string, - vol.Required(CONST_HUISNUMMER): cv.string, - vol.Optional(CONST_TOEVOEGING, default=""): cv.string, - vol.Optional(CONST_LABEL_NONE, default="Geen"): cv.string, -}) - - -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup the sensor platform.""" - postcode = config.get(CONST_POSTCODE) - huisnummer = config.get(CONST_HUISNUMMER) - toevoeging = config.get(CONST_TOEVOEGING) - url = (f"https://www.mijnafvalwijzer.nl/nl/{postcode}/{huisnummer}/{toevoeging}") - - logger.debug(f"Request url: {url}") - response = requests.get(url) - logger.debug(response.content) - - if response.status_code != requests.codes.ok: - logger.exception("Error doing API request") - else: - logger.debug(f"API request ok {response.status_code}") - - soup = bs4.BeautifulSoup(response.text, "html.parser") - - # Get trash shortname - trashShortNames = [] - uniqueTrashShortNames = [] - defaultTrashNames = ['firstdate', 'firstwastetype', 'today', 'tomorrow', 'next'] - uniqueTrashShortNames.extend(defaultTrashNames) - sensors = [] - try: - for element in soup.select('a[href*="#waste"] p[class]'): - trashShortNames.extend(element["class"]) - for element in trashShortNames: - if element not in uniqueTrashShortNames: - uniqueTrashShortNames.append(element) - except IndexError: - return 'Error, empty reply.' - - logger.debug(f"trashShortNames succesfully added: {trashShortNames}") - logger.debug(f"uniqueTrashShortNames succesfully added: {uniqueTrashShortNames}") - - data = (TrashCollectionSchedule(url, defaultTrashNames, config)) - - try: - for name in uniqueTrashShortNames: - sensors.append(TrashCollectionSensor(name, data, config)) - add_devices(sensors) - except IndexError: - return 'Error, empty reply.' - - logger.debug(f"Object succesfully added as sensor(s): {sensors}") - - -class TrashCollectionSensor(Entity): - """Representation of a Sensor.""" - def __init__(self, name, data, config): - """Initialize the sensor.""" - self.config = config - self._state = self.config.get(CONST_LABEL_NONE) - self._name = name - self.data = data - - @property - def name(self): - """Return the name of the sensor.""" - return SENSOR_PREFIX + self._name - - @property - def state(self): - """Return the state of the sensor.""" - return self._state - - @property - def icon(self): - """Set the default sensor icon.""" - return ICON - - def update(self): - """Fetch new state data for the sensor.""" - self.data.update() - self._state = self.config.get(CONST_LABEL_NONE) - - logger.debug("Update called for mijnafvalwijzer...") - - try: - for item in self.data.data: - if item['key'] == self._name: - self._state = item['value'] - except IndexError: - return 'Error, empty reply.' - - -class TrashCollectionSchedule(object): - """Fetch new state data for the sensor.""" - def __init__(self, url, defaultTrashNames, config): - """Fetch vars.""" - self.url = url - self.data = None - self.defaultTrashNames = defaultTrashNames - self.config = config - - @Throttle(MIN_TIME_BETWEEN_UPDATES) - def update(self): - """Fetch new state data for the sensor.""" - response = requests.get(self.url) - if response.status_code != requests.codes.ok: - logger.exception("Error doing API request") - else: - logger.debug(f"API request ok {response.status_code}") - - soup = bs4.BeautifulSoup(response.text, "html.parser") - if len(soup) == 0: - logger.error("Respons doesn't contain data") - else: - logger.debug("Respons contains data") - - today = datetime.today().strftime("%d-%m-%Y") - today_date = datetime.strptime(today, "%d-%m-%Y") - dateConvert = datetime.strptime(today, "%d-%m-%Y") + timedelta(days=1) - tomorrow = datetime.strftime(dateConvert, "%d-%m-%Y") - tomorrow_date = datetime.strptime(tomorrow, "%d-%m-%Y") - labelNone = self.config.get(CONST_LABEL_NONE) - - # Convert month to month number function - def _get_month_number(month): - if month == 'januari': - return '01' - elif month == 'februari': - return '02' - elif month == 'maart': - return '03' - elif month == 'april': - return '04' - elif month == 'mei': - return '05' - elif month == 'juni': - return '06' - elif month == 'juli': - return '07' - elif month == 'augustus': - return '08' - elif month == 'september': - return '09' - elif month == 'oktober': - return '10' - elif month == 'november': - return '11' - elif month == 'december': - return '12' - else: - return '00' - - # Get current year - for item in soup.select('[class="ophaaldagen"]'): - year_id = item["id"] - year = re.sub('jaar-','',year_id) - logger.debug(f"Year found: {year}") - if len(year) == 0: - year = '0000' - - # Get trash dump - trashDump = [] - trashSchedule = [] - json_data = [] - try: - for data in soup.select('a[href*="#waste"] p[class]'): - element = data["class"] - for item in element: - x = item - name = data.get_text() - trashDump.append(name) - trashDump.append(x) - logger.debug(f"Trash scraped from website: {trashDump}") - except IndexError: - return 'Error, empty reply.' - - # Get trash dates and generate dictionairy - uniqueTrashDates = [x.replace('\t', '') for x in trashDump] - uniqueTrashDates = [x.split('\n') for x in uniqueTrashDates] - uniqueTrashDates = list(itertools.chain.from_iterable(uniqueTrashDates)) - uniqueTrashDates = [x for x in uniqueTrashDates if x != ''] - uniqueTrashDates = [uniqueTrashDates[x:x+3] for x in range(0, len(uniqueTrashDates), 3)] - logger.debug(f"Trash dates conversion output from scraped website data: {uniqueTrashDates}") - - try: - for item in uniqueTrashDates: - split_date = item[0].split(' ') - day = split_date[1] - month_name = split_date[2] - month = _get_month_number(month_name) - logger.debug(f"Converting month name: {month_name} to month number {month}") - trashDump = {} - trashDump['key'] = item[2] - trashDump['description'] = item[1] - trashDump['value'] = day + '-' + month + '-' + year - json_data.append(trashDump) - logger.debug(f"New generated dictionairy with converted dates: {json_data}") - except IndexError: - return 'Error, empty reply.' - - # Append first upcoming unique trash item with pickup date - uniqueTrashNames = [] - uniqueTrashNames.extend(self.defaultTrashNames) - try: - for item in json_data: - key = item['key'] - description = item['description'] - value = item['value'] - value_date = datetime.strptime(item['value'], "%d-%m-%Y") - if value_date >= today_date: - if key not in uniqueTrashNames: - trash = {} - trash['key'] = key - trash['description'] = description - trash['value'] = value - uniqueTrashNames.append(key) - trashSchedule.append(trash) - logger.debug(f"New dictionairy with update data: {trashSchedule}") - except IndexError: - return 'Error, empty reply.' - - # Collect data for today, tomorrow and next - today_out = [x for x in trashSchedule if datetime.strptime(x['value'], "%d-%m-%Y") == today_date] - logger.debug(f"Trash Today: {today_out}") - tomorrow_out = [x for x in trashSchedule if datetime.strptime(x['value'], "%d-%m-%Y") == tomorrow_date] - logger.debug(f"Trash Tomorrow: {tomorrow_out}") - next_out = [x for x in trashSchedule if datetime.strptime(x['value'], "%d-%m-%Y") > today_date] - logger.debug(f"Trash Next Pickup Day: {next_out}") - - # Append Today data - trashToday = {} - multiTrashToday = [] - if len(today_out) == 0: - trashToday['key'] = 'today' - trashToday['description'] = 'Trash Today' - trashToday['value'] = labelNone - trashSchedule.append(trashToday) - logger.debug("Today contains no data, skipping...") - else: - try: - for x in today_out: - trashToday['key'] = 'today' - trashToday['description'] = 'Trash Today' - multiTrashToday.append(x['key']) - trashSchedule.append(trashToday) - trashToday['value'] = ', '.join(multiTrashToday) - logger.debug(f"Today data succesfully added {trashToday}") - except IndexError: - return 'Error, empty reply.' - - # Append Tomorrow data - trashTomorrow = {} - multiTrashTomorrow = [] - if len(tomorrow_out) == 0: - trashTomorrow['key'] = 'tomorrow' - trashTomorrow['description'] = 'Trash Tomorrow' - trashTomorrow['value'] = labelNone - trashSchedule.append(trashTomorrow) - logger.debug("Tomorrow contains no data, skipping...") - else: - try: - for x in tomorrow_out: - trashTomorrow['key'] = 'tomorrow' - trashTomorrow['description'] = 'Trash Tomorrow' - multiTrashTomorrow.append(x['key']) - trashSchedule.append(trashTomorrow) - trashTomorrow['value'] = ', '.join(multiTrashTomorrow) - logger.debug(f"Today data succesfully added {trashTomorrow}") - except IndexError: - return 'Error, empty reply.' - - # Append next pickup in days - trashNext = {} - - # Amount of days between two dates function - def d(s): - [year, month, day] = map(int, s.split('-')) - return date(day, month, year) - - def days(start, end): - return (d(end) - d(start)).days - - if len(next_out) == 0: - trashNext['key'] = 'next' - trashNext['value'] = labelNone - trashSchedule.append(trashNext) - logger.debug("Next contains no data, skipping...") - else: - if len(trashNext) == 0: - trashNext['key'] = 'next' - trashNext['value'] = (days(today, next_out[0]['value'])) - trashSchedule.append(trashNext) - logger.debug(f"Next data succesfully added {trashNext}") - - # Append firstDate and firstWasteType - trashFirstDate = {} - trashFirstDate['key'] = 'firstdate' - trashFirstDate['value'] = soup.find('p', attrs={'class':'firstDate'}).text - trashSchedule.append(trashFirstDate) - logger.debug(f"firstDate data succesfully added {trashFirstDate}") - - firstWasteType = {} - firstWasteType['key'] = 'firstwastetype' - firstWasteType['value'] = soup.find('p', attrs={'class':'firstWasteType'}).text - trashSchedule.append(firstWasteType) - logger.debug(f"firstDate data succesfully added {firstWasteType}") - - # Return collected data - logger.debug(f"trashSchedule content {trashSchedule}") - self.data = trashSchedule