diff --git a/.coveragerc b/.coveragerc index 5c99318b255..75b8b3b4ff8 100644 --- a/.coveragerc +++ b/.coveragerc @@ -856,6 +856,7 @@ omit = homeassistant/components/sensor/rainbird.py homeassistant/components/sensor/recollect_waste.py homeassistant/components/sensor/ripple.py + homeassistant/components/sensor/rova.py homeassistant/components/sensor/rtorrent.py homeassistant/components/sensor/ruter.py homeassistant/components/sensor/scrape.py diff --git a/homeassistant/components/sensor/rova.py b/homeassistant/components/sensor/rova.py new file mode 100644 index 00000000000..0b7f43f0973 --- /dev/null +++ b/homeassistant/components/sensor/rova.py @@ -0,0 +1,149 @@ +""" +Support for Rova garbage calendar. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.rova/ +""" + +from datetime import datetime, timedelta +import logging + +import voluptuous as vol + +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import (CONF_MONITORED_CONDITIONS, CONF_NAME, + DEVICE_CLASS_TIMESTAMP) +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity +from homeassistant.util import Throttle + +REQUIREMENTS = ['rova==0.0.2'] + +# Config for rova requests. +CONF_ZIP_CODE = 'zip_code' +CONF_HOUSE_NUMBER = 'house_number' + +UPDATE_DELAY = timedelta(hours=12) +SCAN_INTERVAL = timedelta(hours=12) + +# Supported sensor types: +# Key: [json_key, name, icon] +SENSOR_TYPES = { + 'bio': ['gft', 'Biowaste', 'mdi:recycle'], + 'paper': ['papier', 'Paper', 'mdi:recycle'], + 'plastic': ['plasticplus', 'PET', 'mdi:recycle'], + 'residual': ['rest', 'Residual', 'mdi:recycle']} + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_ZIP_CODE): cv.string, + vol.Required(CONF_HOUSE_NUMBER): cv.string, + vol.Optional(CONF_NAME, default='Rova'): cv.string, + vol.Optional(CONF_MONITORED_CONDITIONS, default=['bio']): + vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]) +}) + +_LOGGER = logging.getLogger(__name__) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Create the Rova data service and sensors.""" + from rova.rova import Rova + from requests.exceptions import HTTPError, ConnectTimeout + + zip_code = config[CONF_ZIP_CODE] + house_number = config[CONF_HOUSE_NUMBER] + platform_name = config[CONF_NAME] + + # Create new Rova object to retrieve data + api = Rova(zip_code, house_number) + + try: + if not api.is_rova_area(): + _LOGGER.error("ROVA does not collect garbage in this area") + return + except (ConnectTimeout, HTTPError): + _LOGGER.error("Could not retrieve details from ROVA API") + return + + # Create rova data service which will retrieve and update the data. + data_service = RovaData(api) + + # Create a new sensor for each garbage type. + entities = [] + for sensor_key in config[CONF_MONITORED_CONDITIONS]: + sensor = RovaSensor(platform_name, sensor_key, data_service) + entities.append(sensor) + + add_entities(entities, True) + + +class RovaSensor(Entity): + """Representation of a Rova sensor.""" + + def __init__(self, platform_name, sensor_key, data_service): + """Initialize the sensor.""" + self.sensor_key = sensor_key + self.platform_name = platform_name + self.data_service = data_service + + self._state = None + + self._json_key = SENSOR_TYPES[self.sensor_key][0] + + @property + def name(self): + """Return the name.""" + return "{}_{}".format(self.platform_name, self.sensor_key) + + @property + def icon(self): + """Return the sensor icon.""" + return SENSOR_TYPES[self.sensor_key][2] + + @property + def device_class(self): + """Return the class of this sensor.""" + return DEVICE_CLASS_TIMESTAMP + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + def update(self): + """Get the latest data from the sensor and update the state.""" + self.data_service.update() + pickup_date = self.data_service.data.get(self._json_key) + if pickup_date is not None: + self._state = pickup_date.isoformat() + + +class RovaData: + """Get and update the latest data from the Rova API.""" + + def __init__(self, api): + """Initialize the data object.""" + self.api = api + self.data = {} + + @Throttle(UPDATE_DELAY) + def update(self): + """Update the data from the Rova API.""" + from requests.exceptions import HTTPError, ConnectTimeout + + try: + items = self.api.get_calendar_items() + except (ConnectTimeout, HTTPError): + _LOGGER.error("Could not retrieve data, retry again later") + return + + self.data = {} + + for item in items: + date = datetime.strptime(item['Date'], '%Y-%m-%dT%H:%M:%S') + code = item['GarbageTypeCode'].lower() + + if code not in self.data and date > datetime.now(): + self.data[code] = date + + _LOGGER.debug("Updated Rova calendar: %s", self.data) diff --git a/requirements_all.txt b/requirements_all.txt index 4b8942681bd..a59f7f5415e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1470,6 +1470,9 @@ rocketchat-API==0.6.1 # homeassistant.components.vacuum.roomba roombapy==1.3.1 +# homeassistant.components.sensor.rova +rova==0.0.2 + # homeassistant.components.switch.rpi_rf # rpi-rf==0.9.7