-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsensor.py
More file actions
164 lines (132 loc) · 4.91 KB
/
sensor.py
File metadata and controls
164 lines (132 loc) · 4.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
"""Support for the Environment Canada weather service."""
from datetime import datetime, timedelta
import logging
import re
import async_timeout
from env_canada import ECWeather # pylint: disable=import-error
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
from homeassistant.const import (
ATTR_ATTRIBUTION,
ATTR_LOCATION,
ATTR_TIME,
CONF_LATITUDE,
CONF_LONGITUDE,
)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from .const import (
ATTRIBUTION,
ATTR_ICON,
ATTR_UNIT,
SENSOR_TYPES
)
_LOGGER = logging.getLogger(__name__)
ATTR_UPDATED = "updated"
ATTR_STATION = "station"
CONF_STATION = "station"
CONF_LANGUAGE = "language"
def validate_station(station):
"""Check that the station ID is well-formed."""
if station is None:
return
if not re.fullmatch(r"[A-Z]{2}/s0000\d{3}", station):
raise vol.error.Invalid('Station ID must be of the form "XX/s0000###"')
return station
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_LANGUAGE, default="english"): vol.In(["english", "french"]),
vol.Optional(CONF_STATION): validate_station,
vol.Inclusive(CONF_LATITUDE, "latlon"): cv.latitude,
vol.Inclusive(CONF_LONGITUDE, "latlon"): cv.longitude,
}
)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the Environment Canada sensor."""
if config.get(CONF_STATION):
ec_data = ECWeather(
station_id=config[CONF_STATION], language=config.get(CONF_LANGUAGE)
)
else:
lat = config.get(CONF_LATITUDE, hass.config.latitude)
lon = config.get(CONF_LONGITUDE, hass.config.longitude)
ec_data = ECWeather(coordinates=(lat, lon), language=config.get(CONF_LANGUAGE))
async def async_update_data():
"""Fetch data from Environment Canada."""
async with async_timeout.timeout(10):
await ec_data.update()
ec_data.conditions.update(ec_data.alerts)
return ec_data.conditions
coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
name="environment_canada_sensor",
update_method=async_update_data,
update_interval=timedelta(minutes=5),
)
# Fetch initial data so we have data when entities subscribe
await coordinator.async_refresh()
async_add_entities(
ECSensor(coordinator, sensor_type, ec_data.language, ec_data.metadata)
for sensor_type in coordinator.data
)
class ECSensor(CoordinatorEntity):
"""Implementation of an Environment Canada sensor."""
def __init__(self, coordinator, sensor_type, language, metadata):
"""Initialize the sensor."""
super().__init__(coordinator)
self.sensor_type = sensor_type
self.language = language
self.metadata = metadata
@property
def unique_id(self) -> str:
"""Return the unique ID of the sensor."""
return f"{self.metadata['location']}-{self.sensor_type}"
@property
def name(self):
"""Return the name of the sensor."""
return SENSOR_TYPES[self.sensor_type][self.language]
@property
def icon(self):
"""Return the icon."""
return SENSOR_TYPES[self.sensor_type][ATTR_ICON]
@property
def state(self):
"""Return the state of the sensor."""
value = self.coordinator.data[self.sensor_type].get("value")
if isinstance(value, list):
return " | ".join([str(s.get("title")) for s in value])[:255]
if self.sensor_type == "tendency":
return str(value).capitalize()
if value is not None and len(str(value)) > 255:
_LOGGER.info("Value for %s truncated to 255 characters", self.unique_id)
return str(value)[:255]
return str(value)
@property
def unit_of_measurement(self):
"""Return the units of measurement."""
return SENSOR_TYPES[self.sensor_type][ATTR_UNIT]
async def async_update(self):
"""Update current conditions."""
await self.ec_data.update()
self.ec_data.conditions.update(self.ec_data.alerts)
@property
def device_state_attributes(self):
"""Return the state attributes of the device."""
attributes = {
ATTR_ATTRIBUTION: ATTRIBUTION,
ATTR_LOCATION: self.metadata.get("location"),
ATTR_STATION: self.metadata.get("station"),
}
timestamp = self.metadata.get("timestamp")
if timestamp:
attributes[ATTR_UPDATED] = timestamp.isoformat()
else:
attributes[ATTR_UPDATED] = None
value = self.coordinator.data[self.sensor_type].get("value")
if isinstance(value, list):
attributes[ATTR_TIME] = " | ".join([str(s.get("date")) for s in value])
return attributes