Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for sensors from Flu Near You #18136

Merged
merged 4 commits into from Nov 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .coveragerc
Expand Up @@ -707,6 +707,7 @@ omit =
homeassistant/components/sensor/fints.py
homeassistant/components/sensor/fitbit.py
homeassistant/components/sensor/fixer.py
homeassistant/components/sensor/flunearyou.py
homeassistant/components/sensor/folder.py
homeassistant/components/sensor/foobot.py
homeassistant/components/sensor/fritzbox_callmonitor.py
Expand Down
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Expand Up @@ -102,6 +102,7 @@ homeassistant/components/sensor/darksky.py @fabaff
homeassistant/components/sensor/file.py @fabaff
homeassistant/components/sensor/filter.py @dgomes
homeassistant/components/sensor/fixer.py @fabaff
homeassistant/components/sensor/flunearyou.py.py @bachya
homeassistant/components/sensor/gearbest.py @HerrHofrat
homeassistant/components/sensor/gitter.py @fabaff
homeassistant/components/sensor/glances.py @fabaff
Expand Down
213 changes: 213 additions & 0 deletions homeassistant/components/sensor/flunearyou.py
@@ -0,0 +1,213 @@
"""
Support for user- and CDC-based flu info sensors from Flu Near You.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.flunearyou/
"""
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

REQUIREMENTS = ['pyflunearyou==0.0.2']
_LOGGER = logging.getLogger(__name__)

ATTR_CITY = 'city'
ATTR_REPORTED_DATE = 'reported_date'
ATTR_REPORTED_LATITUDE = 'reported_latitude'
ATTR_REPORTED_LONGITUDE = 'reported_longitude'
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_NONE = 'none'
TYPE_USER_SYMPTOMS = 'symptoms'
TYPE_USER_TOTAL = 'total'

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_NONE, '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 create_client
from pyflunearyou.errors import FluNearYouError

websession = aiohttp_client.async_get_clientsession(hass)

latitude = config.get(CONF_LATITUDE, hass.config.latitude)
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
identifier = '{0},{1}'.format(latitude, longitude)

try:
client = await create_client(latitude, longitude, websession)
except FluNearYouError as err:
_LOGGER.error('There was an error while setting up: %s', err)
return

fny = FluNearYouData(client, config[CONF_MONITORED_CONDITIONS])
await fny.async_update()

sensors = [
FluNearYouSensor(fny, kind, name, identifier, 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, identifier, category, icon, unit):
"""Initialize the sensor."""
self._attrs = {ATTR_ATTRIBUTION: DEFAULT_ATTRIBUTION}
self._category = category
self._icon = icon
self._identifier = identifier
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}'.format(self._identifier, 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['city'].split('(')[0],
ATTR_REPORTED_LATITUDE: user_data['latitude'],
ATTR_REPORTED_LONGITUDE: user_data['longitude'],
ATTR_ZIP_CODE: user_data['zip'],
})

if self._kind == TYPE_USER_TOTAL:
self._state = sum(
v for k, v in user_data.items() if k in (
TYPE_USER_CHICK, TYPE_USER_DENGUE, TYPE_USER_FLU,
TYPE_USER_LEPTO, TYPE_USER_SYMPTOMS))
else:
self._state = user_data[self._kind]


class FluNearYouData:
"""Define a data object to retrieve info from Flu Near You."""

def __init__(self, client, sensor_types):
"""Initialize."""
self._client = client
self._sensor_types = sensor_types
self.data = {}

async def _get_data(self, category, method):
"""Get data for a specific category."""
from pyflunearyou.errors import FluNearYouError

try:
self.data[category] = await method()
except FluNearYouError as err:
_LOGGER.error(
'There was an error with "%s" data: %s', category, err)
self.data[category] = {}

@Throttle(MIN_TIME_BETWEEN_UPDATES)
async def async_update(self):
"""Update Flu Near You data."""
if CATEGORY_CDC_REPORT in self._sensor_types:
await self._get_data(
CATEGORY_CDC_REPORT, self._client.cdc_reports.status)

if CATEGORY_USER_REPORT in self._sensor_types:
await self._get_data(
CATEGORY_USER_REPORT, self._client.user_reports.status)

_LOGGER.debug('New data stored: %s', self.data)
3 changes: 3 additions & 0 deletions requirements_all.txt
Expand Up @@ -921,6 +921,9 @@ pyflexit==0.3
# homeassistant.components.binary_sensor.flic
pyflic-homeassistant==0.4.dev0

# homeassistant.components.sensor.flunearyou
pyflunearyou==0.0.2

# homeassistant.components.light.futurenow
pyfnip==0.2

Expand Down