core/homeassistant/components/flunearyou/sensor.py

219 lines
7.3 KiB
Python

"""Support for user- and CDC-based flu info sensors from Flu Near You."""
import logging
from datetime import timedelta
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (
ATTR_ATTRIBUTION, ATTR_STATE, CONF_LATITUDE, CONF_MONITORED_CONDITIONS,
CONF_LONGITUDE)
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
_LOGGER = logging.getLogger(__name__)
ATTR_CITY = 'city'
ATTR_REPORTED_DATE = 'reported_date'
ATTR_REPORTED_LATITUDE = 'reported_latitude'
ATTR_REPORTED_LONGITUDE = 'reported_longitude'
ATTR_STATE_REPORTS_LAST_WEEK = 'state_reports_last_week'
ATTR_STATE_REPORTS_THIS_WEEK = 'state_reports_this_week'
ATTR_ZIP_CODE = 'zip_code'
DEFAULT_ATTRIBUTION = 'Data provided by Flu Near You'
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10)
SCAN_INTERVAL = timedelta(minutes=30)
CATEGORY_CDC_REPORT = 'cdc_report'
CATEGORY_USER_REPORT = 'user_report'
TYPE_CDC_LEVEL = 'level'
TYPE_CDC_LEVEL2 = 'level2'
TYPE_USER_CHICK = 'chick'
TYPE_USER_DENGUE = 'dengue'
TYPE_USER_FLU = 'flu'
TYPE_USER_LEPTO = 'lepto'
TYPE_USER_NO_SYMPTOMS = 'none'
TYPE_USER_SYMPTOMS = 'symptoms'
TYPE_USER_TOTAL = 'total'
EXTENDED_TYPE_MAPPING = {
TYPE_USER_FLU: 'ili',
TYPE_USER_NO_SYMPTOMS: 'no_symptoms',
TYPE_USER_TOTAL: 'total_surveys',
}
SENSORS = {
CATEGORY_CDC_REPORT: [
(TYPE_CDC_LEVEL, 'CDC Level', 'mdi:biohazard', None),
(TYPE_CDC_LEVEL2, 'CDC Level 2', 'mdi:biohazard', None),
],
CATEGORY_USER_REPORT: [
(TYPE_USER_CHICK, 'Avian Flu Symptoms', 'mdi:alert', 'reports'),
(TYPE_USER_DENGUE, 'Dengue Fever Symptoms', 'mdi:alert', 'reports'),
(TYPE_USER_FLU, 'Flu Symptoms', 'mdi:alert', 'reports'),
(TYPE_USER_LEPTO, 'Leptospirosis Symptoms', 'mdi:alert', 'reports'),
(TYPE_USER_NO_SYMPTOMS, 'No Symptoms', 'mdi:alert', 'reports'),
(TYPE_USER_SYMPTOMS, 'Flu-like Symptoms', 'mdi:alert', 'reports'),
(TYPE_USER_TOTAL, 'Total Symptoms', 'mdi:alert', 'reports'),
]
}
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_LATITUDE): cv.latitude,
vol.Optional(CONF_LONGITUDE): cv.longitude,
vol.Required(CONF_MONITORED_CONDITIONS, default=list(SENSORS)):
vol.All(cv.ensure_list, [vol.In(SENSORS)])
})
async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None):
"""Configure the platform and add the sensors."""
from pyflunearyou import Client
websession = aiohttp_client.async_get_clientsession(hass)
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
fny = FluNearYouData(
Client(websession), latitude, longitude,
config[CONF_MONITORED_CONDITIONS])
await fny.async_update()
sensors = [
FluNearYouSensor(fny, kind, name, category, icon, unit)
for category in config[CONF_MONITORED_CONDITIONS]
for kind, name, icon, unit in SENSORS[category]
]
async_add_entities(sensors, True)
class FluNearYouSensor(Entity):
"""Define a base Flu Near You sensor."""
def __init__(self, fny, kind, name, category, icon, unit):
"""Initialize the sensor."""
self._attrs = {ATTR_ATTRIBUTION: DEFAULT_ATTRIBUTION}
self._category = category
self._icon = icon
self._kind = kind
self._name = name
self._state = None
self._unit = unit
self.fny = fny
@property
def available(self):
"""Return True if entity is available."""
return bool(self.fny.data[self._category])
@property
def device_state_attributes(self):
"""Return the device state attributes."""
return self._attrs
@property
def icon(self):
"""Return the icon."""
return self._icon
@property
def name(self):
"""Return the name."""
return self._name
@property
def state(self):
"""Return the state."""
return self._state
@property
def unique_id(self):
"""Return a unique, HASS-friendly identifier for this entity."""
return '{0},{1}_{2}'.format(
self.fny.latitude, self.fny.longitude, self._kind)
@property
def unit_of_measurement(self):
"""Return the unit the value is expressed in."""
return self._unit
async def async_update(self):
"""Update the sensor."""
await self.fny.async_update()
cdc_data = self.fny.data.get(CATEGORY_CDC_REPORT)
user_data = self.fny.data.get(CATEGORY_USER_REPORT)
if self._category == CATEGORY_CDC_REPORT and cdc_data:
self._attrs.update({
ATTR_REPORTED_DATE: cdc_data['week_date'],
ATTR_STATE: cdc_data['name'],
})
self._state = cdc_data[self._kind]
elif self._category == CATEGORY_USER_REPORT and user_data:
self._attrs.update({
ATTR_CITY: user_data['local']['city'].split('(')[0],
ATTR_REPORTED_LATITUDE: user_data['local']['latitude'],
ATTR_REPORTED_LONGITUDE: user_data['local']['longitude'],
ATTR_STATE: user_data['state']['name'],
ATTR_ZIP_CODE: user_data['local']['zip'],
})
if self._kind in user_data['state']['data']:
states_key = self._kind
elif self._kind in EXTENDED_TYPE_MAPPING:
states_key = EXTENDED_TYPE_MAPPING[self._kind]
self._attrs[ATTR_STATE_REPORTS_THIS_WEEK] = user_data['state'][
'data'][states_key]
self._attrs[ATTR_STATE_REPORTS_LAST_WEEK] = user_data['state'][
'last_week_data'][states_key]
if self._kind == TYPE_USER_TOTAL:
self._state = sum(
v for k, v in user_data['local'].items() if k in (
TYPE_USER_CHICK, TYPE_USER_DENGUE, TYPE_USER_FLU,
TYPE_USER_LEPTO, TYPE_USER_SYMPTOMS))
else:
self._state = user_data['local'][self._kind]
class FluNearYouData:
"""Define a data object to retrieve info from Flu Near You."""
def __init__(self, client, latitude, longitude, sensor_types):
"""Initialize."""
self._client = client
self._sensor_types = sensor_types
self.data = {}
self.latitude = latitude
self.longitude = longitude
@Throttle(MIN_TIME_BETWEEN_UPDATES)
async def async_update(self):
"""Update Flu Near You data."""
from pyflunearyou.errors import FluNearYouError
for key, method in [(CATEGORY_CDC_REPORT,
self._client.cdc_reports.status_by_coordinates),
(CATEGORY_USER_REPORT,
self._client.user_reports.status_by_coordinates)]:
if key in self._sensor_types:
try:
self.data[key] = await method(
self.latitude, self.longitude)
except FluNearYouError as err:
_LOGGER.error(
'There was an error with "%s" data: %s', key, err)
self.data[key] = {}
_LOGGER.debug('New data stored: %s', self.data)