diff --git a/homeassistant/components/sensor/luftdaten.py b/homeassistant/components/sensor/luftdaten.py index e317e89030febc715c3db928881fa427b652a620..8c5fcc15ec22c086031c13346fa52cc974f32780 100644 --- a/homeassistant/components/sensor/luftdaten.py +++ b/homeassistant/components/sensor/luftdaten.py @@ -5,85 +5,94 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.luftdaten/ """ import asyncio -import json -import logging from datetime import timedelta +import logging -import requests import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( - CONF_NAME, CONF_RESOURCE, CONF_VERIFY_SSL, CONF_MONITORED_CONDITIONS, - TEMP_CELSIUS) -from homeassistant.helpers.entity import Entity + ATTR_ATTRIBUTION, CONF_MONITORED_CONDITIONS, CONF_NAME, TEMP_CELSIUS) +from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity +from homeassistant.util import Throttle + +REQUIREMENTS = ['luftdaten==0.1.1'] _LOGGER = logging.getLogger(__name__) +ATTR_SENSOR_ID = 'sensor_id' + +CONF_ATTRIBUTION = "Data provided by luftdaten.info" + + VOLUME_MICROGRAMS_PER_CUBIC_METER = 'µg/m3' SENSOR_TEMPERATURE = 'temperature' SENSOR_HUMIDITY = 'humidity' SENSOR_PM10 = 'P1' SENSOR_PM2_5 = 'P2' +SENSOR_PRESSURE = 'pressure' SENSOR_TYPES = { SENSOR_TEMPERATURE: ['Temperature', TEMP_CELSIUS], SENSOR_HUMIDITY: ['Humidity', '%'], + SENSOR_PRESSURE: ['Pressure', 'Pa'], SENSOR_PM10: ['PM10', VOLUME_MICROGRAMS_PER_CUBIC_METER], SENSOR_PM2_5: ['PM2.5', VOLUME_MICROGRAMS_PER_CUBIC_METER] } -DEFAULT_NAME = 'Luftdaten Sensor' -DEFAULT_RESOURCE = 'https://api.luftdaten.info/v1/sensor/' -DEFAULT_VERIFY_SSL = True +DEFAULT_NAME = 'Luftdaten' CONF_SENSORID = 'sensorid' -SCAN_INTERVAL = timedelta(minutes=3) +MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_SENSORID): cv.positive_int, vol.Required(CONF_MONITORED_CONDITIONS): vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_RESOURCE, default=DEFAULT_RESOURCE): cv.string, - vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean }) @asyncio.coroutine def async_setup_platform(hass, config, async_add_devices, discovery_info=None): """Set up the Luftdaten sensor.""" + from luftdaten import Luftdaten + name = config.get(CONF_NAME) - sensorid = config.get(CONF_SENSORID) - verify_ssl = config.get(CONF_VERIFY_SSL) + sensor_id = config.get(CONF_SENSORID) - resource = '{}{}/'.format(config.get(CONF_RESOURCE), sensorid) + session = async_get_clientsession(hass) + luftdaten = LuftdatenData(Luftdaten(sensor_id, hass.loop, session)) - rest_client = LuftdatenData(resource, verify_ssl) - rest_client.update() + yield from luftdaten.async_update() - if rest_client.data is None: - _LOGGER.error("Unable to fetch Luftdaten data") - return False + if luftdaten.data is None: + _LOGGER.error("Sensor is not available: %s", sensor_id) + return devices = [] for variable in config[CONF_MONITORED_CONDITIONS]: - devices.append(LuftdatenSensor(rest_client, name, variable)) + if luftdaten.data.values[variable] is None: + _LOGGER.warning("It might be that sensor %s is not providing " + "measurements for %s", sensor_id, variable) + devices.append(LuftdatenSensor(luftdaten, name, variable, sensor_id)) - async_add_devices(devices, True) + async_add_devices(devices) class LuftdatenSensor(Entity): - """Implementation of a LuftdatenSensor sensor.""" + """Implementation of a Luftdaten sensor.""" - def __init__(self, rest_client, name, sensor_type): - """Initialize the LuftdatenSensor sensor.""" - self.rest_client = rest_client + def __init__(self, luftdaten, name, sensor_type, sensor_id): + """Initialize the Luftdaten sensor.""" + self.luftdaten = luftdaten self._name = name self._state = None + self._sensor_id = sensor_id self.sensor_type = sensor_type self._unit_of_measurement = SENSOR_TYPES[sensor_type][1] @@ -95,48 +104,50 @@ class LuftdatenSensor(Entity): @property def state(self): """Return the state of the device.""" - return self._state + return self.luftdaten.data.values[self.sensor_type] @property def unit_of_measurement(self): """Return the unit of measurement of this entity, if any.""" return self._unit_of_measurement - def update(self): - """Get the latest data from REST API and update the state.""" - self.rest_client.update() - value = self.rest_client.data - - if value is None: - self._state = None - else: - parsed_json = json.loads(value) - - log_entries_count = len(parsed_json) - 1 - latest_log_entry = parsed_json[log_entries_count] - sensordata_values = latest_log_entry['sensordatavalues'] - for sensordata_value in sensordata_values: - if sensordata_value['value_type'] == self.sensor_type: - self._state = sensordata_value['value'] + @property + def device_state_attributes(self): + """Return the state attributes.""" + if self.luftdaten.data.meta is None: + return + + attr = { + ATTR_ATTRIBUTION: CONF_ATTRIBUTION, + ATTR_SENSOR_ID: self._sensor_id, + 'lat': self.luftdaten.data.meta['latitude'], + 'long': self.luftdaten.data.meta['longitude'], + } + return attr + + @asyncio.coroutine + def async_update(self): + """Get the latest data from luftdaten.info and update the state.""" + try: + yield from self.luftdaten.async_update() + except TypeError: + pass class LuftdatenData(object): """Class for handling the data retrieval.""" - def __init__(self, resource, verify_ssl): + def __init__(self, data): """Initialize the data object.""" - self._request = requests.Request('GET', resource).prepare() - self._verify_ssl = verify_ssl - self.data = None + self.data = data + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + @asyncio.coroutine + def async_update(self): + """Get the latest data from luftdaten.info.""" + from luftdaten.exceptions import LuftdatenError - def update(self): - """Get the latest data from Luftdaten service.""" try: - with requests.Session() as sess: - response = sess.send( - self._request, timeout=10, verify=self._verify_ssl) - - self.data = response.text - except requests.exceptions.RequestException: - _LOGGER.error("Error fetching data: %s", self._request) - self.data = None + yield from self.data.async_get_data() + except LuftdatenError: + _LOGGER.error("Unable to retrieve data from luftdaten.info") diff --git a/requirements_all.txt b/requirements_all.txt index fc8a4037b8577160c7031f7b51aeca323e71d38d..a5d2936ec367582cbfb785f778565747f9bb5eca 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -444,6 +444,9 @@ liveboxplaytv==2.0.0 # homeassistant.components.notify.lametric lmnotify==0.0.4 +# homeassistant.components.sensor.luftdaten +luftdaten==0.1.1 + # homeassistant.components.sensor.lyft lyft_rides==0.2