236 lines
7.5 KiB
Python
236 lines
7.5 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)
|