diff --git a/.coveragerc b/.coveragerc index 1987dd0b63f290..1846a9dea67bfb 100644 --- a/.coveragerc +++ b/.coveragerc @@ -427,6 +427,7 @@ omit = homeassistant/components/spider.py homeassistant/components/*/spider.py + homeassistant/components/air_pollutants/opensensemap.py homeassistant/components/alarm_control_panel/alarmdotcom.py homeassistant/components/alarm_control_panel/canary.py homeassistant/components/alarm_control_panel/concord232.py diff --git a/homeassistant/components/air_pollutants/opensensemap.py b/homeassistant/components/air_pollutants/opensensemap.py new file mode 100644 index 00000000000000..ae4625bbbe94b6 --- /dev/null +++ b/homeassistant/components/air_pollutants/opensensemap.py @@ -0,0 +1,105 @@ +""" +Support for openSenseMap Air Pollutants data. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/air_pollutants_opensensemap/ +""" +from datetime import timedelta +import logging + +import voluptuous as vol + +from homeassistant.components.air_pollutants import ( + PLATFORM_SCHEMA, AirPollutantsEntity) +from homeassistant.const import CONF_NAME +from homeassistant.helpers.aiohttp_client import async_get_clientsession +import homeassistant.helpers.config_validation as cv +from homeassistant.util import Throttle + +REQUIREMENTS = ['opensensemap-api==0.1.3'] + +_LOGGER = logging.getLogger(__name__) + +ATTRIBUTION = 'Data provided by openSenseMap' + +CONF_STATION_ID = 'station_id' + +SCAN_INTERVAL = timedelta(minutes=10) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_STATION_ID): cv.string, + vol.Optional(CONF_NAME): cv.string, +}) + + +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Set up the openSenseMap air pollutants platform.""" + from opensensemap_api import OpenSenseMap + + name = config.get(CONF_NAME) + station_id = config[CONF_STATION_ID] + + session = async_get_clientsession(hass) + osm_api = OpenSenseMapData(OpenSenseMap(station_id, hass.loop, session)) + + await osm_api.async_update() + + if 'name' not in osm_api.api.data: + _LOGGER.error("Station %s is not available", station_id) + return + + station_name = osm_api.api.data['name'] if name is None else name + + async_add_entities([OpenSenseMapPollutants(station_name, osm_api)], True) + + +class OpenSenseMapPollutants(AirPollutantsEntity): + """Implementation of an openSenseMap air pollutants entity.""" + + def __init__(self, name, osm): + """Initialize the air pollutants entity.""" + self._name = name + self._osm = osm + + @property + def name(self): + """Return the name of the air pollutants entity.""" + return self._name + + @property + def particulate_matter_2_5(self): + """Return the particulate matter 2.5 level.""" + return self._osm.api.pm2_5 + + @property + def particulate_matter_10(self): + """Return the particulate matter 10 level.""" + return self._osm.api.pm10 + + @property + def attribution(self): + """Return the attribution.""" + return ATTRIBUTION + + async def async_update(self): + """Get the latest data from the openSenseMap API.""" + await self._osm.async_update() + + +class OpenSenseMapData: + """Get the latest data and update the states.""" + + def __init__(self, api): + """Initialize the data object.""" + self.api = api + + @Throttle(SCAN_INTERVAL) + async def async_update(self): + """Get the latest data from the Pi-hole.""" + from opensensemap_api.exceptions import OpenSenseMapError + + try: + await self.api.get_data() + except OpenSenseMapError as err: + _LOGGER.error("Unable to fetch data: %s", err) diff --git a/requirements_all.txt b/requirements_all.txt index f4bc4d25825b78..3be50b33371138 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -725,6 +725,9 @@ openevsewifi==0.4 # homeassistant.components.media_player.openhome openhomedevice==0.4.2 +# homeassistant.components.air_pollutants.opensensemap +opensensemap-api==0.1.3 + # homeassistant.components.switch.orvibo orvibo==1.1.1