Enable config flow for Luftdaten (#17700)

* Move file to new location

* Update requirement

* Enable config flow

* Add luftdaten

* Add tests

* Update

* Add constants

* Changes according to the review comments

* Remove wrong entry from flows

* Fix dict handling

* Add callback and use OrderedDict

* Remve leftover

* Fix

* Remove await
This commit is contained in:
Fabian Affolter 2018-11-06 14:27:52 +01:00 committed by GitHub
parent 7933bd7f91
commit 2e517ab6bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 508 additions and 108 deletions

View file

@ -4,152 +4,120 @@ Support for Luftdaten sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.luftdaten/
"""
from datetime import timedelta
import logging
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.components.luftdaten import (
DATA_LUFTDATEN, DATA_LUFTDATEN_CLIENT, DEFAULT_ATTRIBUTION, DOMAIN,
SENSORS, TOPIC_UPDATE)
from homeassistant.components.luftdaten.const import ATTR_SENSOR_ID
from homeassistant.const import (
ATTR_ATTRIBUTION, ATTR_LATITUDE, ATTR_LONGITUDE, CONF_MONITORED_CONDITIONS,
CONF_NAME, CONF_SHOW_ON_MAP, TEMP_CELSIUS)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
ATTR_ATTRIBUTION, ATTR_LATITUDE, ATTR_LONGITUDE, CONF_SHOW_ON_MAP)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
REQUIREMENTS = ['luftdaten==0.2.0']
_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'
CONF_SENSORID = 'sensorid'
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_SHOW_ON_MAP, default=False): cv.boolean,
})
DEPENDENCIES = ['luftdaten']
async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None):
"""Set up the Luftdaten sensor."""
from luftdaten import Luftdaten
"""Set up an Luftdaten sensor based on existing config."""
pass
name = config.get(CONF_NAME)
show_on_map = config.get(CONF_SHOW_ON_MAP)
sensor_id = config.get(CONF_SENSORID)
session = async_get_clientsession(hass)
luftdaten = LuftdatenData(Luftdaten(sensor_id, hass.loop, session))
async def async_setup_entry(hass, entry, async_add_entities):
"""Set up a Luftdaten sensor based on a config entry."""
luftdaten = hass.data[DOMAIN][DATA_LUFTDATEN_CLIENT][entry.entry_id]
await luftdaten.async_update()
sensors = []
for sensor_type in luftdaten.sensor_conditions:
name, icon, unit = SENSORS[sensor_type]
sensors.append(
LuftdatenSensor(
luftdaten, sensor_type, name, icon, unit,
entry.data[CONF_SHOW_ON_MAP])
)
if luftdaten.data is None:
_LOGGER.error("Sensor is not available: %s", sensor_id)
return
devices = []
for variable in config[CONF_MONITORED_CONDITIONS]:
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, show_on_map))
async_add_entities(devices)
async_add_entities(sensors, True)
class LuftdatenSensor(Entity):
"""Implementation of a Luftdaten sensor."""
def __init__(self, luftdaten, name, sensor_type, sensor_id, show):
def __init__(
self, luftdaten, sensor_type, name, icon, unit, show):
"""Initialize the Luftdaten sensor."""
self._async_unsub_dispatcher_connect = None
self.luftdaten = luftdaten
self._icon = icon
self._name = name
self._state = None
self._sensor_id = sensor_id
self._data = None
self.sensor_type = sensor_type
self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
self._unit_of_measurement = unit
self._show_on_map = show
self._attrs = {}
@property
def name(self):
"""Return the name of the sensor."""
return '{} {}'.format(self._name, SENSOR_TYPES[self.sensor_type][0])
def icon(self):
"""Return the icon."""
return self._icon
@property
def state(self):
"""Return the state of the device."""
return self.luftdaten.data.values[self.sensor_type]
return self._data[self.sensor_type]
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
return self._unit_of_measurement
@property
def should_poll(self):
"""Disable polling."""
return False
@property
def unique_id(self) -> str:
"""Return a unique, friendly identifier for this entity."""
return '{0}_{1}'.format(self._data['sensor_id'], self.sensor_type)
@property
def device_state_attributes(self):
"""Return the state attributes."""
onmap = ATTR_LATITUDE, ATTR_LONGITUDE
nomap = 'lat', 'long'
lat_format, lon_format = onmap if self._show_on_map else nomap
self._attrs[ATTR_SENSOR_ID] = self._data['sensor_id']
self._attrs[ATTR_ATTRIBUTION] = DEFAULT_ATTRIBUTION
on_map = ATTR_LATITUDE, ATTR_LONGITUDE
no_map = 'lat', 'long'
lat_format, lon_format = on_map if self._show_on_map else no_map
try:
attr = {
ATTR_ATTRIBUTION: CONF_ATTRIBUTION,
ATTR_SENSOR_ID: self._sensor_id,
lat_format: self.luftdaten.data.meta['latitude'],
lon_format: self.luftdaten.data.meta['longitude'],
}
return attr
self._attrs[lon_format] = self._data['longitude']
self._attrs[lat_format] = self._data['latitude']
return self._attrs
except KeyError:
return
async def async_added_to_hass(self):
"""Register callbacks."""
@callback
def update():
"""Update the state."""
self.async_schedule_update_ha_state(True)
self._async_unsub_dispatcher_connect = async_dispatcher_connect(
self.hass, TOPIC_UPDATE, update)
async def async_will_remove_from_hass(self):
"""Disconnect dispatcher listener when removed."""
if self._async_unsub_dispatcher_connect:
self._async_unsub_dispatcher_connect()
async def async_update(self):
"""Get the latest data from luftdaten.info and update the state."""
await self.luftdaten.async_update()
class LuftdatenData:
"""Class for handling the data retrieval."""
def __init__(self, data):
"""Initialize the data object."""
self.data = data
@Throttle(MIN_TIME_BETWEEN_UPDATES)
async def async_update(self):
"""Get the latest data from luftdaten.info."""
from luftdaten.exceptions import LuftdatenError
"""Get the latest data and update the state."""
try:
await self.data.async_get_data()
except LuftdatenError:
_LOGGER.error("Unable to retrieve data from luftdaten.info")
self._data = self.luftdaten.data[DATA_LUFTDATEN]
except KeyError:
return