Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
414 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
""" | ||
This platform provides binary sensors for OpenUV data. | ||
For more details about this platform, please refer to the documentation at | ||
https://home-assistant.io/components/binary_sensor.openuv/ | ||
""" | ||
import logging | ||
|
||
from homeassistant.components.binary_sensor import BinarySensorDevice | ||
from homeassistant.const import CONF_MONITORED_CONDITIONS | ||
from homeassistant.core import callback | ||
from homeassistant.helpers.dispatcher import async_dispatcher_connect | ||
from homeassistant.components.openuv import ( | ||
BINARY_SENSORS, DATA_PROTECTION_WINDOW, DOMAIN, TOPIC_UPDATE, | ||
TYPE_PROTECTION_WINDOW, OpenUvEntity) | ||
from homeassistant.util.dt import as_local, parse_datetime, utcnow | ||
|
||
DEPENDENCIES = ['openuv'] | ||
_LOGGER = logging.getLogger(__name__) | ||
|
||
ATTR_PROTECTION_WINDOW_STARTING_TIME = 'start_time' | ||
ATTR_PROTECTION_WINDOW_STARTING_UV = 'start_uv' | ||
ATTR_PROTECTION_WINDOW_ENDING_TIME = 'end_time' | ||
ATTR_PROTECTION_WINDOW_ENDING_UV = 'end_uv' | ||
|
||
|
||
async def async_setup_platform( | ||
hass, config, async_add_devices, discovery_info=None): | ||
"""Set up the OpenUV binary sensor platform.""" | ||
if discovery_info is None: | ||
return | ||
|
||
openuv = hass.data[DOMAIN] | ||
|
||
binary_sensors = [] | ||
for sensor_type in discovery_info[CONF_MONITORED_CONDITIONS]: | ||
name, icon = BINARY_SENSORS[sensor_type] | ||
binary_sensors.append( | ||
OpenUvBinarySensor(openuv, sensor_type, name, icon)) | ||
|
||
async_add_devices(binary_sensors, True) | ||
|
||
|
||
class OpenUvBinarySensor(OpenUvEntity, BinarySensorDevice): | ||
"""Define a binary sensor for OpenUV.""" | ||
|
||
def __init__(self, openuv, sensor_type, name, icon): | ||
"""Initialize the sensor.""" | ||
super().__init__(openuv) | ||
|
||
self._icon = icon | ||
self._latitude = openuv.client.latitude | ||
self._longitude = openuv.client.longitude | ||
self._name = name | ||
self._sensor_type = sensor_type | ||
self._state = None | ||
|
||
@property | ||
def icon(self): | ||
"""Return the icon.""" | ||
return self._icon | ||
|
||
@property | ||
def is_on(self): | ||
"""Return the status of the sensor.""" | ||
return self._state | ||
|
||
@property | ||
def should_poll(self): | ||
"""Disable polling.""" | ||
return False | ||
|
||
@property | ||
def unique_id(self) -> str: | ||
"""Return a unique, HASS-friendly identifier for this entity.""" | ||
return '{0}_{1}_{2}'.format( | ||
self._latitude, self._longitude, self._sensor_type) | ||
|
||
@callback | ||
def _update_data(self): | ||
"""Update the state.""" | ||
self.async_schedule_update_ha_state(True) | ||
|
||
async def async_added_to_hass(self): | ||
"""Register callbacks.""" | ||
async_dispatcher_connect( | ||
self.hass, TOPIC_UPDATE, self._update_data) | ||
|
||
async def async_update(self): | ||
"""Update the state.""" | ||
data = self.openuv.data[DATA_PROTECTION_WINDOW]['result'] | ||
if self._sensor_type == TYPE_PROTECTION_WINDOW: | ||
self._state = parse_datetime( | ||
data['from_time']) <= utcnow() <= parse_datetime( | ||
data['to_time']) | ||
self._attrs.update({ | ||
ATTR_PROTECTION_WINDOW_ENDING_TIME: | ||
as_local(parse_datetime(data['to_time'])), | ||
ATTR_PROTECTION_WINDOW_ENDING_UV: data['to_uv'], | ||
ATTR_PROTECTION_WINDOW_STARTING_UV: data['from_uv'], | ||
ATTR_PROTECTION_WINDOW_STARTING_TIME: | ||
as_local(parse_datetime(data['from_time'])), | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
""" | ||
Support for data from openuv.io. | ||
For more details about this component, please refer to the documentation at | ||
https://home-assistant.io/components/openuv/ | ||
""" | ||
import logging | ||
from datetime import timedelta | ||
|
||
import voluptuous as vol | ||
|
||
from homeassistant.const import ( | ||
ATTR_ATTRIBUTION, CONF_API_KEY, CONF_BINARY_SENSORS, CONF_ELEVATION, | ||
CONF_LATITUDE, CONF_LONGITUDE, CONF_MONITORED_CONDITIONS, | ||
CONF_SCAN_INTERVAL, CONF_SENSORS) | ||
from homeassistant.helpers import ( | ||
aiohttp_client, config_validation as cv, discovery) | ||
from homeassistant.helpers.dispatcher import async_dispatcher_send | ||
from homeassistant.helpers.entity import Entity | ||
from homeassistant.helpers.event import async_track_time_interval | ||
|
||
REQUIREMENTS = ['pyopenuv==1.0.1'] | ||
_LOGGER = logging.getLogger(__name__) | ||
|
||
DOMAIN = 'openuv' | ||
|
||
DATA_PROTECTION_WINDOW = 'protection_window' | ||
DATA_UV = 'uv' | ||
|
||
DEFAULT_ATTRIBUTION = 'Data provided by OpenUV' | ||
DEFAULT_SCAN_INTERVAL = timedelta(minutes=30) | ||
|
||
NOTIFICATION_ID = 'openuv_notification' | ||
NOTIFICATION_TITLE = 'OpenUV Component Setup' | ||
|
||
TOPIC_UPDATE = '{0}_data_update'.format(DOMAIN) | ||
|
||
TYPE_CURRENT_OZONE_LEVEL = 'current_ozone_level' | ||
TYPE_CURRENT_UV_INDEX = 'current_uv_index' | ||
TYPE_MAX_UV_INDEX = 'max_uv_index' | ||
TYPE_PROTECTION_WINDOW = 'uv_protection_window' | ||
TYPE_SAFE_EXPOSURE_TIME_1 = 'safe_exposure_time_type_1' | ||
TYPE_SAFE_EXPOSURE_TIME_2 = 'safe_exposure_time_type_2' | ||
TYPE_SAFE_EXPOSURE_TIME_3 = 'safe_exposure_time_type_3' | ||
TYPE_SAFE_EXPOSURE_TIME_4 = 'safe_exposure_time_type_4' | ||
TYPE_SAFE_EXPOSURE_TIME_5 = 'safe_exposure_time_type_5' | ||
TYPE_SAFE_EXPOSURE_TIME_6 = 'safe_exposure_time_type_6' | ||
|
||
BINARY_SENSORS = { | ||
TYPE_PROTECTION_WINDOW: ('Protection Window', 'mdi:sunglasses') | ||
} | ||
|
||
BINARY_SENSOR_SCHEMA = vol.Schema({ | ||
vol.Optional(CONF_MONITORED_CONDITIONS, default=list(BINARY_SENSORS)): | ||
vol.All(cv.ensure_list, [vol.In(BINARY_SENSORS)]) | ||
}) | ||
|
||
SENSORS = { | ||
TYPE_CURRENT_OZONE_LEVEL: ( | ||
'Current Ozone Level', 'mdi:vector-triangle', 'du'), | ||
TYPE_CURRENT_UV_INDEX: ('Current UV Index', 'mdi:weather-sunny', 'index'), | ||
TYPE_MAX_UV_INDEX: ('Max UV Index', 'mdi:weather-sunny', 'index'), | ||
TYPE_SAFE_EXPOSURE_TIME_1: ( | ||
'Skin Type 1 Safe Exposure Time', 'mdi:timer', 'minutes'), | ||
TYPE_SAFE_EXPOSURE_TIME_2: ( | ||
'Skin Type 2 Safe Exposure Time', 'mdi:timer', 'minutes'), | ||
TYPE_SAFE_EXPOSURE_TIME_3: ( | ||
'Skin Type 3 Safe Exposure Time', 'mdi:timer', 'minutes'), | ||
TYPE_SAFE_EXPOSURE_TIME_4: ( | ||
'Skin Type 4 Safe Exposure Time', 'mdi:timer', 'minutes'), | ||
TYPE_SAFE_EXPOSURE_TIME_5: ( | ||
'Skin Type 5 Safe Exposure Time', 'mdi:timer', 'minutes'), | ||
TYPE_SAFE_EXPOSURE_TIME_6: ( | ||
'Skin Type 6 Safe Exposure Time', 'mdi:timer', 'minutes'), | ||
} | ||
|
||
SENSOR_SCHEMA = vol.Schema({ | ||
vol.Optional(CONF_MONITORED_CONDITIONS, default=list(SENSORS)): | ||
vol.All(cv.ensure_list, [vol.In(SENSORS)]) | ||
}) | ||
|
||
CONFIG_SCHEMA = vol.Schema({ | ||
DOMAIN: vol.Schema({ | ||
vol.Required(CONF_API_KEY): cv.string, | ||
vol.Optional(CONF_ELEVATION): float, | ||
vol.Optional(CONF_LATITUDE): cv.latitude, | ||
vol.Optional(CONF_LONGITUDE): cv.longitude, | ||
vol.Optional(CONF_BINARY_SENSORS, default={}): BINARY_SENSOR_SCHEMA, | ||
vol.Optional(CONF_SENSORS, default={}): SENSOR_SCHEMA, | ||
vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): | ||
cv.time_period, | ||
}) | ||
}, extra=vol.ALLOW_EXTRA) | ||
|
||
|
||
async def async_setup(hass, config): | ||
"""Set up the OpenUV component.""" | ||
from pyopenuv import Client | ||
from pyopenuv.errors import OpenUvError | ||
|
||
conf = config[DOMAIN] | ||
api_key = conf[CONF_API_KEY] | ||
elevation = conf.get(CONF_ELEVATION, hass.config.elevation) | ||
latitude = conf.get(CONF_LATITUDE, hass.config.latitude) | ||
longitude = conf.get(CONF_LONGITUDE, hass.config.longitude) | ||
|
||
try: | ||
websession = aiohttp_client.async_get_clientsession(hass) | ||
openuv = OpenUV( | ||
Client( | ||
api_key, latitude, longitude, websession, altitude=elevation), | ||
conf[CONF_BINARY_SENSORS][CONF_MONITORED_CONDITIONS] + | ||
conf[CONF_SENSORS][CONF_MONITORED_CONDITIONS]) | ||
await openuv.async_update() | ||
hass.data[DOMAIN] = openuv | ||
except OpenUvError as err: | ||
_LOGGER.error('An error occurred: %s', str(err)) | ||
hass.components.persistent_notification.create( | ||
'Error: {0}<br />' | ||
'You will need to restart hass after fixing.' | ||
''.format(err), | ||
title=NOTIFICATION_TITLE, | ||
notification_id=NOTIFICATION_ID) | ||
return False | ||
|
||
for component, schema in [ | ||
('binary_sensor', conf[CONF_BINARY_SENSORS]), | ||
('sensor', conf[CONF_SENSORS]), | ||
]: | ||
hass.async_create_task( | ||
discovery.async_load_platform( | ||
hass, component, DOMAIN, schema, config)) | ||
|
||
async def refresh_sensors(event_time): | ||
"""Refresh OpenUV data.""" | ||
_LOGGER.debug('Refreshing OpenUV data') | ||
await openuv.async_update() | ||
async_dispatcher_send(hass, TOPIC_UPDATE) | ||
|
||
async_track_time_interval(hass, refresh_sensors, conf[CONF_SCAN_INTERVAL]) | ||
|
||
return True | ||
|
||
|
||
class OpenUV: | ||
"""Define a generic OpenUV object.""" | ||
|
||
def __init__(self, client, monitored_conditions): | ||
"""Initialize.""" | ||
self._monitored_conditions = monitored_conditions | ||
self.client = client | ||
self.data = {} | ||
|
||
async def async_update(self): | ||
"""Update sensor/binary sensor data.""" | ||
if TYPE_PROTECTION_WINDOW in self._monitored_conditions: | ||
data = await self.client.uv_protection_window() | ||
self.data[DATA_PROTECTION_WINDOW] = data | ||
|
||
if any(c in self._monitored_conditions for c in SENSORS): | ||
data = await self.client.uv_index() | ||
self.data[DATA_UV] = data | ||
|
||
|
||
class OpenUvEntity(Entity): | ||
"""Define a generic OpenUV entity.""" | ||
|
||
def __init__(self, openuv): | ||
"""Initialize.""" | ||
self._attrs = {ATTR_ATTRIBUTION: DEFAULT_ATTRIBUTION} | ||
self._name = None | ||
self.openuv = openuv | ||
|
||
@property | ||
def device_state_attributes(self): | ||
"""Return the state attributes.""" | ||
return self._attrs | ||
|
||
@property | ||
def name(self): | ||
"""Return the name of the entity.""" | ||
return self._name |
Oops, something went wrong.