""" Support for the World Air Quality Index service. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.waqi/ """ import asyncio import logging from datetime import timedelta import aiohttp import voluptuous as vol from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv from homeassistant.const import ( ATTR_ATTRIBUTION, ATTR_TIME, ATTR_TEMPERATURE, CONF_TOKEN) from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.config_validation import PLATFORM_SCHEMA from homeassistant.helpers.entity import Entity REQUIREMENTS = ['waqiasync==1.0.0'] _LOGGER = logging.getLogger(__name__) ATTR_DOMINENTPOL = 'dominentpol' ATTR_HUMIDITY = 'humidity' ATTR_NITROGEN_DIOXIDE = 'nitrogen_dioxide' ATTR_OZONE = 'ozone' ATTR_PM10 = 'pm_10' ATTR_PM2_5 = 'pm_2_5' ATTR_PRESSURE = 'pressure' ATTR_SULFUR_DIOXIDE = 'sulfur_dioxide' KEY_TO_ATTR = { 'pm25': ATTR_PM2_5, 'pm10': ATTR_PM10, 'h': ATTR_HUMIDITY, 'p': ATTR_PRESSURE, 't': ATTR_TEMPERATURE, 'o3': ATTR_OZONE, 'no2': ATTR_NITROGEN_DIOXIDE, 'so2': ATTR_SULFUR_DIOXIDE, } ATTRIBUTION = 'Data provided by the World Air Quality Index project' CONF_LOCATIONS = 'locations' CONF_STATIONS = 'stations' SCAN_INTERVAL = timedelta(minutes=5) TIMEOUT = 10 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_STATIONS): cv.ensure_list, vol.Required(CONF_TOKEN): cv.string, vol.Required(CONF_LOCATIONS): cv.ensure_list, }) @asyncio.coroutine def async_setup_platform(hass, config, async_add_devices, discovery_info=None): """Set up the requested World Air Quality Index locations.""" import waqiasync token = config.get(CONF_TOKEN) station_filter = config.get(CONF_STATIONS) locations = config.get(CONF_LOCATIONS) client = waqiasync.WaqiClient( token, async_get_clientsession(hass), timeout=TIMEOUT) dev = [] try: for location_name in locations: stations = yield from client.search(location_name) _LOGGER.debug("The following stations were returned: %s", stations) for station in stations: waqi_sensor = WaqiSensor(client, station) if not station_filter or \ {waqi_sensor.uid, waqi_sensor.url, waqi_sensor.station_name} & set(station_filter): dev.append(waqi_sensor) except (aiohttp.client_exceptions.ClientConnectorError, asyncio.TimeoutError): _LOGGER.exception('Failed to connct to WAQI servers.') raise PlatformNotReady async_add_devices(dev, True) class WaqiSensor(Entity): """Implementation of a WAQI sensor.""" def __init__(self, client, station): """Initialize the sensor.""" self._client = client try: self.uid = station['uid'] except (KeyError, TypeError): self.uid = None try: self.url = station['station']['url'] except (KeyError, TypeError): self.url = None try: self.station_name = station['station']['name'] except (KeyError, TypeError): self.station_name = None self._data = None @property def name(self): """Return the name of the sensor.""" if self.station_name: return 'WAQI {}'.format(self.station_name) return 'WAQI {}'.format(self.url if self.url else self.uid) @property def icon(self): """Icon to use in the frontend, if any.""" return 'mdi:cloud' @property def state(self): """Return the state of the device.""" if self._data is not None: return self._data.get('aqi') return None @property def unit_of_measurement(self): """Return the unit of measurement of this entity, if any.""" return 'AQI' @property def device_state_attributes(self): """Return the state attributes of the last update.""" attrs = {} if self._data is not None: try: attrs[ATTR_ATTRIBUTION] = ' and '.join( [ATTRIBUTION] + [ v['name'] for v in self._data.get('attributions', [])]) attrs[ATTR_TIME] = self._data['time']['s'] attrs[ATTR_DOMINENTPOL] = self._data.get('dominentpol') iaqi = self._data['iaqi'] for key in iaqi: if key in KEY_TO_ATTR: attrs[KEY_TO_ATTR[key]] = iaqi[key]['v'] else: attrs[key] = iaqi[key]['v'] return attrs except (IndexError, KeyError): return {ATTR_ATTRIBUTION: ATTRIBUTION} @asyncio.coroutine def async_update(self): """Get the latest data and updates the states.""" if self.uid: result = yield from self._client.get_station_by_number(self.uid) elif self.url: result = yield from self._client.get_station_by_name(self.url) else: result = None self._data = result