forked from home-assistant/core
Compare commits
283 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37d75e8a03 | ||
|
|
42f586c585 | ||
|
|
d705b35ea1 | ||
|
|
0684f8bddf | ||
|
|
3aed58f825 | ||
|
|
86247c93fc | ||
|
|
53ea24ec15 | ||
|
|
2f3a11f930 | ||
|
|
0d1412ea17 | ||
|
|
1ca9deb520 | ||
|
|
789f21c427 | ||
|
|
9da3fa5d75 | ||
|
|
93083513b4 | ||
|
|
c6888e4faf | ||
|
|
b1dbdec2ea | ||
|
|
6895081595 | ||
|
|
34d54511e8 | ||
|
|
8d4aac618d | ||
|
|
4fa9871080 | ||
|
|
b088ce601c | ||
|
|
bcfedeb797 | ||
|
|
9093819671 | ||
|
|
cac3e1acfa | ||
|
|
eba9b61011 | ||
|
|
0533a9c714 | ||
|
|
12b1f87b35 | ||
|
|
8b6d0ca13f | ||
|
|
dd7dea9a3f | ||
|
|
b99a22cd4d | ||
|
|
2634949999 | ||
|
|
e671ad41ec | ||
|
|
9ee0d8fefe | ||
|
|
e6a29b6a2a | ||
|
|
df928c80b8 | ||
|
|
05abf1405d | ||
|
|
753285eae7 | ||
|
|
67b7144703 | ||
|
|
d50b700dc7 | ||
|
|
4475cf24c8 | ||
|
|
3001df99cb | ||
|
|
364edbfd8a | ||
|
|
755835ee2e | ||
|
|
a4e4ffef0a | ||
|
|
655399eb7b | ||
|
|
77b60c712e | ||
|
|
a4dae0c1e1 | ||
|
|
96db04213b | ||
|
|
cc6a0d2f8d | ||
|
|
99ef2ae54d | ||
|
|
0dd128af77 | ||
|
|
1b3530a3f8 | ||
|
|
8565821394 | ||
|
|
523998f8a1 | ||
|
|
c2b89725be | ||
|
|
22961b30d2 | ||
|
|
aa6cb84b27 | ||
|
|
4e1e7a4a71 | ||
|
|
5a2bcd2763 | ||
|
|
f8ebc31576 | ||
|
|
cce0ca5688 | ||
|
|
d39b861110 | ||
|
|
715ce3185b | ||
|
|
c81a319346 | ||
|
|
f5a543b220 | ||
|
|
58da58c008 | ||
|
|
6348bf70ac | ||
|
|
0700108278 | ||
|
|
d8b85b2067 | ||
|
|
b7e8348c30 | ||
|
|
38d42de2c0 | ||
|
|
b3181a0ab2 | ||
|
|
10317fba17 | ||
|
|
7aa454231f | ||
|
|
19c54b8cbf | ||
|
|
195ee2a188 | ||
|
|
0dc8fb497b | ||
|
|
b10fc89a6b | ||
|
|
0749e045bb | ||
|
|
19dcb19d07 | ||
|
|
501e7c84be | ||
|
|
a54b9502ef | ||
|
|
617e8544c0 | ||
|
|
edddeaf5ab | ||
|
|
a756308e79 | ||
|
|
cd51d994b1 | ||
|
|
4eba2ccebc | ||
|
|
f5cd321185 | ||
|
|
bbd9c6eb5b | ||
|
|
ce6921d73c | ||
|
|
9db13a3e74 | ||
|
|
1e4233fe20 | ||
|
|
76ce0f6ea7 | ||
|
|
798f487ea4 | ||
|
|
3c0a34dd01 | ||
|
|
fbf812a845 | ||
|
|
7111fc47c4 | ||
|
|
e0f640c0f8 | ||
|
|
7caa985a59 | ||
|
|
25b39b36e7 | ||
|
|
418d6a6a41 | ||
|
|
a234f2ab31 | ||
|
|
7461af68b9 | ||
|
|
2171922265 | ||
|
|
4310a7d814 | ||
|
|
ae9e3c237a | ||
|
|
b4d4fe4ef8 | ||
|
|
70338da50e | ||
|
|
173b87e675 | ||
|
|
91cd6951f3 | ||
|
|
4684ea2d14 | ||
|
|
0c2772e0be | ||
|
|
8319f232b8 | ||
|
|
d8a81a54d8 | ||
|
|
8af0cb9e65 | ||
|
|
02db4dbe5e | ||
|
|
7dbe8070f7 | ||
|
|
363320eedb | ||
|
|
2e5c1236f9 | ||
|
|
348bdca647 | ||
|
|
4f33679255 | ||
|
|
cabb9c0ea4 | ||
|
|
d4a2b36638 | ||
|
|
bfd799dc04 | ||
|
|
acdddabe1f | ||
|
|
d5b6dc4f26 | ||
|
|
69aba2a6a1 | ||
|
|
cdaba62d2c | ||
|
|
b3b9fb0a7c | ||
|
|
cb1e0666c8 | ||
|
|
6b4f2e6f8f | ||
|
|
aef4a69cd0 | ||
|
|
02eba22068 | ||
|
|
7dbd0e5274 | ||
|
|
e631671832 | ||
|
|
27e29b714c | ||
|
|
c68e87c40e | ||
|
|
80af2f4279 | ||
|
|
f8ec85686a | ||
|
|
33fb080c1e | ||
|
|
9284f7b147 | ||
|
|
bcf97cb308 | ||
|
|
04a052a37d | ||
|
|
befcafbc49 | ||
|
|
02b7356596 | ||
|
|
46159c3f18 | ||
|
|
a28593f133 | ||
|
|
889aced3b6 | ||
|
|
36b37b6db3 | ||
|
|
3bc58f9750 | ||
|
|
343054494c | ||
|
|
93c086d830 | ||
|
|
9e41a37284 | ||
|
|
ff229dd599 | ||
|
|
cc4b2fbcfa | ||
|
|
5d1a193eca | ||
|
|
71c6f99d31 | ||
|
|
bd60a58765 | ||
|
|
08a0377dcb | ||
|
|
4d98a7e156 | ||
|
|
3e38dc0fd9 | ||
|
|
afc0a1f376 | ||
|
|
1849eae0ff | ||
|
|
f9225bad5f | ||
|
|
88a08fdf57 | ||
|
|
d277e0fb03 | ||
|
|
13b001cd9b | ||
|
|
dd21bf73fc | ||
|
|
368cac7e5d | ||
|
|
18c03e2f8d | ||
|
|
9b3346bc80 | ||
|
|
76f21452ee | ||
|
|
433775cf4b | ||
|
|
46f05ca279 | ||
|
|
3d9d104482 | ||
|
|
1d1b5ab345 | ||
|
|
1c01ff401f | ||
|
|
3bd9be2f6d | ||
|
|
daa9c8d856 | ||
|
|
1aa30ea87b | ||
|
|
331726ec2f | ||
|
|
27ecd43da3 | ||
|
|
d62a78ae61 | ||
|
|
fa7873dc6d | ||
|
|
de5a22953d | ||
|
|
cbc68e45cd | ||
|
|
c4235edc41 | ||
|
|
ed53bb1d91 | ||
|
|
a668300c2e | ||
|
|
722aa0895e | ||
|
|
7e9f8de7e0 | ||
|
|
8faec3da8d | ||
|
|
1060630bbd | ||
|
|
25273c694a | ||
|
|
071fcee9a9 | ||
|
|
5549a925b8 | ||
|
|
be04d7b92e | ||
|
|
f37c541a50 | ||
|
|
6823b14d4c | ||
|
|
94e0db8ec4 | ||
|
|
ebc2a0103e | ||
|
|
ea7f3c8bb3 | ||
|
|
32df2f7d8b | ||
|
|
b43c80ca21 | ||
|
|
fa201b6c2b | ||
|
|
76ce33dc24 | ||
|
|
8b436c43f7 | ||
|
|
fd66120d6d | ||
|
|
43b8353566 | ||
|
|
4aed0b6ccf | ||
|
|
3647ada143 | ||
|
|
2dddd31d97 | ||
|
|
923158cfba | ||
|
|
291a2d6258 | ||
|
|
43288d3e1f | ||
|
|
d41fa66bca | ||
|
|
f1ba98927c | ||
|
|
f91cc21bbd | ||
|
|
13cc671844 | ||
|
|
979797136a | ||
|
|
778fa2e3fe | ||
|
|
6a93f5b7ad | ||
|
|
19873e6547 | ||
|
|
2fcd77098d | ||
|
|
d1965eef8b | ||
|
|
16351ef3c2 | ||
|
|
e2f257cb63 | ||
|
|
ea8702b0df | ||
|
|
470aa7e871 | ||
|
|
61a7ce173c | ||
|
|
b0c52220bc | ||
|
|
714564eaa6 | ||
|
|
1f37c215f6 | ||
|
|
46d0523f98 | ||
|
|
eb458fb1d5 | ||
|
|
10fa63775d | ||
|
|
ed19fdd462 | ||
|
|
2cc87cb7ab | ||
|
|
7ac72ebf38 | ||
|
|
98c8782c2b | ||
|
|
7bd7d644a0 | ||
|
|
3f2fad1a27 | ||
|
|
819fd811af | ||
|
|
e2dac31471 | ||
|
|
7e70252de5 | ||
|
|
cc857abfd2 | ||
|
|
adab367f0e | ||
|
|
259eeb3169 | ||
|
|
efb1fb9978 | ||
|
|
5693f9ff9b | ||
|
|
9ba504cd78 | ||
|
|
176fd39e0b | ||
|
|
cd0ae66d58 | ||
|
|
65d14909ee | ||
|
|
5393a16c44 | ||
|
|
cbd65efe52 | ||
|
|
ef10773202 | ||
|
|
dfc2556669 | ||
|
|
14aa19b814 | ||
|
|
c3972b22fd | ||
|
|
ae1d2926cf | ||
|
|
089dfad78a | ||
|
|
f6bb5c77a0 | ||
|
|
eb9d242ade | ||
|
|
fbcf21412d | ||
|
|
b3e84c6ee8 | ||
|
|
c3316df31d | ||
|
|
f942cb03a4 | ||
|
|
d3ac72d013 | ||
|
|
d59ea5329e | ||
|
|
d4fa625a7f | ||
|
|
a89057ece5 | ||
|
|
2d5176eee9 | ||
|
|
0a07ff4d23 | ||
|
|
96303a1d80 | ||
|
|
03d3bbfba1 | ||
|
|
56246056ce | ||
|
|
6d4a47a53d | ||
|
|
e6d710c203 | ||
|
|
b45c985d58 | ||
|
|
3d7bfa8357 | ||
|
|
b81c2806bb | ||
|
|
06a30c882f | ||
|
|
9315f3bdd9 |
@@ -1032,6 +1032,8 @@ omit =
|
||||
homeassistant/components/tank_utility/sensor.py
|
||||
homeassistant/components/tankerkoenig/*
|
||||
homeassistant/components/tapsaff/binary_sensor.py
|
||||
homeassistant/components/tautulli/const.py
|
||||
homeassistant/components/tautulli/coordinator.py
|
||||
homeassistant/components/tautulli/sensor.py
|
||||
homeassistant/components/ted5000/sensor.py
|
||||
homeassistant/components/telegram/notify.py
|
||||
|
||||
2
.github/workflows/wheels.yml
vendored
2
.github/workflows/wheels.yml
vendored
@@ -65,7 +65,6 @@ jobs:
|
||||
matrix:
|
||||
arch: ${{ fromJson(needs.init.outputs.architectures) }}
|
||||
tag:
|
||||
- "3.9-alpine3.13"
|
||||
- "3.9-alpine3.14"
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
@@ -106,7 +105,6 @@ jobs:
|
||||
matrix:
|
||||
arch: ${{ fromJson(needs.init.outputs.architectures) }}
|
||||
tag:
|
||||
- "3.9-alpine3.13"
|
||||
- "3.9-alpine3.14"
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
|
||||
@@ -202,7 +202,7 @@ homeassistant/components/group/* @home-assistant/core
|
||||
homeassistant/components/growatt_server/* @indykoning @muppet3000 @JasperPlant
|
||||
homeassistant/components/guardian/* @bachya
|
||||
homeassistant/components/habitica/* @ASMfreaK @leikoilja
|
||||
homeassistant/components/harmony/* @ehendrix23 @bramkragten @bdraco @mkeesey
|
||||
homeassistant/components/harmony/* @ehendrix23 @bramkragten @bdraco @mkeesey @Aohzan
|
||||
homeassistant/components/hassio/* @home-assistant/supervisor
|
||||
homeassistant/components/heatmiser/* @andylockran
|
||||
homeassistant/components/heos/* @andrewsayre
|
||||
@@ -248,6 +248,7 @@ homeassistant/components/integration/* @dgomes
|
||||
homeassistant/components/intent/* @home-assistant/core
|
||||
homeassistant/components/intesishome/* @jnimmo
|
||||
homeassistant/components/ios/* @robbiet480
|
||||
homeassistant/components/iotawatt/* @gtdiehl
|
||||
homeassistant/components/iperf3/* @rohankapoorcom
|
||||
homeassistant/components/ipma/* @dgomes @abmantis
|
||||
homeassistant/components/ipp/* @ctalkington
|
||||
@@ -552,6 +553,7 @@ homeassistant/components/uptimerobot/* @ludeeus
|
||||
homeassistant/components/usb/* @bdraco
|
||||
homeassistant/components/usgs_earthquakes_feed/* @exxamalte
|
||||
homeassistant/components/utility_meter/* @dgomes
|
||||
homeassistant/components/vallox/* @andre-richter
|
||||
homeassistant/components/velbus/* @Cereal2nd @brefra
|
||||
homeassistant/components/velux/* @Julius2342
|
||||
homeassistant/components/vera/* @pavoni
|
||||
|
||||
15
Dockerfile
15
Dockerfile
@@ -16,6 +16,21 @@ RUN \
|
||||
-e ./homeassistant \
|
||||
&& python3 -m compileall homeassistant/homeassistant
|
||||
|
||||
# Fix Bug with Alpine 3.14 and sqlite 3.35
|
||||
# https://gitlab.alpinelinux.org/alpine/aports/-/issues/12524
|
||||
ARG BUILD_ARCH
|
||||
RUN \
|
||||
if [ "${BUILD_ARCH}" = "amd64" ]; then \
|
||||
export APK_ARCH=x86_64; \
|
||||
elif [ "${BUILD_ARCH}" = "i386" ]; then \
|
||||
export APK_ARCH=x86; \
|
||||
else \
|
||||
export APK_ARCH=${BUILD_ARCH}; \
|
||||
fi \
|
||||
&& curl -O http://dl-cdn.alpinelinux.org/alpine/v3.13/main/${APK_ARCH}/sqlite-libs-3.34.1-r0.apk \
|
||||
&& apk add --no-cache sqlite-libs-3.34.1-r0.apk \
|
||||
&& rm -f sqlite-libs-3.34.1-r0.apk
|
||||
|
||||
# Home Assistant S6-Overlay
|
||||
COPY rootfs /
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ RUN \
|
||||
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
|
||||
&& apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
# Additional library needed by some tests and accordingly by VScode Tests Discovery
|
||||
bluez \
|
||||
libudev-dev \
|
||||
libavformat-dev \
|
||||
libavcodec-dev \
|
||||
|
||||
@@ -118,14 +118,6 @@ homeassistant.util.pressure
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.ruamel\_yaml
|
||||
-------------------------------
|
||||
|
||||
.. automodule:: homeassistant.util.ruamel_yaml
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.ssl
|
||||
----------------------
|
||||
|
||||
|
||||
@@ -342,7 +342,11 @@ def async_enable_logging(
|
||||
err_log_path, backupCount=1
|
||||
)
|
||||
|
||||
err_handler.doRollover()
|
||||
try:
|
||||
err_handler.doRollover()
|
||||
except OSError as err:
|
||||
_LOGGER.error("Error rolling over log file: %s", err)
|
||||
|
||||
err_handler.setLevel(logging.INFO if verbose else logging.WARNING)
|
||||
err_handler.setFormatter(logging.Formatter(fmt, datefmt=datefmt))
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"config": {
|
||||
"abort": {
|
||||
"reauth_successful": "La r\u00e9-authentification a r\u00e9ussi",
|
||||
"single_instance_allowed": "D\u00e9ja configur\u00e9. Une seule configuration possible."
|
||||
"single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"api_key": "Cl\u00e9 d'API",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"name": "Nom de l'int\u00e9gration"
|
||||
"name": "Nom"
|
||||
},
|
||||
"description": "Si vous avez besoin d'aide pour la configuration, consultez le site suivant : https://www.home-assistant.io/integrations/accuweather/\n\nCertains capteurs ne sont pas activ\u00e9s par d\u00e9faut. Vous pouvez les activer dans le registre des entit\u00e9s apr\u00e8s la configuration de l'int\u00e9gration.\nLes pr\u00e9visions m\u00e9t\u00e9orologiques ne sont pas activ\u00e9es par d\u00e9faut. Vous pouvez l'activer dans les options d'int\u00e9gration.",
|
||||
"title": "AccuWeather"
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"account_id": "ID de la cuenta"
|
||||
"account_id": "ID de la cuenta",
|
||||
"host": "Anfitri\u00f3n",
|
||||
"password": "Contrase\u00f1a"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
"host": "H\u00f4te",
|
||||
"password": "Mot de passe",
|
||||
"port": "Port",
|
||||
"ssl": "AdGuard Home utilise un certificat SSL",
|
||||
"ssl": "Utilise un certificat SSL",
|
||||
"username": "Nom d'utilisateur",
|
||||
"verify_ssl": "AdGuard Home utilise un certificat appropri\u00e9"
|
||||
"verify_ssl": "V\u00e9rifier le certificat SSL"
|
||||
},
|
||||
"description": "Configurez votre instance AdGuard Home pour permettre la surveillance et le contr\u00f4le."
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
"""Sensor platform for Advantage Air integration."""
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT, SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
SensorEntity,
|
||||
)
|
||||
from homeassistant.const import PERCENTAGE, TEMP_CELSIUS
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
|
||||
@@ -138,11 +142,11 @@ class AdvantageAirZoneSignal(AdvantageAirEntity, SensorEntity):
|
||||
|
||||
|
||||
class AdvantageAirZoneTemp(AdvantageAirEntity, SensorEntity):
|
||||
"""Representation of Advantage Air Zone wireless signal sensor."""
|
||||
"""Representation of Advantage Air Zone temperature sensor."""
|
||||
|
||||
_attr_native_unit_of_measurement = TEMP_CELSIUS
|
||||
_attr_device_class = DEVICE_CLASS_TEMPERATURE
|
||||
_attr_state_class = STATE_CLASS_MEASUREMENT
|
||||
_attr_icon = "mdi:thermometer"
|
||||
_attr_entity_registry_enabled_default = False
|
||||
|
||||
def __init__(self, instance, ac_key, zone_key):
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Constant values for the AEMET OpenData component."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.sensor import SensorEntityDescription
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_CLEAR_NIGHT,
|
||||
ATTR_CONDITION_CLOUDY,
|
||||
@@ -40,9 +42,6 @@ DEFAULT_NAME = "AEMET"
|
||||
DOMAIN = "aemet"
|
||||
ENTRY_NAME = "name"
|
||||
ENTRY_WEATHER_COORDINATOR = "weather_coordinator"
|
||||
SENSOR_NAME = "sensor_name"
|
||||
SENSOR_UNIT = "sensor_unit"
|
||||
SENSOR_DEVICE_CLASS = "sensor_device_class"
|
||||
|
||||
ATTR_API_CONDITION = "condition"
|
||||
ATTR_API_FORECAST_DAILY = "forecast-daily"
|
||||
@@ -200,118 +199,145 @@ FORECAST_MODE_ATTR_API = {
|
||||
FORECAST_MODE_HOURLY: ATTR_API_FORECAST_HOURLY,
|
||||
}
|
||||
|
||||
FORECAST_SENSOR_TYPES = {
|
||||
ATTR_FORECAST_CONDITION: {
|
||||
SENSOR_NAME: "Condition",
|
||||
},
|
||||
ATTR_FORECAST_PRECIPITATION: {
|
||||
SENSOR_NAME: "Precipitation",
|
||||
SENSOR_UNIT: PRECIPITATION_MILLIMETERS_PER_HOUR,
|
||||
},
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY: {
|
||||
SENSOR_NAME: "Precipitation probability",
|
||||
SENSOR_UNIT: PERCENTAGE,
|
||||
},
|
||||
ATTR_FORECAST_TEMP: {
|
||||
SENSOR_NAME: "Temperature",
|
||||
SENSOR_UNIT: TEMP_CELSIUS,
|
||||
SENSOR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
},
|
||||
ATTR_FORECAST_TEMP_LOW: {
|
||||
SENSOR_NAME: "Temperature Low",
|
||||
SENSOR_UNIT: TEMP_CELSIUS,
|
||||
SENSOR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
},
|
||||
ATTR_FORECAST_TIME: {
|
||||
SENSOR_NAME: "Time",
|
||||
SENSOR_DEVICE_CLASS: DEVICE_CLASS_TIMESTAMP,
|
||||
},
|
||||
ATTR_FORECAST_WIND_BEARING: {
|
||||
SENSOR_NAME: "Wind bearing",
|
||||
SENSOR_UNIT: DEGREE,
|
||||
},
|
||||
ATTR_FORECAST_WIND_SPEED: {
|
||||
SENSOR_NAME: "Wind speed",
|
||||
SENSOR_UNIT: SPEED_KILOMETERS_PER_HOUR,
|
||||
},
|
||||
}
|
||||
WEATHER_SENSOR_TYPES = {
|
||||
ATTR_API_CONDITION: {
|
||||
SENSOR_NAME: "Condition",
|
||||
},
|
||||
ATTR_API_HUMIDITY: {
|
||||
SENSOR_NAME: "Humidity",
|
||||
SENSOR_UNIT: PERCENTAGE,
|
||||
SENSOR_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY,
|
||||
},
|
||||
ATTR_API_PRESSURE: {
|
||||
SENSOR_NAME: "Pressure",
|
||||
SENSOR_UNIT: PRESSURE_HPA,
|
||||
SENSOR_DEVICE_CLASS: DEVICE_CLASS_PRESSURE,
|
||||
},
|
||||
ATTR_API_RAIN: {
|
||||
SENSOR_NAME: "Rain",
|
||||
SENSOR_UNIT: PRECIPITATION_MILLIMETERS_PER_HOUR,
|
||||
},
|
||||
ATTR_API_RAIN_PROB: {
|
||||
SENSOR_NAME: "Rain probability",
|
||||
SENSOR_UNIT: PERCENTAGE,
|
||||
},
|
||||
ATTR_API_SNOW: {
|
||||
SENSOR_NAME: "Snow",
|
||||
SENSOR_UNIT: PRECIPITATION_MILLIMETERS_PER_HOUR,
|
||||
},
|
||||
ATTR_API_SNOW_PROB: {
|
||||
SENSOR_NAME: "Snow probability",
|
||||
SENSOR_UNIT: PERCENTAGE,
|
||||
},
|
||||
ATTR_API_STATION_ID: {
|
||||
SENSOR_NAME: "Station ID",
|
||||
},
|
||||
ATTR_API_STATION_NAME: {
|
||||
SENSOR_NAME: "Station name",
|
||||
},
|
||||
ATTR_API_STATION_TIMESTAMP: {
|
||||
SENSOR_NAME: "Station timestamp",
|
||||
SENSOR_DEVICE_CLASS: DEVICE_CLASS_TIMESTAMP,
|
||||
},
|
||||
ATTR_API_STORM_PROB: {
|
||||
SENSOR_NAME: "Storm probability",
|
||||
SENSOR_UNIT: PERCENTAGE,
|
||||
},
|
||||
ATTR_API_TEMPERATURE: {
|
||||
SENSOR_NAME: "Temperature",
|
||||
SENSOR_UNIT: TEMP_CELSIUS,
|
||||
SENSOR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
},
|
||||
ATTR_API_TEMPERATURE_FEELING: {
|
||||
SENSOR_NAME: "Temperature feeling",
|
||||
SENSOR_UNIT: TEMP_CELSIUS,
|
||||
SENSOR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
},
|
||||
ATTR_API_TOWN_ID: {
|
||||
SENSOR_NAME: "Town ID",
|
||||
},
|
||||
ATTR_API_TOWN_NAME: {
|
||||
SENSOR_NAME: "Town name",
|
||||
},
|
||||
ATTR_API_TOWN_TIMESTAMP: {
|
||||
SENSOR_NAME: "Town timestamp",
|
||||
SENSOR_DEVICE_CLASS: DEVICE_CLASS_TIMESTAMP,
|
||||
},
|
||||
ATTR_API_WIND_BEARING: {
|
||||
SENSOR_NAME: "Wind bearing",
|
||||
SENSOR_UNIT: DEGREE,
|
||||
},
|
||||
ATTR_API_WIND_MAX_SPEED: {
|
||||
SENSOR_NAME: "Wind max speed",
|
||||
SENSOR_UNIT: SPEED_KILOMETERS_PER_HOUR,
|
||||
},
|
||||
ATTR_API_WIND_SPEED: {
|
||||
SENSOR_NAME: "Wind speed",
|
||||
SENSOR_UNIT: SPEED_KILOMETERS_PER_HOUR,
|
||||
},
|
||||
}
|
||||
FORECAST_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_CONDITION,
|
||||
name="Condition",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_PRECIPITATION,
|
||||
name="Precipitation",
|
||||
native_unit_of_measurement=PRECIPITATION_MILLIMETERS_PER_HOUR,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
name="Precipitation probability",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_TEMP,
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_TEMP_LOW,
|
||||
name="Temperature Low",
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_TIME,
|
||||
name="Time",
|
||||
device_class=DEVICE_CLASS_TIMESTAMP,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_WIND_BEARING,
|
||||
name="Wind bearing",
|
||||
native_unit_of_measurement=DEGREE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_WIND_SPEED,
|
||||
name="Wind speed",
|
||||
native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR,
|
||||
),
|
||||
)
|
||||
WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_CONDITION,
|
||||
name="Condition",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_HUMIDITY,
|
||||
name="Humidity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_PRESSURE,
|
||||
name="Pressure",
|
||||
native_unit_of_measurement=PRESSURE_HPA,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_RAIN,
|
||||
name="Rain",
|
||||
native_unit_of_measurement=PRECIPITATION_MILLIMETERS_PER_HOUR,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_RAIN_PROB,
|
||||
name="Rain probability",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_SNOW,
|
||||
name="Snow",
|
||||
native_unit_of_measurement=PRECIPITATION_MILLIMETERS_PER_HOUR,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_SNOW_PROB,
|
||||
name="Snow probability",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_STATION_ID,
|
||||
name="Station ID",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_STATION_NAME,
|
||||
name="Station name",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_STATION_TIMESTAMP,
|
||||
name="Station timestamp",
|
||||
device_class=DEVICE_CLASS_TIMESTAMP,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_STORM_PROB,
|
||||
name="Storm probability",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_TEMPERATURE,
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_TEMPERATURE_FEELING,
|
||||
name="Temperature feeling",
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_TOWN_ID,
|
||||
name="Town ID",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_TOWN_NAME,
|
||||
name="Town name",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_TOWN_TIMESTAMP,
|
||||
name="Town timestamp",
|
||||
device_class=DEVICE_CLASS_TIMESTAMP,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_WIND_BEARING,
|
||||
name="Wind bearing",
|
||||
native_unit_of_measurement=DEGREE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_WIND_MAX_SPEED,
|
||||
name="Wind max speed",
|
||||
native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_WIND_SPEED,
|
||||
name="Wind speed",
|
||||
native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR,
|
||||
),
|
||||
)
|
||||
|
||||
WIND_BEARING_MAP = {
|
||||
"C": None,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Support for the AEMET OpenData service."""
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
|
||||
from homeassistant.const import ATTR_ATTRIBUTION
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
@@ -14,9 +16,6 @@ from .const import (
|
||||
FORECAST_MONITORED_CONDITIONS,
|
||||
FORECAST_SENSOR_TYPES,
|
||||
MONITORED_CONDITIONS,
|
||||
SENSOR_DEVICE_CLASS,
|
||||
SENSOR_NAME,
|
||||
SENSOR_UNIT,
|
||||
WEATHER_SENSOR_TYPES,
|
||||
)
|
||||
from .weather_update_coordinator import WeatherUpdateCoordinator
|
||||
@@ -28,37 +27,30 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
name = domain_data[ENTRY_NAME]
|
||||
weather_coordinator = domain_data[ENTRY_WEATHER_COORDINATOR]
|
||||
|
||||
weather_sensor_types = WEATHER_SENSOR_TYPES
|
||||
forecast_sensor_types = FORECAST_SENSOR_TYPES
|
||||
|
||||
entities = []
|
||||
for sensor_type in MONITORED_CONDITIONS:
|
||||
unique_id = f"{config_entry.unique_id}-{sensor_type}"
|
||||
entities.append(
|
||||
AemetSensor(
|
||||
name,
|
||||
unique_id,
|
||||
sensor_type,
|
||||
weather_sensor_types[sensor_type],
|
||||
unique_id = config_entry.unique_id
|
||||
entities: list[AbstractAemetSensor] = [
|
||||
AemetSensor(name, unique_id, weather_coordinator, description)
|
||||
for description in WEATHER_SENSOR_TYPES
|
||||
if description.key in MONITORED_CONDITIONS
|
||||
]
|
||||
entities.extend(
|
||||
[
|
||||
AemetForecastSensor(
|
||||
name_prefix,
|
||||
unique_id_prefix,
|
||||
weather_coordinator,
|
||||
mode,
|
||||
description,
|
||||
)
|
||||
)
|
||||
|
||||
for mode in FORECAST_MODES:
|
||||
name = f"{domain_data[ENTRY_NAME]} {mode}"
|
||||
|
||||
for sensor_type in FORECAST_MONITORED_CONDITIONS:
|
||||
unique_id = f"{config_entry.unique_id}-forecast-{mode}-{sensor_type}"
|
||||
entities.append(
|
||||
AemetForecastSensor(
|
||||
f"{name} Forecast",
|
||||
unique_id,
|
||||
sensor_type,
|
||||
forecast_sensor_types[sensor_type],
|
||||
weather_coordinator,
|
||||
mode,
|
||||
)
|
||||
for mode in FORECAST_MODES
|
||||
if (
|
||||
(name_prefix := f"{domain_data[ENTRY_NAME]} {mode} Forecast")
|
||||
and (unique_id_prefix := f"{unique_id}-forecast-{mode}")
|
||||
)
|
||||
for description in FORECAST_SENSOR_TYPES
|
||||
if description.key in FORECAST_MONITORED_CONDITIONS
|
||||
]
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
@@ -72,20 +64,14 @@ class AbstractAemetSensor(CoordinatorEntity, SensorEntity):
|
||||
self,
|
||||
name,
|
||||
unique_id,
|
||||
sensor_type,
|
||||
sensor_configuration,
|
||||
coordinator: WeatherUpdateCoordinator,
|
||||
description: SensorEntityDescription,
|
||||
):
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(coordinator)
|
||||
self._name = name
|
||||
self._unique_id = unique_id
|
||||
self._sensor_type = sensor_type
|
||||
self._sensor_name = sensor_configuration[SENSOR_NAME]
|
||||
self._attr_name = f"{self._name} {self._sensor_name}"
|
||||
self._attr_unique_id = self._unique_id
|
||||
self._attr_device_class = sensor_configuration.get(SENSOR_DEVICE_CLASS)
|
||||
self._attr_native_unit_of_measurement = sensor_configuration.get(SENSOR_UNIT)
|
||||
self.entity_description = description
|
||||
self._attr_name = f"{name} {description.name}"
|
||||
self._attr_unique_id = unique_id
|
||||
|
||||
|
||||
class AemetSensor(AbstractAemetSensor):
|
||||
@@ -95,20 +81,21 @@ class AemetSensor(AbstractAemetSensor):
|
||||
self,
|
||||
name,
|
||||
unique_id,
|
||||
sensor_type,
|
||||
sensor_configuration,
|
||||
weather_coordinator: WeatherUpdateCoordinator,
|
||||
description: SensorEntityDescription,
|
||||
):
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(
|
||||
name, unique_id, sensor_type, sensor_configuration, weather_coordinator
|
||||
name=name,
|
||||
unique_id=f"{unique_id}-{description.key}",
|
||||
coordinator=weather_coordinator,
|
||||
description=description,
|
||||
)
|
||||
self._weather_coordinator = weather_coordinator
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
"""Return the state of the device."""
|
||||
return self._weather_coordinator.data.get(self._sensor_type)
|
||||
return self.coordinator.data.get(self.entity_description.key)
|
||||
|
||||
|
||||
class AemetForecastSensor(AbstractAemetSensor):
|
||||
@@ -118,16 +105,17 @@ class AemetForecastSensor(AbstractAemetSensor):
|
||||
self,
|
||||
name,
|
||||
unique_id,
|
||||
sensor_type,
|
||||
sensor_configuration,
|
||||
weather_coordinator: WeatherUpdateCoordinator,
|
||||
forecast_mode,
|
||||
description: SensorEntityDescription,
|
||||
):
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(
|
||||
name, unique_id, sensor_type, sensor_configuration, weather_coordinator
|
||||
name=name,
|
||||
unique_id=f"{unique_id}-{description.key}",
|
||||
coordinator=weather_coordinator,
|
||||
description=description,
|
||||
)
|
||||
self._weather_coordinator = weather_coordinator
|
||||
self._forecast_mode = forecast_mode
|
||||
self._attr_entity_registry_enabled_default = (
|
||||
self._forecast_mode == FORECAST_MODE_DAILY
|
||||
@@ -137,9 +125,9 @@ class AemetForecastSensor(AbstractAemetSensor):
|
||||
def native_value(self):
|
||||
"""Return the state of the device."""
|
||||
forecast = None
|
||||
forecasts = self._weather_coordinator.data.get(
|
||||
forecasts = self.coordinator.data.get(
|
||||
FORECAST_MODE_ATTR_API[self._forecast_mode]
|
||||
)
|
||||
if forecasts:
|
||||
forecast = forecasts[0].get(self._sensor_type)
|
||||
forecast = forecasts[0].get(self.entity_description.key)
|
||||
return forecast
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"already_in_progress": "La configuration de l'appareil est d\u00e9j\u00e0 en cours.",
|
||||
"already_in_progress": "La configuration est d\u00e9j\u00e0 en cours",
|
||||
"cannot_connect": "\u00c9chec de connexion"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Nom d'h\u00f4te ou adresse IP",
|
||||
"host": "H\u00f4te",
|
||||
"port": "Port"
|
||||
},
|
||||
"title": "Configurer l'agent DVR"
|
||||
|
||||
@@ -3,23 +3,6 @@ from __future__ import annotations
|
||||
|
||||
from typing import Final
|
||||
|
||||
from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
DEVICE_CLASS_AQI,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_PM1,
|
||||
DEVICE_CLASS_PM10,
|
||||
DEVICE_CLASS_PM25,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
PERCENTAGE,
|
||||
PRESSURE_HPA,
|
||||
TEMP_CELSIUS,
|
||||
)
|
||||
|
||||
from .model import AirlySensorEntityDescription
|
||||
|
||||
ATTR_API_ADVICE: Final = "ADVICE"
|
||||
ATTR_API_CAQI: Final = "CAQI"
|
||||
ATTR_API_CAQI_DESCRIPTION: Final = "DESCRIPTION"
|
||||
@@ -49,56 +32,3 @@ MANUFACTURER: Final = "Airly sp. z o.o."
|
||||
MAX_UPDATE_INTERVAL: Final = 90
|
||||
MIN_UPDATE_INTERVAL: Final = 5
|
||||
NO_AIRLY_SENSORS: Final = "There are no Airly sensors in this area yet."
|
||||
|
||||
SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_CAQI,
|
||||
device_class=DEVICE_CLASS_AQI,
|
||||
name=ATTR_API_CAQI,
|
||||
native_unit_of_measurement="CAQI",
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_PM1,
|
||||
device_class=DEVICE_CLASS_PM1,
|
||||
name=ATTR_API_PM1,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_PM25,
|
||||
device_class=DEVICE_CLASS_PM25,
|
||||
name="PM2.5",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_PM10,
|
||||
device_class=DEVICE_CLASS_PM10,
|
||||
name=ATTR_API_PM10,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_HUMIDITY,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
name=ATTR_API_HUMIDITY.capitalize(),
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
value=lambda value: round(value, 1),
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_PRESSURE,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
name=ATTR_API_PRESSURE.capitalize(),
|
||||
native_unit_of_measurement=PRESSURE_HPA,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_TEMPERATURE,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
name=ATTR_API_TEMPERATURE.capitalize(),
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
value=lambda value: round(value, 1),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
"""Type definitions for Airly integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Callable
|
||||
|
||||
from homeassistant.components.sensor import SensorEntityDescription
|
||||
|
||||
|
||||
@dataclass
|
||||
class AirlySensorEntityDescription(SensorEntityDescription):
|
||||
"""Class describing Airly sensor entities."""
|
||||
|
||||
value: Callable = round
|
||||
@@ -1,11 +1,30 @@
|
||||
"""Support for the Airly sensor service."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, cast
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Callable, cast
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_ATTRIBUTION, CONF_NAME
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONF_NAME,
|
||||
DEVICE_CLASS_AQI,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_PM1,
|
||||
DEVICE_CLASS_PM10,
|
||||
DEVICE_CLASS_PM25,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
PERCENTAGE,
|
||||
PRESSURE_HPA,
|
||||
TEMP_CELSIUS,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
@@ -18,8 +37,12 @@ from .const import (
|
||||
ATTR_API_CAQI,
|
||||
ATTR_API_CAQI_DESCRIPTION,
|
||||
ATTR_API_CAQI_LEVEL,
|
||||
ATTR_API_HUMIDITY,
|
||||
ATTR_API_PM1,
|
||||
ATTR_API_PM10,
|
||||
ATTR_API_PM25,
|
||||
ATTR_API_PRESSURE,
|
||||
ATTR_API_TEMPERATURE,
|
||||
ATTR_DESCRIPTION,
|
||||
ATTR_LEVEL,
|
||||
ATTR_LIMIT,
|
||||
@@ -28,15 +51,74 @@ from .const import (
|
||||
DEFAULT_NAME,
|
||||
DOMAIN,
|
||||
MANUFACTURER,
|
||||
SENSOR_TYPES,
|
||||
SUFFIX_LIMIT,
|
||||
SUFFIX_PERCENT,
|
||||
)
|
||||
from .model import AirlySensorEntityDescription
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
|
||||
|
||||
@dataclass
|
||||
class AirlySensorEntityDescription(SensorEntityDescription):
|
||||
"""Class describing Airly sensor entities."""
|
||||
|
||||
value: Callable = round
|
||||
|
||||
|
||||
SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_CAQI,
|
||||
device_class=DEVICE_CLASS_AQI,
|
||||
name=ATTR_API_CAQI,
|
||||
native_unit_of_measurement="CAQI",
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_PM1,
|
||||
device_class=DEVICE_CLASS_PM1,
|
||||
name=ATTR_API_PM1,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_PM25,
|
||||
device_class=DEVICE_CLASS_PM25,
|
||||
name="PM2.5",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_PM10,
|
||||
device_class=DEVICE_CLASS_PM10,
|
||||
name=ATTR_API_PM10,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_HUMIDITY,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
name=ATTR_API_HUMIDITY.capitalize(),
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
value=lambda value: round(value, 1),
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_PRESSURE,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
name=ATTR_API_PRESSURE.capitalize(),
|
||||
native_unit_of_measurement=PRESSURE_HPA,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_TEMPERATURE,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
name=ATTR_API_TEMPERATURE.capitalize(),
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
value=lambda value: round(value, 1),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'int\u00e9gration des coordonn\u00e9es d'Airly est d\u00e9j\u00e0 configur\u00e9."
|
||||
"already_configured": "L'emplacement est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"invalid_api_key": "Cl\u00e9 API invalide",
|
||||
@@ -13,7 +13,7 @@
|
||||
"api_key": "Cl\u00e9 d'API",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"name": "Nom de l'int\u00e9gration"
|
||||
"name": "Nom"
|
||||
},
|
||||
"description": "Configurez l'int\u00e9gration de la qualit\u00e9 de l'air Airly. Pour g\u00e9n\u00e9rer une cl\u00e9 API, rendez-vous sur https://developer.airly.eu/register.",
|
||||
"title": "Airly"
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
"""Support for the AirNow sensor service."""
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION,
|
||||
ATTR_DEVICE_CLASS,
|
||||
ATTR_ICON,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
)
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from . import AirNowDataUpdateCoordinator
|
||||
from .const import (
|
||||
ATTR_API_AQI,
|
||||
ATTR_API_AQI_DESCRIPTION,
|
||||
@@ -22,69 +23,69 @@ from .const import (
|
||||
|
||||
ATTRIBUTION = "Data provided by AirNow"
|
||||
|
||||
ATTR_LABEL = "label"
|
||||
ATTR_UNIT = "unit"
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
|
||||
SENSOR_TYPES = {
|
||||
ATTR_API_AQI: {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:blur",
|
||||
ATTR_LABEL: ATTR_API_AQI,
|
||||
ATTR_UNIT: "aqi",
|
||||
},
|
||||
ATTR_API_PM25: {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:blur",
|
||||
ATTR_LABEL: ATTR_API_PM25,
|
||||
ATTR_UNIT: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
},
|
||||
ATTR_API_O3: {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:blur",
|
||||
ATTR_LABEL: ATTR_API_O3,
|
||||
ATTR_UNIT: CONCENTRATION_PARTS_PER_MILLION,
|
||||
},
|
||||
}
|
||||
SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_AQI,
|
||||
icon="mdi:blur",
|
||||
name=ATTR_API_AQI,
|
||||
native_unit_of_measurement="aqi",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_PM25,
|
||||
icon="mdi:blur",
|
||||
name=ATTR_API_PM25,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_O3,
|
||||
icon="mdi:blur",
|
||||
name=ATTR_API_O3,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up AirNow sensor entities based on a config entry."""
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
sensors = []
|
||||
for sensor in SENSOR_TYPES:
|
||||
sensors.append(AirNowSensor(coordinator, sensor))
|
||||
entities = [AirNowSensor(coordinator, description) for description in SENSOR_TYPES]
|
||||
|
||||
async_add_entities(sensors, False)
|
||||
async_add_entities(entities, False)
|
||||
|
||||
|
||||
class AirNowSensor(CoordinatorEntity, SensorEntity):
|
||||
"""Define an AirNow sensor."""
|
||||
|
||||
def __init__(self, coordinator, kind):
|
||||
coordinator: AirNowDataUpdateCoordinator
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AirNowDataUpdateCoordinator,
|
||||
description: SensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator)
|
||||
self.kind = kind
|
||||
self.entity_description = description
|
||||
self._state = None
|
||||
self._attrs = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
self._attr_name = f"AirNow {SENSOR_TYPES[self.kind][ATTR_LABEL]}"
|
||||
self._attr_icon = SENSOR_TYPES[self.kind][ATTR_ICON]
|
||||
self._attr_device_class = SENSOR_TYPES[self.kind][ATTR_DEVICE_CLASS]
|
||||
self._attr_native_unit_of_measurement = SENSOR_TYPES[self.kind][ATTR_UNIT]
|
||||
self._attr_unique_id = f"{self.coordinator.latitude}-{self.coordinator.longitude}-{self.kind.lower()}"
|
||||
self._attr_name = f"AirNow {description.name}"
|
||||
self._attr_unique_id = (
|
||||
f"{coordinator.latitude}-{coordinator.longitude}-{description.key.lower()}"
|
||||
)
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
"""Return the state."""
|
||||
self._state = self.coordinator.data[self.kind]
|
||||
self._state = self.coordinator.data[self.entity_description.key]
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
if self.kind == ATTR_API_AQI:
|
||||
if self.entity_description.key == ATTR_API_AQI:
|
||||
self._attrs[SENSOR_AQI_ATTR_DESCR] = self.coordinator.data[
|
||||
ATTR_API_AQI_DESCRIPTION
|
||||
]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec \u00e0 la connexion",
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_auth": "Authentification invalide",
|
||||
"invalid_location": "Aucun r\u00e9sultat trouv\u00e9 pour cet emplacement",
|
||||
"unknown": "Erreur inattendue"
|
||||
@@ -12,7 +12,7 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "Cl\u00e9 API",
|
||||
"api_key": "Cl\u00e9 d'API",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"radius": "Rayon d'action de la station (en miles, facultatif)"
|
||||
|
||||
9
homeassistant/components/airtouch4/translations/el.json
Normal file
9
homeassistant/components/airtouch4/translations/el.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b1 \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 {intergration}."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
homeassistant/components/airtouch4/translations/es.json
Normal file
15
homeassistant/components/airtouch4/translations/es.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"no_units": "No se pudo encontrar ning\u00fan grupo AirTouch 4."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Anfitri\u00f3n"
|
||||
},
|
||||
"title": "Configura los detalles de conexi\u00f3n de tu AirTouch 4."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
homeassistant/components/airtouch4/translations/fr.json
Normal file
17
homeassistant/components/airtouch4/translations/fr.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "H\u00f4te"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
"step": {
|
||||
"geography_by_coords": {
|
||||
"data": {
|
||||
"api_key": "Clef d'API",
|
||||
"api_key": "Cl\u00e9 d'API",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude"
|
||||
},
|
||||
@@ -22,7 +22,7 @@
|
||||
},
|
||||
"geography_by_name": {
|
||||
"data": {
|
||||
"api_key": "Clef d'API",
|
||||
"api_key": "Cl\u00e9 d'API",
|
||||
"city": "Ville",
|
||||
"country": "Pays",
|
||||
"state": "Etat"
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
"s2": "Di\u00f3xido de azufre"
|
||||
},
|
||||
"airvisual__pollutant_level": {
|
||||
"good": "Bien",
|
||||
"hazardous": "Peligroso",
|
||||
"good": "Bueno",
|
||||
"hazardous": "Da\u00f1ino",
|
||||
"moderate": "Moderado",
|
||||
"unhealthy": "Insalubre",
|
||||
"unhealthy_sensitive": "Incorrecto para grupos sensibles",
|
||||
"unhealthy_sensitive": "Insalubre para grupos sensibles",
|
||||
"very_unhealthy": "Muy poco saludable"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"state": {
|
||||
"airvisual__pollutant_label": {
|
||||
"co": "Tlenek w\u0119gla",
|
||||
"n2": "Dwutlenek azotu",
|
||||
"o3": "Ozon",
|
||||
"co": "tlenek w\u0119gla",
|
||||
"n2": "dwutlenek azotu",
|
||||
"o3": "ozon",
|
||||
"p1": "PM10",
|
||||
"p2": "PM2.5",
|
||||
"s2": "Dwutlenek siarki"
|
||||
"s2": "dwutlenek siarki"
|
||||
},
|
||||
"airvisual__pollutant_level": {
|
||||
"good": "dobry",
|
||||
|
||||
@@ -11,7 +11,10 @@ from homeassistant.components.alarm_control_panel.const import (
|
||||
SUPPORT_ALARM_ARM_NIGHT,
|
||||
SUPPORT_ALARM_ARM_VACATION,
|
||||
)
|
||||
from homeassistant.components.automation import AutomationActionType
|
||||
from homeassistant.components.automation import (
|
||||
AutomationActionType,
|
||||
AutomationTriggerInfo,
|
||||
)
|
||||
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA
|
||||
from homeassistant.components.homeassistant.triggers import state as state_trigger
|
||||
from homeassistant.const import (
|
||||
@@ -129,7 +132,7 @@ async def async_attach_trigger(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
action: AutomationActionType,
|
||||
automation_info: dict,
|
||||
automation_info: AutomationTriggerInfo,
|
||||
) -> CALLBACK_TYPE:
|
||||
"""Attach a trigger."""
|
||||
if config[CONF_TYPE] == "triggered":
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"arm_away": "Schakel {entity_name} in voor vertrek",
|
||||
"arm_home": "Schakel {entity_name} in voor thuis",
|
||||
"arm_night": "Schakel {entity_name} in voor 's nachts",
|
||||
"arm_vacation": "Schakel {entity_name} in op vakantie",
|
||||
"arm_vacation": "Schakel {entity_name} in voor vakantie",
|
||||
"disarm": "Schakel {entity_name} uit",
|
||||
"trigger": "Laat {entity_name} afgaan"
|
||||
},
|
||||
@@ -12,7 +12,7 @@
|
||||
"is_armed_away": "{entity_name} ingeschakeld voor vertrek",
|
||||
"is_armed_home": "{entity_name} ingeschakeld voor thuis",
|
||||
"is_armed_night": "{entity_name} is ingeschakeld voor 's nachts",
|
||||
"is_armed_vacation": "{entity_name} is in vakantie geschakeld",
|
||||
"is_armed_vacation": "{entity_name} is ingeschakeld voor vakantie",
|
||||
"is_disarmed": "{entity_name} is uitgeschakeld",
|
||||
"is_triggered": "{entity_name} gaat af"
|
||||
},
|
||||
@@ -20,7 +20,7 @@
|
||||
"armed_away": "{entity_name} ingeschakeld voor vertrek",
|
||||
"armed_home": "{entity_name} ingeschakeld voor thuis",
|
||||
"armed_night": "{entity_name} ingeschakeld voor 's nachts",
|
||||
"armed_vacation": "{entity_name} schakelde vakantie in",
|
||||
"armed_vacation": "{entity_name} schakelde in voor vakantie",
|
||||
"disarmed": "{entity_name} uitgeschakeld",
|
||||
"triggered": "{entity_name} afgegaan"
|
||||
}
|
||||
@@ -40,5 +40,5 @@
|
||||
"triggered": "Gaat af"
|
||||
}
|
||||
},
|
||||
"title": "Alarm bedieningspaneel"
|
||||
"title": "Alarmbedieningspaneel"
|
||||
}
|
||||
@@ -1483,16 +1483,6 @@ class AlexaRangeController(AlexaCapability):
|
||||
if self.entity.state in (STATE_UNAVAILABLE, STATE_UNKNOWN, None):
|
||||
return None
|
||||
|
||||
# Fan Speed
|
||||
if self.instance == f"{fan.DOMAIN}.{fan.ATTR_SPEED}":
|
||||
speed_list = self.entity.attributes.get(fan.ATTR_SPEED_LIST)
|
||||
speed = self.entity.attributes.get(fan.ATTR_SPEED)
|
||||
if speed_list is not None and speed is not None:
|
||||
speed_index = next(
|
||||
(i for i, v in enumerate(speed_list) if v == speed), None
|
||||
)
|
||||
return speed_index
|
||||
|
||||
# Cover Position
|
||||
if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
return self.entity.attributes.get(cover.ATTR_CURRENT_POSITION)
|
||||
|
||||
@@ -535,10 +535,6 @@ class FanCapabilities(AlexaEntity):
|
||||
if supported & fan.SUPPORT_SET_SPEED:
|
||||
yield AlexaPercentageController(self.entity)
|
||||
yield AlexaPowerLevelController(self.entity)
|
||||
# The use of legacy speeds is deprecated in the schema, support will be removed after a quarter (2021.7)
|
||||
yield AlexaRangeController(
|
||||
self.entity, instance=f"{fan.DOMAIN}.{fan.ATTR_SPEED}"
|
||||
)
|
||||
if supported & fan.SUPPORT_OSCILLATE:
|
||||
yield AlexaToggleController(
|
||||
self.entity, instance=f"{fan.DOMAIN}.{fan.ATTR_OSCILLATING}"
|
||||
|
||||
@@ -1091,24 +1091,8 @@ async def async_api_set_range(hass, config, directive, context):
|
||||
data = {ATTR_ENTITY_ID: entity.entity_id}
|
||||
range_value = directive.payload["rangeValue"]
|
||||
|
||||
# Fan Speed
|
||||
if instance == f"{fan.DOMAIN}.{fan.ATTR_SPEED}":
|
||||
range_value = int(range_value)
|
||||
service = fan.SERVICE_SET_SPEED
|
||||
speed_list = entity.attributes[fan.ATTR_SPEED_LIST]
|
||||
speed = next((v for i, v in enumerate(speed_list) if i == range_value), None)
|
||||
|
||||
if not speed:
|
||||
msg = "Entity does not support value"
|
||||
raise AlexaInvalidValueError(msg)
|
||||
|
||||
if speed == fan.SPEED_OFF:
|
||||
service = fan.SERVICE_TURN_OFF
|
||||
|
||||
data[fan.ATTR_SPEED] = speed
|
||||
|
||||
# Cover Position
|
||||
elif instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
if instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
range_value = int(range_value)
|
||||
if range_value == 0:
|
||||
service = cover.SERVICE_CLOSE_COVER
|
||||
@@ -1184,29 +1168,8 @@ async def async_api_adjust_range(hass, config, directive, context):
|
||||
range_delta_default = bool(directive.payload["rangeValueDeltaDefault"])
|
||||
response_value = 0
|
||||
|
||||
# Fan Speed
|
||||
if instance == f"{fan.DOMAIN}.{fan.ATTR_SPEED}":
|
||||
range_delta = int(range_delta)
|
||||
service = fan.SERVICE_SET_SPEED
|
||||
speed_list = entity.attributes[fan.ATTR_SPEED_LIST]
|
||||
current_speed = entity.attributes[fan.ATTR_SPEED]
|
||||
current_speed_index = next(
|
||||
(i for i, v in enumerate(speed_list) if v == current_speed), 0
|
||||
)
|
||||
new_speed_index = min(
|
||||
len(speed_list) - 1, max(0, current_speed_index + range_delta)
|
||||
)
|
||||
speed = next(
|
||||
(v for i, v in enumerate(speed_list) if i == new_speed_index), None
|
||||
)
|
||||
|
||||
if speed == fan.SPEED_OFF:
|
||||
service = fan.SERVICE_TURN_OFF
|
||||
|
||||
data[fan.ATTR_SPEED] = response_value = speed
|
||||
|
||||
# Cover Position
|
||||
elif instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
if instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
range_delta = int(range_delta * 20) if range_delta_default else int(range_delta)
|
||||
service = SERVICE_SET_COVER_POSITION
|
||||
current = entity.attributes.get(cover.ATTR_POSITION)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"cannot_connect": "Impossible de se connecter au serveur Almond",
|
||||
"missing_configuration": "Veuillez consulter la documentation pour savoir comment configurer Almond.",
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"missing_configuration": "Le composant n'est pas configur\u00e9. Veuillez suivre la documentation.",
|
||||
"no_url_available": "Aucune URL disponible. Pour plus d'informations sur cette erreur, [consultez la section d'aide] ( {docs_url} )",
|
||||
"single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible."
|
||||
},
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"reauth_successful": "R\u00e9-authentification r\u00e9ussie"
|
||||
"reauth_successful": "La r\u00e9-authentification a r\u00e9ussi"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_api_key": "Cl\u00e9 API non valide"
|
||||
"invalid_api_key": "Cl\u00e9 API invalide"
|
||||
},
|
||||
"step": {
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_key": "cl\u00e9 API",
|
||||
"api_key": "Cl\u00e9 d'API",
|
||||
"description": "R\u00e9-authentifiez-vous avec votre compte Ambee."
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "cl\u00e9 API",
|
||||
"api_key": "Cl\u00e9 d'API",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"name": "Nom"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"state": {
|
||||
"ambee__risk": {
|
||||
"high": "Wysoki",
|
||||
"low": "Niski",
|
||||
"moderate": "Umiarkowany",
|
||||
"very high": "Bardzo wysoki"
|
||||
"high": "wysoki",
|
||||
"low": "niski",
|
||||
"moderate": "umiarkowany",
|
||||
"very high": "bardzo wysoki"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
"missing_configuration": "Le composant n'est pas configur\u00e9. Veuillez suivre la documentation."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Authentifi\u00e9 avec succ\u00e8s avec Ambiclimate"
|
||||
"default": "Authentification r\u00e9ussie"
|
||||
},
|
||||
"error": {
|
||||
"follow_link": "Veuillez suivre le lien et vous authentifier avant d'appuyer sur Soumettre.",
|
||||
|
||||
@@ -1,38 +1,17 @@
|
||||
"""Support for Ambient Weather Station Service."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from aioambient import Client
|
||||
from aioambient.errors import WebsocketError
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
DEVICE_CLASS_CONNECTIVITY,
|
||||
DOMAIN as BINARY_SENSOR,
|
||||
)
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_LOCATION,
|
||||
ATTR_NAME,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
CONF_API_KEY,
|
||||
DEGREE,
|
||||
DEVICE_CLASS_BATTERY,
|
||||
DEVICE_CLASS_CO2,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
DEVICE_CLASS_TIMESTAMP,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
IRRADIATION_WATTS_PER_SQUARE_METER,
|
||||
LIGHT_LUX,
|
||||
PERCENTAGE,
|
||||
PRECIPITATION_INCHES,
|
||||
PRECIPITATION_INCHES_PER_HOUR,
|
||||
PRESSURE_INHG,
|
||||
SPEED_MILES_PER_HOUR,
|
||||
TEMP_FAHRENHEIT,
|
||||
)
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
@@ -41,266 +20,43 @@ from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
async_dispatcher_send,
|
||||
)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
from homeassistant.helpers.event import async_call_later
|
||||
|
||||
from .const import (
|
||||
ATTR_LAST_DATA,
|
||||
ATTR_MONITORED_CONDITIONS,
|
||||
CONF_APP_KEY,
|
||||
DATA_CLIENT,
|
||||
DOMAIN,
|
||||
LOGGER,
|
||||
TYPE_SOLARRADIATION,
|
||||
TYPE_SOLARRADIATION_LX,
|
||||
)
|
||||
|
||||
PLATFORMS = [BINARY_SENSOR, SENSOR]
|
||||
PLATFORMS = ["binary_sensor", "sensor"]
|
||||
|
||||
DATA_CONFIG = "config"
|
||||
|
||||
DEFAULT_SOCKET_MIN_RETRY = 15
|
||||
|
||||
TYPE_24HOURRAININ = "24hourrainin"
|
||||
TYPE_BAROMABSIN = "baromabsin"
|
||||
TYPE_BAROMRELIN = "baromrelin"
|
||||
TYPE_BATT1 = "batt1"
|
||||
TYPE_BATT10 = "batt10"
|
||||
TYPE_BATT2 = "batt2"
|
||||
TYPE_BATT3 = "batt3"
|
||||
TYPE_BATT4 = "batt4"
|
||||
TYPE_BATT5 = "batt5"
|
||||
TYPE_BATT6 = "batt6"
|
||||
TYPE_BATT7 = "batt7"
|
||||
TYPE_BATT8 = "batt8"
|
||||
TYPE_BATT9 = "batt9"
|
||||
TYPE_BATT_CO2 = "batt_co2"
|
||||
TYPE_BATTOUT = "battout"
|
||||
TYPE_CO2 = "co2"
|
||||
TYPE_DAILYRAININ = "dailyrainin"
|
||||
TYPE_DEWPOINT = "dewPoint"
|
||||
TYPE_EVENTRAININ = "eventrainin"
|
||||
TYPE_FEELSLIKE = "feelsLike"
|
||||
TYPE_HOURLYRAININ = "hourlyrainin"
|
||||
TYPE_HUMIDITY = "humidity"
|
||||
TYPE_HUMIDITY1 = "humidity1"
|
||||
TYPE_HUMIDITY10 = "humidity10"
|
||||
TYPE_HUMIDITY2 = "humidity2"
|
||||
TYPE_HUMIDITY3 = "humidity3"
|
||||
TYPE_HUMIDITY4 = "humidity4"
|
||||
TYPE_HUMIDITY5 = "humidity5"
|
||||
TYPE_HUMIDITY6 = "humidity6"
|
||||
TYPE_HUMIDITY7 = "humidity7"
|
||||
TYPE_HUMIDITY8 = "humidity8"
|
||||
TYPE_HUMIDITY9 = "humidity9"
|
||||
TYPE_HUMIDITYIN = "humidityin"
|
||||
TYPE_LASTRAIN = "lastRain"
|
||||
TYPE_MAXDAILYGUST = "maxdailygust"
|
||||
TYPE_MONTHLYRAININ = "monthlyrainin"
|
||||
TYPE_PM25 = "pm25"
|
||||
TYPE_PM25_24H = "pm25_24h"
|
||||
TYPE_PM25_BATT = "batt_25"
|
||||
TYPE_PM25_IN = "pm25_in"
|
||||
TYPE_PM25_IN_24H = "pm25_in_24h"
|
||||
TYPE_PM25IN_BATT = "batt_25in"
|
||||
TYPE_RELAY1 = "relay1"
|
||||
TYPE_RELAY10 = "relay10"
|
||||
TYPE_RELAY2 = "relay2"
|
||||
TYPE_RELAY3 = "relay3"
|
||||
TYPE_RELAY4 = "relay4"
|
||||
TYPE_RELAY5 = "relay5"
|
||||
TYPE_RELAY6 = "relay6"
|
||||
TYPE_RELAY7 = "relay7"
|
||||
TYPE_RELAY8 = "relay8"
|
||||
TYPE_RELAY9 = "relay9"
|
||||
TYPE_SOILHUM1 = "soilhum1"
|
||||
TYPE_SOILHUM10 = "soilhum10"
|
||||
TYPE_SOILHUM2 = "soilhum2"
|
||||
TYPE_SOILHUM3 = "soilhum3"
|
||||
TYPE_SOILHUM4 = "soilhum4"
|
||||
TYPE_SOILHUM5 = "soilhum5"
|
||||
TYPE_SOILHUM6 = "soilhum6"
|
||||
TYPE_SOILHUM7 = "soilhum7"
|
||||
TYPE_SOILHUM8 = "soilhum8"
|
||||
TYPE_SOILHUM9 = "soilhum9"
|
||||
TYPE_SOILTEMP1F = "soiltemp1f"
|
||||
TYPE_SOILTEMP10F = "soiltemp10f"
|
||||
TYPE_SOILTEMP2F = "soiltemp2f"
|
||||
TYPE_SOILTEMP3F = "soiltemp3f"
|
||||
TYPE_SOILTEMP4F = "soiltemp4f"
|
||||
TYPE_SOILTEMP5F = "soiltemp5f"
|
||||
TYPE_SOILTEMP6F = "soiltemp6f"
|
||||
TYPE_SOILTEMP7F = "soiltemp7f"
|
||||
TYPE_SOILTEMP8F = "soiltemp8f"
|
||||
TYPE_SOILTEMP9F = "soiltemp9f"
|
||||
TYPE_SOLARRADIATION = "solarradiation"
|
||||
TYPE_SOLARRADIATION_LX = "solarradiation_lx"
|
||||
TYPE_TEMP10F = "temp10f"
|
||||
TYPE_TEMP1F = "temp1f"
|
||||
TYPE_TEMP2F = "temp2f"
|
||||
TYPE_TEMP3F = "temp3f"
|
||||
TYPE_TEMP4F = "temp4f"
|
||||
TYPE_TEMP5F = "temp5f"
|
||||
TYPE_TEMP6F = "temp6f"
|
||||
TYPE_TEMP7F = "temp7f"
|
||||
TYPE_TEMP8F = "temp8f"
|
||||
TYPE_TEMP9F = "temp9f"
|
||||
TYPE_TEMPF = "tempf"
|
||||
TYPE_TEMPINF = "tempinf"
|
||||
TYPE_TOTALRAININ = "totalrainin"
|
||||
TYPE_UV = "uv"
|
||||
TYPE_WEEKLYRAININ = "weeklyrainin"
|
||||
TYPE_WINDDIR = "winddir"
|
||||
TYPE_WINDDIR_AVG10M = "winddir_avg10m"
|
||||
TYPE_WINDDIR_AVG2M = "winddir_avg2m"
|
||||
TYPE_WINDGUSTDIR = "windgustdir"
|
||||
TYPE_WINDGUSTMPH = "windgustmph"
|
||||
TYPE_WINDSPDMPH_AVG10M = "windspdmph_avg10m"
|
||||
TYPE_WINDSPDMPH_AVG2M = "windspdmph_avg2m"
|
||||
TYPE_WINDSPEEDMPH = "windspeedmph"
|
||||
TYPE_YEARLYRAININ = "yearlyrainin"
|
||||
SENSOR_TYPES = {
|
||||
TYPE_24HOURRAININ: ("24 Hr Rain", PRECIPITATION_INCHES, SENSOR, None),
|
||||
TYPE_BAROMABSIN: ("Abs Pressure", PRESSURE_INHG, SENSOR, DEVICE_CLASS_PRESSURE),
|
||||
TYPE_BAROMRELIN: ("Rel Pressure", PRESSURE_INHG, SENSOR, DEVICE_CLASS_PRESSURE),
|
||||
TYPE_BATT10: ("Battery 10", None, BINARY_SENSOR, DEVICE_CLASS_BATTERY),
|
||||
TYPE_BATT1: ("Battery 1", None, BINARY_SENSOR, DEVICE_CLASS_BATTERY),
|
||||
TYPE_BATT2: ("Battery 2", None, BINARY_SENSOR, DEVICE_CLASS_BATTERY),
|
||||
TYPE_BATT3: ("Battery 3", None, BINARY_SENSOR, DEVICE_CLASS_BATTERY),
|
||||
TYPE_BATT4: ("Battery 4", None, BINARY_SENSOR, DEVICE_CLASS_BATTERY),
|
||||
TYPE_BATT5: ("Battery 5", None, BINARY_SENSOR, DEVICE_CLASS_BATTERY),
|
||||
TYPE_BATT6: ("Battery 6", None, BINARY_SENSOR, DEVICE_CLASS_BATTERY),
|
||||
TYPE_BATT7: ("Battery 7", None, BINARY_SENSOR, DEVICE_CLASS_BATTERY),
|
||||
TYPE_BATT8: ("Battery 8", None, BINARY_SENSOR, DEVICE_CLASS_BATTERY),
|
||||
TYPE_BATT9: ("Battery 9", None, BINARY_SENSOR, DEVICE_CLASS_BATTERY),
|
||||
TYPE_BATTOUT: ("Battery", None, BINARY_SENSOR, DEVICE_CLASS_BATTERY),
|
||||
TYPE_BATT_CO2: ("CO2 Battery", None, BINARY_SENSOR, DEVICE_CLASS_BATTERY),
|
||||
TYPE_CO2: ("co2", CONCENTRATION_PARTS_PER_MILLION, SENSOR, DEVICE_CLASS_CO2),
|
||||
TYPE_DAILYRAININ: ("Daily Rain", PRECIPITATION_INCHES, SENSOR, None),
|
||||
TYPE_DEWPOINT: ("Dew Point", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_EVENTRAININ: ("Event Rain", PRECIPITATION_INCHES, SENSOR, None),
|
||||
TYPE_FEELSLIKE: ("Feels Like", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_HOURLYRAININ: (
|
||||
"Hourly Rain Rate",
|
||||
PRECIPITATION_INCHES_PER_HOUR,
|
||||
SENSOR,
|
||||
None,
|
||||
),
|
||||
TYPE_HUMIDITY10: ("Humidity 10", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_HUMIDITY1: ("Humidity 1", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_HUMIDITY2: ("Humidity 2", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_HUMIDITY3: ("Humidity 3", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_HUMIDITY4: ("Humidity 4", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_HUMIDITY5: ("Humidity 5", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_HUMIDITY6: ("Humidity 6", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_HUMIDITY7: ("Humidity 7", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_HUMIDITY8: ("Humidity 8", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_HUMIDITY9: ("Humidity 9", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_HUMIDITY: ("Humidity", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_HUMIDITYIN: ("Humidity In", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_LASTRAIN: ("Last Rain", None, SENSOR, DEVICE_CLASS_TIMESTAMP),
|
||||
TYPE_MAXDAILYGUST: ("Max Gust", SPEED_MILES_PER_HOUR, SENSOR, None),
|
||||
TYPE_MONTHLYRAININ: ("Monthly Rain", PRECIPITATION_INCHES, SENSOR, None),
|
||||
TYPE_PM25_24H: (
|
||||
"PM25 24h Avg",
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
SENSOR,
|
||||
None,
|
||||
),
|
||||
TYPE_PM25_BATT: ("PM25 Battery", None, BINARY_SENSOR, DEVICE_CLASS_BATTERY),
|
||||
TYPE_PM25_IN: (
|
||||
"PM25 Indoor",
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
SENSOR,
|
||||
None,
|
||||
),
|
||||
TYPE_PM25_IN_24H: (
|
||||
"PM25 Indoor 24h Avg",
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
SENSOR,
|
||||
None,
|
||||
),
|
||||
TYPE_PM25: ("PM25", CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, SENSOR, None),
|
||||
TYPE_PM25IN_BATT: (
|
||||
"PM25 Indoor Battery",
|
||||
None,
|
||||
BINARY_SENSOR,
|
||||
DEVICE_CLASS_BATTERY,
|
||||
),
|
||||
TYPE_RELAY10: ("Relay 10", None, BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY1: ("Relay 1", None, BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY2: ("Relay 2", None, BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY3: ("Relay 3", None, BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY4: ("Relay 4", None, BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY5: ("Relay 5", None, BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY6: ("Relay 6", None, BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY7: ("Relay 7", None, BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY8: ("Relay 8", None, BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY9: ("Relay 9", None, BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_SOILHUM10: ("Soil Humidity 10", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_SOILHUM1: ("Soil Humidity 1", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_SOILHUM2: ("Soil Humidity 2", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_SOILHUM3: ("Soil Humidity 3", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_SOILHUM4: ("Soil Humidity 4", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_SOILHUM5: ("Soil Humidity 5", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_SOILHUM6: ("Soil Humidity 6", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_SOILHUM7: ("Soil Humidity 7", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_SOILHUM8: ("Soil Humidity 8", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_SOILHUM9: ("Soil Humidity 9", PERCENTAGE, SENSOR, DEVICE_CLASS_HUMIDITY),
|
||||
TYPE_SOILTEMP10F: (
|
||||
"Soil Temp 10",
|
||||
TEMP_FAHRENHEIT,
|
||||
SENSOR,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
TYPE_SOILTEMP1F: ("Soil Temp 1", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_SOILTEMP2F: ("Soil Temp 2", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_SOILTEMP3F: ("Soil Temp 3", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_SOILTEMP4F: ("Soil Temp 4", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_SOILTEMP5F: ("Soil Temp 5", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_SOILTEMP6F: ("Soil Temp 6", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_SOILTEMP7F: ("Soil Temp 7", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_SOILTEMP8F: ("Soil Temp 8", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_SOILTEMP9F: ("Soil Temp 9", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_SOLARRADIATION: (
|
||||
"Solar Rad",
|
||||
IRRADIATION_WATTS_PER_SQUARE_METER,
|
||||
SENSOR,
|
||||
None,
|
||||
),
|
||||
TYPE_SOLARRADIATION_LX: (
|
||||
"Solar Rad (lx)",
|
||||
LIGHT_LUX,
|
||||
SENSOR,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
),
|
||||
TYPE_TEMP10F: ("Temp 10", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_TEMP1F: ("Temp 1", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_TEMP2F: ("Temp 2", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_TEMP3F: ("Temp 3", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_TEMP4F: ("Temp 4", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_TEMP5F: ("Temp 5", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_TEMP6F: ("Temp 6", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_TEMP7F: ("Temp 7", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_TEMP8F: ("Temp 8", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_TEMP9F: ("Temp 9", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_TEMPF: ("Temp", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_TEMPINF: ("Inside Temp", TEMP_FAHRENHEIT, SENSOR, DEVICE_CLASS_TEMPERATURE),
|
||||
TYPE_TOTALRAININ: ("Lifetime Rain", PRECIPITATION_INCHES, SENSOR, None),
|
||||
TYPE_UV: ("uv", "Index", SENSOR, None),
|
||||
TYPE_WEEKLYRAININ: ("Weekly Rain", PRECIPITATION_INCHES, SENSOR, None),
|
||||
TYPE_WINDDIR: ("Wind Dir", DEGREE, SENSOR, None),
|
||||
TYPE_WINDDIR_AVG10M: ("Wind Dir Avg 10m", DEGREE, SENSOR, None),
|
||||
TYPE_WINDDIR_AVG2M: ("Wind Dir Avg 2m", SPEED_MILES_PER_HOUR, SENSOR, None),
|
||||
TYPE_WINDGUSTDIR: ("Gust Dir", DEGREE, SENSOR, None),
|
||||
TYPE_WINDGUSTMPH: ("Wind Gust", SPEED_MILES_PER_HOUR, SENSOR, None),
|
||||
TYPE_WINDSPDMPH_AVG10M: ("Wind Avg 10m", SPEED_MILES_PER_HOUR, SENSOR, None),
|
||||
TYPE_WINDSPDMPH_AVG2M: ("Wind Avg 2m", SPEED_MILES_PER_HOUR, SENSOR, None),
|
||||
TYPE_WINDSPEEDMPH: ("Wind Speed", SPEED_MILES_PER_HOUR, SENSOR, None),
|
||||
TYPE_YEARLYRAININ: ("Yearly Rain", PRECIPITATION_INCHES, SENSOR, None),
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = cv.deprecated(DOMAIN)
|
||||
|
||||
|
||||
@callback
|
||||
def async_wm2_to_lx(value: float) -> int:
|
||||
"""Calculate illuminance (in lux)."""
|
||||
return round(value / 0.0079)
|
||||
|
||||
|
||||
@callback
|
||||
def async_hydrate_station_data(data: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Hydrate station data with addition or normalized data."""
|
||||
if (irradiation := data.get(TYPE_SOLARRADIATION)) is not None:
|
||||
data[TYPE_SOLARRADIATION_LX] = async_wm2_to_lx(irradiation)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
"""Set up the Ambient PWS as config entry."""
|
||||
hass.data.setdefault(DOMAIN, {DATA_CLIENT: {}})
|
||||
@@ -319,6 +75,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
config_entry.data[CONF_API_KEY],
|
||||
config_entry.data[CONF_APP_KEY],
|
||||
session=session,
|
||||
logger=LOGGER,
|
||||
),
|
||||
)
|
||||
hass.loop.create_task(ambient.ws_connect())
|
||||
@@ -405,13 +162,14 @@ class AmbientStation:
|
||||
|
||||
def on_data(data: dict) -> None:
|
||||
"""Define a handler to fire when the data is received."""
|
||||
mac_address = data["macAddress"]
|
||||
if data != self.stations[mac_address][ATTR_LAST_DATA]:
|
||||
LOGGER.debug("New data received: %s", data)
|
||||
self.stations[mac_address][ATTR_LAST_DATA] = data
|
||||
async_dispatcher_send(
|
||||
self._hass, f"ambient_station_data_update_{mac_address}"
|
||||
)
|
||||
mac = data["macAddress"]
|
||||
|
||||
if data == self.stations[mac][ATTR_LAST_DATA]:
|
||||
return
|
||||
|
||||
LOGGER.debug("New data received: %s", data)
|
||||
self.stations[mac][ATTR_LAST_DATA] = async_hydrate_station_data(data)
|
||||
async_dispatcher_send(self._hass, f"ambient_station_data_update_{mac}")
|
||||
|
||||
def on_disconnect() -> None:
|
||||
"""Define a handler to fire when the websocket is disconnected."""
|
||||
@@ -420,26 +178,19 @@ class AmbientStation:
|
||||
def on_subscribed(data: dict) -> None:
|
||||
"""Define a handler to fire when the subscription is set."""
|
||||
for station in data["devices"]:
|
||||
if station["macAddress"] in self.stations:
|
||||
mac = station["macAddress"]
|
||||
|
||||
if mac in self.stations:
|
||||
continue
|
||||
|
||||
LOGGER.debug("New station subscription: %s", data)
|
||||
|
||||
# Only create entities based on the data coming through the socket.
|
||||
# If the user is monitoring brightness (in W/m^2), make sure we also
|
||||
# add a calculated sensor for the same data measured in lx:
|
||||
monitored_conditions = [
|
||||
k for k in station["lastData"] if k in SENSOR_TYPES
|
||||
]
|
||||
if TYPE_SOLARRADIATION in monitored_conditions:
|
||||
monitored_conditions.append(TYPE_SOLARRADIATION_LX)
|
||||
self.stations[station["macAddress"]] = {
|
||||
ATTR_LAST_DATA: station["lastData"],
|
||||
self.stations[mac] = {
|
||||
ATTR_LAST_DATA: async_hydrate_station_data(station["lastData"]),
|
||||
ATTR_LOCATION: station.get("info", {}).get("location"),
|
||||
ATTR_MONITORED_CONDITIONS: monitored_conditions,
|
||||
ATTR_NAME: station.get("info", {}).get(
|
||||
"name", station["macAddress"]
|
||||
),
|
||||
ATTR_NAME: station.get("info", {}).get("name", mac),
|
||||
}
|
||||
|
||||
# If the websocket disconnects and reconnects, the on_subscribed
|
||||
# handler will get called again; in that case, we don't want to
|
||||
# attempt forward setup of the config entry (because it will have
|
||||
@@ -466,28 +217,26 @@ class AmbientStation:
|
||||
class AmbientWeatherEntity(Entity):
|
||||
"""Define a base Ambient PWS entity."""
|
||||
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ambient: AmbientStation,
|
||||
mac_address: str,
|
||||
station_name: str,
|
||||
sensor_type: str,
|
||||
sensor_name: str,
|
||||
device_class: str | None,
|
||||
description: EntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
self._ambient = ambient
|
||||
self._attr_device_class = device_class
|
||||
self._attr_device_info = {
|
||||
"identifiers": {(DOMAIN, mac_address)},
|
||||
"name": station_name,
|
||||
"manufacturer": "Ambient Weather",
|
||||
}
|
||||
self._attr_name = f"{station_name}_{sensor_name}"
|
||||
self._attr_should_poll = False
|
||||
self._attr_unique_id = f"{mac_address}_{sensor_type}"
|
||||
self._attr_name = f"{station_name}_{description.name}"
|
||||
self._attr_unique_id = f"{mac_address}_{description.key}"
|
||||
self._mac_address = mac_address
|
||||
self._sensor_type = sensor_type
|
||||
self.entity_description = description
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Register callbacks."""
|
||||
@@ -495,18 +244,18 @@ class AmbientWeatherEntity(Entity):
|
||||
@callback
|
||||
def update() -> None:
|
||||
"""Update the state."""
|
||||
if self._sensor_type == TYPE_SOLARRADIATION_LX:
|
||||
if self.entity_description.key == TYPE_SOLARRADIATION_LX:
|
||||
self._attr_available = (
|
||||
self._ambient.stations[self._mac_address][ATTR_LAST_DATA].get(
|
||||
self._ambient.stations[self._mac_address][ATTR_LAST_DATA][
|
||||
TYPE_SOLARRADIATION
|
||||
)
|
||||
]
|
||||
is not None
|
||||
)
|
||||
else:
|
||||
self._attr_available = (
|
||||
self._ambient.stations[self._mac_address][ATTR_LAST_DATA].get(
|
||||
self._sensor_type
|
||||
)
|
||||
self._ambient.stations[self._mac_address][ATTR_LAST_DATA][
|
||||
self.entity_description.key
|
||||
]
|
||||
is not None
|
||||
)
|
||||
|
||||
|
||||
@@ -1,34 +1,209 @@
|
||||
"""Support for Ambient Weather Station binary sensors."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
DOMAIN as BINARY_SENSOR,
|
||||
DEVICE_CLASS_BATTERY,
|
||||
DEVICE_CLASS_CONNECTIVITY,
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_NAME
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import (
|
||||
SENSOR_TYPES,
|
||||
TYPE_BATT1,
|
||||
TYPE_BATT2,
|
||||
TYPE_BATT3,
|
||||
TYPE_BATT4,
|
||||
TYPE_BATT5,
|
||||
TYPE_BATT6,
|
||||
TYPE_BATT7,
|
||||
TYPE_BATT8,
|
||||
TYPE_BATT9,
|
||||
TYPE_BATT10,
|
||||
TYPE_BATT_CO2,
|
||||
TYPE_BATTOUT,
|
||||
TYPE_PM25_BATT,
|
||||
TYPE_PM25IN_BATT,
|
||||
AmbientWeatherEntity,
|
||||
from . import AmbientWeatherEntity
|
||||
from .const import ATTR_LAST_DATA, DATA_CLIENT, DOMAIN
|
||||
|
||||
TYPE_BATT1 = "batt1"
|
||||
TYPE_BATT10 = "batt10"
|
||||
TYPE_BATT2 = "batt2"
|
||||
TYPE_BATT3 = "batt3"
|
||||
TYPE_BATT4 = "batt4"
|
||||
TYPE_BATT5 = "batt5"
|
||||
TYPE_BATT6 = "batt6"
|
||||
TYPE_BATT7 = "batt7"
|
||||
TYPE_BATT8 = "batt8"
|
||||
TYPE_BATT9 = "batt9"
|
||||
TYPE_BATT_CO2 = "batt_co2"
|
||||
TYPE_BATTOUT = "battout"
|
||||
TYPE_PM25_BATT = "batt_25"
|
||||
TYPE_PM25IN_BATT = "batt_25in"
|
||||
TYPE_RELAY1 = "relay1"
|
||||
TYPE_RELAY10 = "relay10"
|
||||
TYPE_RELAY2 = "relay2"
|
||||
TYPE_RELAY3 = "relay3"
|
||||
TYPE_RELAY4 = "relay4"
|
||||
TYPE_RELAY5 = "relay5"
|
||||
TYPE_RELAY6 = "relay6"
|
||||
TYPE_RELAY7 = "relay7"
|
||||
TYPE_RELAY8 = "relay8"
|
||||
TYPE_RELAY9 = "relay9"
|
||||
|
||||
|
||||
@dataclass
|
||||
class AmbientBinarySensorDescriptionMixin:
|
||||
"""Define an entity description mixin for binary sensors."""
|
||||
|
||||
on_state: Literal[0, 1]
|
||||
|
||||
|
||||
@dataclass
|
||||
class AmbientBinarySensorDescription(
|
||||
BinarySensorEntityDescription, AmbientBinarySensorDescriptionMixin
|
||||
):
|
||||
"""Describe an Ambient PWS binary sensor."""
|
||||
|
||||
|
||||
BINARY_SENSOR_DESCRIPTIONS = (
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATTOUT,
|
||||
name="Battery",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT1,
|
||||
name="Battery 1",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT2,
|
||||
name="Battery 2",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT3,
|
||||
name="Battery 3",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT4,
|
||||
name="Battery 4",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT5,
|
||||
name="Battery 5",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT6,
|
||||
name="Battery 6",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT7,
|
||||
name="Battery 7",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT8,
|
||||
name="Battery 8",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT9,
|
||||
name="Battery 9",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT10,
|
||||
name="Battery 10",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT_CO2,
|
||||
name="CO2 Battery",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_PM25IN_BATT,
|
||||
name="PM25 Indoor Battery",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_PM25_BATT,
|
||||
name="PM25 Battery",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY1,
|
||||
name="Relay 1",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY2,
|
||||
name="Relay 2",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY3,
|
||||
name="Relay 3",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY4,
|
||||
name="Relay 4",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY5,
|
||||
name="Relay 5",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY6,
|
||||
name="Relay 6",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY7,
|
||||
name="Relay 7",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY8,
|
||||
name="Relay 8",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY9,
|
||||
name="Relay 9",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY10,
|
||||
name="Relay 10",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
on_state=1,
|
||||
),
|
||||
)
|
||||
from .const import ATTR_LAST_DATA, ATTR_MONITORED_CONDITIONS, DATA_CLIENT, DOMAIN
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -37,51 +212,29 @@ async def async_setup_entry(
|
||||
"""Set up Ambient PWS binary sensors based on a config entry."""
|
||||
ambient = hass.data[DOMAIN][DATA_CLIENT][entry.entry_id]
|
||||
|
||||
binary_sensor_list = []
|
||||
for mac_address, station in ambient.stations.items():
|
||||
for condition in station[ATTR_MONITORED_CONDITIONS]:
|
||||
name, _, kind, device_class = SENSOR_TYPES[condition]
|
||||
if kind == BINARY_SENSOR:
|
||||
binary_sensor_list.append(
|
||||
AmbientWeatherBinarySensor(
|
||||
ambient,
|
||||
mac_address,
|
||||
station[ATTR_NAME],
|
||||
condition,
|
||||
name,
|
||||
device_class,
|
||||
)
|
||||
)
|
||||
|
||||
async_add_entities(binary_sensor_list)
|
||||
async_add_entities(
|
||||
[
|
||||
AmbientWeatherBinarySensor(
|
||||
ambient, mac_address, station[ATTR_NAME], description
|
||||
)
|
||||
for mac_address, station in ambient.stations.items()
|
||||
for description in BINARY_SENSOR_DESCRIPTIONS
|
||||
if description.key in station[ATTR_LAST_DATA]
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class AmbientWeatherBinarySensor(AmbientWeatherEntity, BinarySensorEntity):
|
||||
"""Define an Ambient binary sensor."""
|
||||
|
||||
entity_description: AmbientBinarySensorDescription
|
||||
|
||||
@callback
|
||||
def update_from_latest_data(self) -> None:
|
||||
"""Fetch new state data for the entity."""
|
||||
state = self._ambient.stations[self._mac_address][ATTR_LAST_DATA].get(
|
||||
self._sensor_type
|
||||
self._attr_is_on = (
|
||||
self._ambient.stations[self._mac_address][ATTR_LAST_DATA][
|
||||
self.entity_description.key
|
||||
]
|
||||
== self.entity_description.on_state
|
||||
)
|
||||
|
||||
if self._sensor_type in (
|
||||
TYPE_BATT1,
|
||||
TYPE_BATT10,
|
||||
TYPE_BATT2,
|
||||
TYPE_BATT3,
|
||||
TYPE_BATT4,
|
||||
TYPE_BATT5,
|
||||
TYPE_BATT6,
|
||||
TYPE_BATT7,
|
||||
TYPE_BATT8,
|
||||
TYPE_BATT9,
|
||||
TYPE_BATT_CO2,
|
||||
TYPE_BATTOUT,
|
||||
TYPE_PM25_BATT,
|
||||
TYPE_PM25IN_BATT,
|
||||
):
|
||||
self._attr_is_on = state == 0
|
||||
else:
|
||||
self._attr_is_on = state == 1
|
||||
|
||||
@@ -5,8 +5,10 @@ DOMAIN = "ambient_station"
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
|
||||
ATTR_LAST_DATA = "last_data"
|
||||
ATTR_MONITORED_CONDITIONS = "monitored_conditions"
|
||||
|
||||
CONF_APP_KEY = "app_key"
|
||||
|
||||
DATA_CLIENT = "data_client"
|
||||
|
||||
TYPE_SOLARRADIATION = "solarradiation"
|
||||
TYPE_SOLARRADIATION_LX = "solarradiation_lx"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Ambient Weather Station",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/ambient_station",
|
||||
"requirements": ["aioambient==1.2.6"],
|
||||
"requirements": ["aioambient==1.3.0"],
|
||||
"codeowners": ["@bachya"],
|
||||
"iot_class": "cloud_push"
|
||||
}
|
||||
|
||||
@@ -1,20 +1,554 @@
|
||||
"""Support for Ambient Weather Station sensors."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR, SensorEntity
|
||||
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_NAME
|
||||
from homeassistant.const import (
|
||||
ATTR_NAME,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
DEGREE,
|
||||
DEVICE_CLASS_CO2,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
DEVICE_CLASS_PM25,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
DEVICE_CLASS_TIMESTAMP,
|
||||
IRRADIATION_WATTS_PER_SQUARE_METER,
|
||||
LIGHT_LUX,
|
||||
PERCENTAGE,
|
||||
PRECIPITATION_INCHES,
|
||||
PRECIPITATION_INCHES_PER_HOUR,
|
||||
PRESSURE_INHG,
|
||||
SPEED_MILES_PER_HOUR,
|
||||
TEMP_FAHRENHEIT,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import (
|
||||
SENSOR_TYPES,
|
||||
TYPE_SOLARRADIATION,
|
||||
TYPE_SOLARRADIATION_LX,
|
||||
AmbientStation,
|
||||
AmbientWeatherEntity,
|
||||
from . import TYPE_SOLARRADIATION, TYPE_SOLARRADIATION_LX, AmbientWeatherEntity
|
||||
from .const import ATTR_LAST_DATA, DATA_CLIENT, DOMAIN
|
||||
|
||||
TYPE_24HOURRAININ = "24hourrainin"
|
||||
TYPE_BAROMABSIN = "baromabsin"
|
||||
TYPE_BAROMRELIN = "baromrelin"
|
||||
TYPE_CO2 = "co2"
|
||||
TYPE_DAILYRAININ = "dailyrainin"
|
||||
TYPE_DEWPOINT = "dewPoint"
|
||||
TYPE_EVENTRAININ = "eventrainin"
|
||||
TYPE_FEELSLIKE = "feelsLike"
|
||||
TYPE_HOURLYRAININ = "hourlyrainin"
|
||||
TYPE_HUMIDITY = "humidity"
|
||||
TYPE_HUMIDITY1 = "humidity1"
|
||||
TYPE_HUMIDITY10 = "humidity10"
|
||||
TYPE_HUMIDITY2 = "humidity2"
|
||||
TYPE_HUMIDITY3 = "humidity3"
|
||||
TYPE_HUMIDITY4 = "humidity4"
|
||||
TYPE_HUMIDITY5 = "humidity5"
|
||||
TYPE_HUMIDITY6 = "humidity6"
|
||||
TYPE_HUMIDITY7 = "humidity7"
|
||||
TYPE_HUMIDITY8 = "humidity8"
|
||||
TYPE_HUMIDITY9 = "humidity9"
|
||||
TYPE_HUMIDITYIN = "humidityin"
|
||||
TYPE_LASTRAIN = "lastRain"
|
||||
TYPE_MAXDAILYGUST = "maxdailygust"
|
||||
TYPE_MONTHLYRAININ = "monthlyrainin"
|
||||
TYPE_PM25 = "pm25"
|
||||
TYPE_PM25_24H = "pm25_24h"
|
||||
TYPE_PM25_IN = "pm25_in"
|
||||
TYPE_PM25_IN_24H = "pm25_in_24h"
|
||||
TYPE_SOILHUM1 = "soilhum1"
|
||||
TYPE_SOILHUM10 = "soilhum10"
|
||||
TYPE_SOILHUM2 = "soilhum2"
|
||||
TYPE_SOILHUM3 = "soilhum3"
|
||||
TYPE_SOILHUM4 = "soilhum4"
|
||||
TYPE_SOILHUM5 = "soilhum5"
|
||||
TYPE_SOILHUM6 = "soilhum6"
|
||||
TYPE_SOILHUM7 = "soilhum7"
|
||||
TYPE_SOILHUM8 = "soilhum8"
|
||||
TYPE_SOILHUM9 = "soilhum9"
|
||||
TYPE_SOILTEMP1F = "soiltemp1f"
|
||||
TYPE_SOILTEMP10F = "soiltemp10f"
|
||||
TYPE_SOILTEMP2F = "soiltemp2f"
|
||||
TYPE_SOILTEMP3F = "soiltemp3f"
|
||||
TYPE_SOILTEMP4F = "soiltemp4f"
|
||||
TYPE_SOILTEMP5F = "soiltemp5f"
|
||||
TYPE_SOILTEMP6F = "soiltemp6f"
|
||||
TYPE_SOILTEMP7F = "soiltemp7f"
|
||||
TYPE_SOILTEMP8F = "soiltemp8f"
|
||||
TYPE_SOILTEMP9F = "soiltemp9f"
|
||||
TYPE_TEMP10F = "temp10f"
|
||||
TYPE_TEMP1F = "temp1f"
|
||||
TYPE_TEMP2F = "temp2f"
|
||||
TYPE_TEMP3F = "temp3f"
|
||||
TYPE_TEMP4F = "temp4f"
|
||||
TYPE_TEMP5F = "temp5f"
|
||||
TYPE_TEMP6F = "temp6f"
|
||||
TYPE_TEMP7F = "temp7f"
|
||||
TYPE_TEMP8F = "temp8f"
|
||||
TYPE_TEMP9F = "temp9f"
|
||||
TYPE_TEMPF = "tempf"
|
||||
TYPE_TEMPINF = "tempinf"
|
||||
TYPE_TOTALRAININ = "totalrainin"
|
||||
TYPE_UV = "uv"
|
||||
TYPE_WEEKLYRAININ = "weeklyrainin"
|
||||
TYPE_WINDDIR = "winddir"
|
||||
TYPE_WINDDIR_AVG10M = "winddir_avg10m"
|
||||
TYPE_WINDDIR_AVG2M = "winddir_avg2m"
|
||||
TYPE_WINDGUSTDIR = "windgustdir"
|
||||
TYPE_WINDGUSTMPH = "windgustmph"
|
||||
TYPE_WINDSPDMPH_AVG10M = "windspdmph_avg10m"
|
||||
TYPE_WINDSPDMPH_AVG2M = "windspdmph_avg2m"
|
||||
TYPE_WINDSPEEDMPH = "windspeedmph"
|
||||
TYPE_YEARLYRAININ = "yearlyrainin"
|
||||
|
||||
SENSOR_DESCRIPTIONS = (
|
||||
SensorEntityDescription(
|
||||
key=TYPE_24HOURRAININ,
|
||||
name="24 Hr Rain",
|
||||
icon="mdi:water",
|
||||
native_unit_of_measurement=PRECIPITATION_INCHES,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_BAROMABSIN,
|
||||
name="Abs Pressure",
|
||||
native_unit_of_measurement=PRESSURE_INHG,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_BAROMRELIN,
|
||||
name="Rel Pressure",
|
||||
native_unit_of_measurement=PRESSURE_INHG,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_CO2,
|
||||
name="co2",
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
|
||||
device_class=DEVICE_CLASS_CO2,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_DAILYRAININ,
|
||||
name="Daily Rain",
|
||||
icon="mdi:water",
|
||||
native_unit_of_measurement=PRECIPITATION_INCHES,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_DEWPOINT,
|
||||
name="Dew Point",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_EVENTRAININ,
|
||||
name="Event Rain",
|
||||
icon="mdi:water",
|
||||
native_unit_of_measurement=PRECIPITATION_INCHES,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_FEELSLIKE,
|
||||
name="Feels Like",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_HOURLYRAININ,
|
||||
name="Hourly Rain Rate",
|
||||
icon="mdi:water",
|
||||
native_unit_of_measurement=PRECIPITATION_INCHES_PER_HOUR,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_HUMIDITY10,
|
||||
name="Humidity 10",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_HUMIDITY1,
|
||||
name="Humidity 1",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_HUMIDITY2,
|
||||
name="Humidity 2",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_HUMIDITY3,
|
||||
name="Humidity 3",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_HUMIDITY4,
|
||||
name="Humidity 4",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_HUMIDITY5,
|
||||
name="Humidity 5",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_HUMIDITY6,
|
||||
name="Humidity 6",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_HUMIDITY7,
|
||||
name="Humidity 7",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_HUMIDITY8,
|
||||
name="Humidity 8",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_HUMIDITY9,
|
||||
name="Humidity 9",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_HUMIDITY,
|
||||
name="Humidity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_HUMIDITYIN,
|
||||
name="Humidity In",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_LASTRAIN,
|
||||
name="Last Rain",
|
||||
icon="mdi:water",
|
||||
device_class=DEVICE_CLASS_TIMESTAMP,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_MAXDAILYGUST,
|
||||
name="Max Gust",
|
||||
icon="mdi:weather-windy",
|
||||
native_unit_of_measurement=SPEED_MILES_PER_HOUR,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_MONTHLYRAININ,
|
||||
name="Monthly Rain",
|
||||
icon="mdi:water",
|
||||
native_unit_of_measurement=PRECIPITATION_INCHES,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_PM25_24H,
|
||||
name="PM25 24h Avg",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
device_class=DEVICE_CLASS_PM25,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_PM25_IN,
|
||||
name="PM25 Indoor",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
device_class=DEVICE_CLASS_PM25,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_PM25_IN_24H,
|
||||
name="PM25 Indoor 24h Avg",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
device_class=DEVICE_CLASS_PM25,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_PM25,
|
||||
name="PM25",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
device_class=DEVICE_CLASS_PM25,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILHUM10,
|
||||
name="Soil Humidity 10",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILHUM1,
|
||||
name="Soil Humidity 1",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILHUM2,
|
||||
name="Soil Humidity 2",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILHUM3,
|
||||
name="Soil Humidity 3",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILHUM4,
|
||||
name="Soil Humidity 4",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILHUM5,
|
||||
name="Soil Humidity 5",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILHUM6,
|
||||
name="Soil Humidity 6",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILHUM7,
|
||||
name="Soil Humidity 7",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILHUM8,
|
||||
name="Soil Humidity 8",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILHUM9,
|
||||
name="Soil Humidity 9",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILTEMP10F,
|
||||
name="Soil Temp 10",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILTEMP1F,
|
||||
name="Soil Temp 1",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILTEMP2F,
|
||||
name="Soil Temp 2",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILTEMP3F,
|
||||
name="Soil Temp 3",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILTEMP4F,
|
||||
name="Soil Temp 4",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILTEMP5F,
|
||||
name="Soil Temp 5",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILTEMP6F,
|
||||
name="Soil Temp 6",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILTEMP7F,
|
||||
name="Soil Temp 7",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILTEMP8F,
|
||||
name="Soil Temp 8",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOILTEMP9F,
|
||||
name="Soil Temp 9",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOLARRADIATION,
|
||||
name="Solar Rad",
|
||||
native_unit_of_measurement=IRRADIATION_WATTS_PER_SQUARE_METER,
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_SOLARRADIATION_LX,
|
||||
name="Solar Rad (lx)",
|
||||
native_unit_of_measurement=LIGHT_LUX,
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_TEMP10F,
|
||||
name="Temp 10",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_TEMP1F,
|
||||
name="Temp 1",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_TEMP2F,
|
||||
name="Temp 2",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_TEMP3F,
|
||||
name="Temp 3",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_TEMP4F,
|
||||
name="Temp 4",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_TEMP5F,
|
||||
name="Temp 5",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_TEMP6F,
|
||||
name="Temp 6",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_TEMP7F,
|
||||
name="Temp 7",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_TEMP8F,
|
||||
name="Temp 8",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_TEMP9F,
|
||||
name="Temp 9",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_TEMPF,
|
||||
name="Temp",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_TEMPINF,
|
||||
name="Inside Temp",
|
||||
native_unit_of_measurement=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_TOTALRAININ,
|
||||
name="Lifetime Rain",
|
||||
icon="mdi:water",
|
||||
native_unit_of_measurement=PRECIPITATION_INCHES,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_UV,
|
||||
name="UV Index",
|
||||
native_unit_of_measurement="Index",
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_WEEKLYRAININ,
|
||||
name="Weekly Rain",
|
||||
icon="mdi:water",
|
||||
native_unit_of_measurement=PRECIPITATION_INCHES,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_WINDDIR,
|
||||
name="Wind Dir",
|
||||
icon="mdi:weather-windy",
|
||||
native_unit_of_measurement=DEGREE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_WINDDIR_AVG10M,
|
||||
name="Wind Dir Avg 10m",
|
||||
icon="mdi:weather-windy",
|
||||
native_unit_of_measurement=DEGREE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_WINDDIR_AVG2M,
|
||||
name="Wind Dir Avg 2m",
|
||||
icon="mdi:weather-windy",
|
||||
native_unit_of_measurement=SPEED_MILES_PER_HOUR,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_WINDGUSTDIR,
|
||||
name="Gust Dir",
|
||||
icon="mdi:weather-windy",
|
||||
native_unit_of_measurement=DEGREE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_WINDGUSTMPH,
|
||||
name="Wind Gust",
|
||||
icon="mdi:weather-windy",
|
||||
native_unit_of_measurement=SPEED_MILES_PER_HOUR,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_WINDSPDMPH_AVG10M,
|
||||
name="Wind Avg 10m",
|
||||
icon="mdi:weather-windy",
|
||||
native_unit_of_measurement=SPEED_MILES_PER_HOUR,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_WINDSPDMPH_AVG2M,
|
||||
name="Wind Avg 2m",
|
||||
icon="mdi:weather-windy",
|
||||
native_unit_of_measurement=SPEED_MILES_PER_HOUR,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_WINDSPEEDMPH,
|
||||
name="Wind Speed",
|
||||
icon="mdi:weather-windy",
|
||||
native_unit_of_measurement=SPEED_MILES_PER_HOUR,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=TYPE_YEARLYRAININ,
|
||||
name="Yearly Rain",
|
||||
icon="mdi:water",
|
||||
native_unit_of_measurement=PRECIPITATION_INCHES,
|
||||
),
|
||||
)
|
||||
from .const import ATTR_LAST_DATA, ATTR_MONITORED_CONDITIONS, DATA_CLIENT, DOMAIN
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -23,62 +557,22 @@ async def async_setup_entry(
|
||||
"""Set up Ambient PWS sensors based on a config entry."""
|
||||
ambient = hass.data[DOMAIN][DATA_CLIENT][entry.entry_id]
|
||||
|
||||
sensor_list = []
|
||||
for mac_address, station in ambient.stations.items():
|
||||
for condition in station[ATTR_MONITORED_CONDITIONS]:
|
||||
name, unit, kind, device_class = SENSOR_TYPES[condition]
|
||||
if kind == SENSOR:
|
||||
sensor_list.append(
|
||||
AmbientWeatherSensor(
|
||||
ambient,
|
||||
mac_address,
|
||||
station[ATTR_NAME],
|
||||
condition,
|
||||
name,
|
||||
device_class,
|
||||
unit,
|
||||
)
|
||||
)
|
||||
|
||||
async_add_entities(sensor_list)
|
||||
async_add_entities(
|
||||
[
|
||||
AmbientWeatherSensor(ambient, mac_address, station[ATTR_NAME], description)
|
||||
for mac_address, station in ambient.stations.items()
|
||||
for description in SENSOR_DESCRIPTIONS
|
||||
if description.key in station[ATTR_LAST_DATA]
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class AmbientWeatherSensor(AmbientWeatherEntity, SensorEntity):
|
||||
"""Define an Ambient sensor."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ambient: AmbientStation,
|
||||
mac_address: str,
|
||||
station_name: str,
|
||||
sensor_type: str,
|
||||
sensor_name: str,
|
||||
device_class: str | None,
|
||||
unit: str | None,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(
|
||||
ambient, mac_address, station_name, sensor_type, sensor_name, device_class
|
||||
)
|
||||
|
||||
self._attr_native_unit_of_measurement = unit
|
||||
|
||||
@callback
|
||||
def update_from_latest_data(self) -> None:
|
||||
"""Fetch new state data for the sensor."""
|
||||
if self._sensor_type == TYPE_SOLARRADIATION_LX:
|
||||
# If the user requests the solarradiation_lx sensor, use the
|
||||
# value of the solarradiation sensor and apply a very accurate
|
||||
# approximation of converting sunlight W/m^2 to lx:
|
||||
w_m2_brightness_val = self._ambient.stations[self._mac_address][
|
||||
ATTR_LAST_DATA
|
||||
].get(TYPE_SOLARRADIATION)
|
||||
|
||||
if w_m2_brightness_val is None:
|
||||
self._attr_native_value = None
|
||||
else:
|
||||
self._attr_native_value = round(float(w_m2_brightness_val) / 0.0079)
|
||||
else:
|
||||
self._attr_native_value = self._ambient.stations[self._mac_address][
|
||||
ATTR_LAST_DATA
|
||||
].get(self._sensor_type)
|
||||
self._attr_native_value = self._ambient.stations[self._mac_address][
|
||||
ATTR_LAST_DATA
|
||||
][self.entity_description.key]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Cette cl\u00e9 d'application est d\u00e9j\u00e0 utilis\u00e9e."
|
||||
"already_configured": "Le service est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"invalid_key": "Cl\u00e9 d'API et / ou cl\u00e9 d'application non valide",
|
||||
"invalid_key": "Cl\u00e9 API invalide",
|
||||
"no_devices": "Aucun appareil trouv\u00e9 dans le compte"
|
||||
},
|
||||
"step": {
|
||||
|
||||
@@ -232,8 +232,6 @@ class AmcrestBinarySensor(BinarySensorEntity):
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to signals."""
|
||||
assert self.hass is not None
|
||||
|
||||
self._unsub_dispatcher.append(
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
|
||||
@@ -180,7 +180,6 @@ class AmcrestCam(Camera):
|
||||
raise CannotSnapshot
|
||||
|
||||
async def _async_get_image(self) -> None:
|
||||
assert self.hass is not None
|
||||
try:
|
||||
# Send the request to snap a picture and return raw jpg data
|
||||
# Snapshot command needs a much longer read timeout than other commands.
|
||||
@@ -201,7 +200,6 @@ class AmcrestCam(Camera):
|
||||
self, width: int | None = None, height: int | None = None
|
||||
) -> bytes | None:
|
||||
"""Return a still image response from the camera."""
|
||||
assert self.hass is not None
|
||||
_LOGGER.debug("Take snapshot from %s", self._name)
|
||||
try:
|
||||
# Amcrest cameras only support one snapshot command at a time.
|
||||
@@ -226,7 +224,6 @@ class AmcrestCam(Camera):
|
||||
self, request: web.Request
|
||||
) -> web.StreamResponse | None:
|
||||
"""Return an MJPEG stream."""
|
||||
assert self.hass is not None
|
||||
# The snapshot implementation is handled by the parent class
|
||||
if self._stream_source == "snapshot":
|
||||
return await super().handle_async_mjpeg_stream(request)
|
||||
@@ -344,7 +341,6 @@ class AmcrestCam(Camera):
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to signals and add camera to list."""
|
||||
assert self.hass is not None
|
||||
self._unsub_dispatcher.extend(
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
@@ -364,7 +360,6 @@ class AmcrestCam(Camera):
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Remove camera from list and disconnect from signals."""
|
||||
assert self.hass is not None
|
||||
self.hass.data[DATA_AMCREST][CAMERAS].remove(self.entity_id)
|
||||
for unsub_dispatcher in self._unsub_dispatcher:
|
||||
unsub_dispatcher()
|
||||
@@ -379,15 +374,16 @@ class AmcrestCam(Camera):
|
||||
try:
|
||||
if self._brand is None:
|
||||
resp = self._api.vendor_information.strip()
|
||||
_LOGGER.debug("Assigned brand=%s", resp)
|
||||
if resp.startswith("vendor="):
|
||||
self._brand = resp.split("=")[-1]
|
||||
else:
|
||||
self._brand = "unknown"
|
||||
if self._model is None:
|
||||
resp = self._api.device_type.strip()
|
||||
_LOGGER.debug("Device_type=%s", resp)
|
||||
if resp.startswith("type="):
|
||||
self._model = resp.split("=")[-1]
|
||||
_LOGGER.debug("Assigned model=%s", resp)
|
||||
if resp:
|
||||
self._model = resp
|
||||
else:
|
||||
self._model = "unknown"
|
||||
if self._attr_unique_id is None:
|
||||
@@ -428,57 +424,46 @@ class AmcrestCam(Camera):
|
||||
|
||||
async def async_enable_recording(self) -> None:
|
||||
"""Call the job and enable recording."""
|
||||
assert self.hass is not None
|
||||
await self.hass.async_add_executor_job(self._enable_recording, True)
|
||||
|
||||
async def async_disable_recording(self) -> None:
|
||||
"""Call the job and disable recording."""
|
||||
assert self.hass is not None
|
||||
await self.hass.async_add_executor_job(self._enable_recording, False)
|
||||
|
||||
async def async_enable_audio(self) -> None:
|
||||
"""Call the job and enable audio."""
|
||||
assert self.hass is not None
|
||||
await self.hass.async_add_executor_job(self._enable_audio, True)
|
||||
|
||||
async def async_disable_audio(self) -> None:
|
||||
"""Call the job and disable audio."""
|
||||
assert self.hass is not None
|
||||
await self.hass.async_add_executor_job(self._enable_audio, False)
|
||||
|
||||
async def async_enable_motion_recording(self) -> None:
|
||||
"""Call the job and enable motion recording."""
|
||||
assert self.hass is not None
|
||||
await self.hass.async_add_executor_job(self._enable_motion_recording, True)
|
||||
|
||||
async def async_disable_motion_recording(self) -> None:
|
||||
"""Call the job and disable motion recording."""
|
||||
assert self.hass is not None
|
||||
await self.hass.async_add_executor_job(self._enable_motion_recording, False)
|
||||
|
||||
async def async_goto_preset(self, preset: int) -> None:
|
||||
"""Call the job and move camera to preset position."""
|
||||
assert self.hass is not None
|
||||
await self.hass.async_add_executor_job(self._goto_preset, preset)
|
||||
|
||||
async def async_set_color_bw(self, color_bw: str) -> None:
|
||||
"""Call the job and set camera color mode."""
|
||||
assert self.hass is not None
|
||||
await self.hass.async_add_executor_job(self._set_color_bw, color_bw)
|
||||
|
||||
async def async_start_tour(self) -> None:
|
||||
"""Call the job and start camera tour."""
|
||||
assert self.hass is not None
|
||||
await self.hass.async_add_executor_job(self._start_tour, True)
|
||||
|
||||
async def async_stop_tour(self) -> None:
|
||||
"""Call the job and stop camera tour."""
|
||||
assert self.hass is not None
|
||||
await self.hass.async_add_executor_job(self._start_tour, False)
|
||||
|
||||
async def async_ptz_control(self, movement: str, travel_time: float) -> None:
|
||||
"""Move or zoom camera in specified direction."""
|
||||
assert self.hass is not None
|
||||
code = _ACTION[_MOV.index(movement)]
|
||||
|
||||
kwargs = {"code": code, "arg1": 0, "arg2": 0, "arg3": 0}
|
||||
|
||||
@@ -129,7 +129,6 @@ class AmcrestSensor(SensorEntity):
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to update signal."""
|
||||
assert self.hass is not None
|
||||
self._unsub_dispatcher = async_dispatcher_connect(
|
||||
self.hass,
|
||||
service_signal(SERVICE_UPDATE, self._signal_name),
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
"""Support for APCUPSd sensors."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from apcaccess.status import ALL_UNITS
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_RESOURCES,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
@@ -25,74 +31,360 @@ from . import DOMAIN
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SENSOR_PREFIX = "UPS "
|
||||
SENSOR_TYPES = {
|
||||
"alarmdel": ["Alarm Delay", None, "mdi:alarm", None],
|
||||
"ambtemp": ["Ambient Temperature", None, "mdi:thermometer", None],
|
||||
"apc": ["Status Data", None, "mdi:information-outline", None],
|
||||
"apcmodel": ["Model", None, "mdi:information-outline", None],
|
||||
"badbatts": ["Bad Batteries", None, "mdi:information-outline", None],
|
||||
"battdate": ["Battery Replaced", None, "mdi:calendar-clock", None],
|
||||
"battstat": ["Battery Status", None, "mdi:information-outline", None],
|
||||
"battv": ["Battery Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"bcharge": ["Battery", PERCENTAGE, "mdi:battery", None],
|
||||
"cable": ["Cable Type", None, "mdi:ethernet-cable", None],
|
||||
"cumonbatt": ["Total Time on Battery", None, "mdi:timer-outline", None],
|
||||
"date": ["Status Date", None, "mdi:calendar-clock", None],
|
||||
"dipsw": ["Dip Switch Settings", None, "mdi:information-outline", None],
|
||||
"dlowbatt": ["Low Battery Signal", None, "mdi:clock-alert", None],
|
||||
"driver": ["Driver", None, "mdi:information-outline", None],
|
||||
"dshutd": ["Shutdown Delay", None, "mdi:timer-outline", None],
|
||||
"dwake": ["Wake Delay", None, "mdi:timer-outline", None],
|
||||
"endapc": ["Date and Time", None, "mdi:calendar-clock", None],
|
||||
"extbatts": ["External Batteries", None, "mdi:information-outline", None],
|
||||
"firmware": ["Firmware Version", None, "mdi:information-outline", None],
|
||||
"hitrans": ["Transfer High", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"hostname": ["Hostname", None, "mdi:information-outline", None],
|
||||
"humidity": ["Ambient Humidity", PERCENTAGE, "mdi:water-percent", None],
|
||||
"itemp": ["Internal Temperature", TEMP_CELSIUS, None, DEVICE_CLASS_TEMPERATURE],
|
||||
"lastxfer": ["Last Transfer", None, "mdi:transfer", None],
|
||||
"linefail": ["Input Voltage Status", None, "mdi:information-outline", None],
|
||||
"linefreq": ["Line Frequency", FREQUENCY_HERTZ, "mdi:information-outline", None],
|
||||
"linev": ["Input Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"loadpct": ["Load", PERCENTAGE, "mdi:gauge", None],
|
||||
"loadapnt": ["Load Apparent Power", PERCENTAGE, "mdi:gauge", None],
|
||||
"lotrans": ["Transfer Low", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"mandate": ["Manufacture Date", None, "mdi:calendar", None],
|
||||
"masterupd": ["Master Update", None, "mdi:information-outline", None],
|
||||
"maxlinev": ["Input Voltage High", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"maxtime": ["Battery Timeout", None, "mdi:timer-off-outline", None],
|
||||
"mbattchg": ["Battery Shutdown", PERCENTAGE, "mdi:battery-alert", None],
|
||||
"minlinev": ["Input Voltage Low", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"mintimel": ["Shutdown Time", None, "mdi:timer-outline", None],
|
||||
"model": ["Model", None, "mdi:information-outline", None],
|
||||
"nombattv": ["Battery Nominal Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"nominv": ["Nominal Input Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"nomoutv": ["Nominal Output Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"nompower": ["Nominal Output Power", POWER_WATT, "mdi:flash", None],
|
||||
"nomapnt": ["Nominal Apparent Power", POWER_VOLT_AMPERE, "mdi:flash", None],
|
||||
"numxfers": ["Transfer Count", None, "mdi:counter", None],
|
||||
"outcurnt": ["Output Current", ELECTRIC_CURRENT_AMPERE, "mdi:flash", None],
|
||||
"outputv": ["Output Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"reg1": ["Register 1 Fault", None, "mdi:information-outline", None],
|
||||
"reg2": ["Register 2 Fault", None, "mdi:information-outline", None],
|
||||
"reg3": ["Register 3 Fault", None, "mdi:information-outline", None],
|
||||
"retpct": ["Restore Requirement", PERCENTAGE, "mdi:battery-alert", None],
|
||||
"selftest": ["Last Self Test", None, "mdi:calendar-clock", None],
|
||||
"sense": ["Sensitivity", None, "mdi:information-outline", None],
|
||||
"serialno": ["Serial Number", None, "mdi:information-outline", None],
|
||||
"starttime": ["Startup Time", None, "mdi:calendar-clock", None],
|
||||
"statflag": ["Status Flag", None, "mdi:information-outline", None],
|
||||
"status": ["Status", None, "mdi:information-outline", None],
|
||||
"stesti": ["Self Test Interval", None, "mdi:information-outline", None],
|
||||
"timeleft": ["Time Left", None, "mdi:clock-alert", None],
|
||||
"tonbatt": ["Time on Battery", None, "mdi:timer-outline", None],
|
||||
"upsmode": ["Mode", None, "mdi:information-outline", None],
|
||||
"upsname": ["Name", None, "mdi:information-outline", None],
|
||||
"version": ["Daemon Info", None, "mdi:information-outline", None],
|
||||
"xoffbat": ["Transfer from Battery", None, "mdi:transfer", None],
|
||||
"xoffbatt": ["Transfer from Battery", None, "mdi:transfer", None],
|
||||
"xonbatt": ["Transfer to Battery", None, "mdi:transfer", None],
|
||||
}
|
||||
SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key="alarmdel",
|
||||
name="Alarm Delay",
|
||||
icon="mdi:alarm",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="ambtemp",
|
||||
name="Ambient Temperature",
|
||||
icon="mdi:thermometer",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="apc",
|
||||
name="Status Data",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="apcmodel",
|
||||
name="Model",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="badbatts",
|
||||
name="Bad Batteries",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="battdate",
|
||||
name="Battery Replaced",
|
||||
icon="mdi:calendar-clock",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="battstat",
|
||||
name="Battery Status",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="battv",
|
||||
name="Battery Voltage",
|
||||
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="bcharge",
|
||||
name="Battery",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon="mdi:battery",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="cable",
|
||||
name="Cable Type",
|
||||
icon="mdi:ethernet-cable",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="cumonbatt",
|
||||
name="Total Time on Battery",
|
||||
icon="mdi:timer-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="date",
|
||||
name="Status Date",
|
||||
icon="mdi:calendar-clock",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="dipsw",
|
||||
name="Dip Switch Settings",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="dlowbatt",
|
||||
name="Low Battery Signal",
|
||||
icon="mdi:clock-alert",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="driver",
|
||||
name="Driver",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="dshutd",
|
||||
name="Shutdown Delay",
|
||||
icon="mdi:timer-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="dwake",
|
||||
name="Wake Delay",
|
||||
icon="mdi:timer-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="endapc",
|
||||
name="Date and Time",
|
||||
icon="mdi:calendar-clock",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="extbatts",
|
||||
name="External Batteries",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="firmware",
|
||||
name="Firmware Version",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="hitrans",
|
||||
name="Transfer High",
|
||||
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="hostname",
|
||||
name="Hostname",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="humidity",
|
||||
name="Ambient Humidity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon="mdi:water-percent",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="itemp",
|
||||
name="Internal Temperature",
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="lastxfer",
|
||||
name="Last Transfer",
|
||||
icon="mdi:transfer",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="linefail",
|
||||
name="Input Voltage Status",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="linefreq",
|
||||
name="Line Frequency",
|
||||
native_unit_of_measurement=FREQUENCY_HERTZ,
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="linev",
|
||||
name="Input Voltage",
|
||||
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="loadpct",
|
||||
name="Load",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon="mdi:gauge",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="loadapnt",
|
||||
name="Load Apparent Power",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon="mdi:gauge",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="lotrans",
|
||||
name="Transfer Low",
|
||||
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="mandate",
|
||||
name="Manufacture Date",
|
||||
icon="mdi:calendar",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="masterupd",
|
||||
name="Master Update",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="maxlinev",
|
||||
name="Input Voltage High",
|
||||
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="maxtime",
|
||||
name="Battery Timeout",
|
||||
icon="mdi:timer-off-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="mbattchg",
|
||||
name="Battery Shutdown",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon="mdi:battery-alert",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="minlinev",
|
||||
name="Input Voltage Low",
|
||||
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="mintimel",
|
||||
name="Shutdown Time",
|
||||
icon="mdi:timer-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="model",
|
||||
name="Model",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="nombattv",
|
||||
name="Battery Nominal Voltage",
|
||||
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="nominv",
|
||||
name="Nominal Input Voltage",
|
||||
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="nomoutv",
|
||||
name="Nominal Output Voltage",
|
||||
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="nompower",
|
||||
name="Nominal Output Power",
|
||||
native_unit_of_measurement=POWER_WATT,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="nomapnt",
|
||||
name="Nominal Apparent Power",
|
||||
native_unit_of_measurement=POWER_VOLT_AMPERE,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="numxfers",
|
||||
name="Transfer Count",
|
||||
icon="mdi:counter",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="outcurnt",
|
||||
name="Output Current",
|
||||
native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="outputv",
|
||||
name="Output Voltage",
|
||||
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="reg1",
|
||||
name="Register 1 Fault",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="reg2",
|
||||
name="Register 2 Fault",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="reg3",
|
||||
name="Register 3 Fault",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="retpct",
|
||||
name="Restore Requirement",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon="mdi:battery-alert",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="selftest",
|
||||
name="Last Self Test",
|
||||
icon="mdi:calendar-clock",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="sense",
|
||||
name="Sensitivity",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="serialno",
|
||||
name="Serial Number",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="starttime",
|
||||
name="Startup Time",
|
||||
icon="mdi:calendar-clock",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="statflag",
|
||||
name="Status Flag",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="status",
|
||||
name="Status",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="stesti",
|
||||
name="Self Test Interval",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="timeleft",
|
||||
name="Time Left",
|
||||
icon="mdi:clock-alert",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="tonbatt",
|
||||
name="Time on Battery",
|
||||
icon="mdi:timer-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="upsmode",
|
||||
name="Mode",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="upsname",
|
||||
name="Name",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="version",
|
||||
name="Daemon Info",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="xoffbat",
|
||||
name="Transfer from Battery",
|
||||
icon="mdi:transfer",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="xoffbatt",
|
||||
name="Transfer from Battery",
|
||||
icon="mdi:transfer",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="xonbatt",
|
||||
name="Transfer to Battery",
|
||||
icon="mdi:transfer",
|
||||
),
|
||||
)
|
||||
SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES]
|
||||
|
||||
SPECIFIC_UNITS = {"ITEMP": TEMP_CELSIUS}
|
||||
INFERRED_UNITS = {
|
||||
@@ -111,7 +403,7 @@ INFERRED_UNITS = {
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_RESOURCES, default=[]): vol.All(
|
||||
cv.ensure_list, [vol.In(SENSOR_TYPES)]
|
||||
cv.ensure_list, [vol.In(SENSOR_KEYS)]
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -120,25 +412,20 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the APCUPSd sensors."""
|
||||
apcups_data = hass.data[DOMAIN]
|
||||
entities = []
|
||||
resources = config[CONF_RESOURCES]
|
||||
|
||||
for resource in config[CONF_RESOURCES]:
|
||||
sensor_type = resource.lower()
|
||||
|
||||
if sensor_type not in SENSOR_TYPES:
|
||||
SENSOR_TYPES[sensor_type] = [
|
||||
sensor_type.title(),
|
||||
"",
|
||||
"mdi:information-outline",
|
||||
]
|
||||
|
||||
if sensor_type.upper() not in apcups_data.status:
|
||||
for resource in resources:
|
||||
if resource.upper() not in apcups_data.status:
|
||||
_LOGGER.warning(
|
||||
"Sensor type: %s does not appear in the APCUPSd status output",
|
||||
sensor_type,
|
||||
resource,
|
||||
)
|
||||
|
||||
entities.append(APCUPSdSensor(apcups_data, sensor_type))
|
||||
entities = [
|
||||
APCUPSdSensor(apcups_data, description)
|
||||
for description in SENSOR_TYPES
|
||||
if description.key in resources
|
||||
]
|
||||
|
||||
add_entities(entities, True)
|
||||
|
||||
@@ -159,22 +446,18 @@ def infer_unit(value):
|
||||
class APCUPSdSensor(SensorEntity):
|
||||
"""Representation of a sensor entity for APCUPSd status values."""
|
||||
|
||||
def __init__(self, data, sensor_type):
|
||||
def __init__(self, data, description: SensorEntityDescription):
|
||||
"""Initialize the sensor."""
|
||||
self.entity_description = description
|
||||
self._data = data
|
||||
self.type = sensor_type
|
||||
self._attr_name = SENSOR_PREFIX + SENSOR_TYPES[sensor_type][0]
|
||||
self._attr_icon = SENSOR_TYPES[self.type][2]
|
||||
self._attr_native_unit_of_measurement = SENSOR_TYPES[sensor_type][1]
|
||||
self._attr_device_class = SENSOR_TYPES[sensor_type][3]
|
||||
self._attr_name = f"{SENSOR_PREFIX}{description.name}"
|
||||
|
||||
def update(self):
|
||||
"""Get the latest status and use it to update our sensor state."""
|
||||
if self.type.upper() not in self._data.status:
|
||||
key = self.entity_description.key.upper()
|
||||
if key not in self._data.status:
|
||||
self._attr_native_value = None
|
||||
else:
|
||||
self._attr_native_value, inferred_unit = infer_unit(
|
||||
self._data.status[self.type.upper()]
|
||||
)
|
||||
if not self._attr_native_unit_of_measurement:
|
||||
self._attr_native_value, inferred_unit = infer_unit(self._data.status[key])
|
||||
if not self.native_unit_of_measurement:
|
||||
self._attr_native_unit_of_measurement = inferred_unit
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/apple_tv",
|
||||
"requirements": ["pyatv==0.8.2"],
|
||||
"zeroconf": ["_mediaremotetv._tcp.local.", "_touch-able._tcp.local."],
|
||||
"after_dependencies": ["discovery"],
|
||||
"codeowners": ["@postlund"],
|
||||
"iot_class": "local_push"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured_device": "Le p\u00e9riph\u00e9rique est d\u00e9j\u00e0 configur\u00e9",
|
||||
"already_configured_device": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"already_in_progress": "La configuration est d\u00e9j\u00e0 en cours",
|
||||
"backoff": "L'appareil n'accepte pas les demandes d'appariement pour le moment (vous avez peut-\u00eatre saisi un code PIN non valide trop de fois), r\u00e9essayez plus tard.",
|
||||
"device_did_not_pair": "Aucune tentative pour terminer l'appairage n'a \u00e9t\u00e9 effectu\u00e9e \u00e0 partir de l'appareil.",
|
||||
@@ -11,10 +11,10 @@
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"invalid_auth": "Autentification invalide",
|
||||
"no_devices_found": "Aucun appareil d\u00e9tect\u00e9 sur le r\u00e9seau",
|
||||
"invalid_auth": "Authentification invalide",
|
||||
"no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau",
|
||||
"no_usable_service": "Un dispositif a \u00e9t\u00e9 trouv\u00e9, mais aucun moyen d\u2019\u00e9tablir un lien avec lui. Si vous continuez \u00e0 voir ce message, essayez de sp\u00e9cifier son adresse IP ou de red\u00e9marrer votre Apple TV.",
|
||||
"unknown": "Erreur innatendue"
|
||||
"unknown": "Erreur inattendue"
|
||||
},
|
||||
"flow_title": "Apple TV: {name}",
|
||||
"step": {
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
"""Support for AquaLogic sensors."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_MONITORED_CONDITIONS,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
@@ -16,40 +23,88 @@ import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from . import DOMAIN, UPDATE_TOPIC
|
||||
|
||||
TEMP_UNITS = [TEMP_CELSIUS, TEMP_FAHRENHEIT]
|
||||
PERCENT_UNITS = [PERCENTAGE, PERCENTAGE]
|
||||
SALT_UNITS = ["g/L", "PPM"]
|
||||
WATT_UNITS = [POWER_WATT, POWER_WATT]
|
||||
NO_UNITS = [None, None]
|
||||
|
||||
# sensor_type [ description, unit, icon, device_class ]
|
||||
# sensor_type corresponds to property names in aqualogic.core.AquaLogic
|
||||
SENSOR_TYPES = {
|
||||
"air_temp": ["Air Temperature", TEMP_UNITS, None, DEVICE_CLASS_TEMPERATURE],
|
||||
"pool_temp": [
|
||||
"Pool Temperature",
|
||||
TEMP_UNITS,
|
||||
"mdi:oil-temperature",
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
],
|
||||
"spa_temp": [
|
||||
"Spa Temperature",
|
||||
TEMP_UNITS,
|
||||
"mdi:oil-temperature",
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
],
|
||||
"pool_chlorinator": ["Pool Chlorinator", PERCENT_UNITS, "mdi:gauge", None],
|
||||
"spa_chlorinator": ["Spa Chlorinator", PERCENT_UNITS, "mdi:gauge", None],
|
||||
"salt_level": ["Salt Level", SALT_UNITS, "mdi:gauge", None],
|
||||
"pump_speed": ["Pump Speed", PERCENT_UNITS, "mdi:speedometer", None],
|
||||
"pump_power": ["Pump Power", WATT_UNITS, "mdi:gauge", None],
|
||||
"status": ["Status", NO_UNITS, "mdi:alert", None],
|
||||
}
|
||||
@dataclass
|
||||
class AquaLogicSensorEntityDescription(SensorEntityDescription):
|
||||
"""Describes AquaLogic sensor entity."""
|
||||
|
||||
unit_metric: str | None = None
|
||||
unit_imperial: str | None = None
|
||||
|
||||
|
||||
# keys correspond to property names in aqualogic.core.AquaLogic
|
||||
SENSOR_TYPES: tuple[AquaLogicSensorEntityDescription, ...] = (
|
||||
AquaLogicSensorEntityDescription(
|
||||
key="air_temp",
|
||||
name="Air Temperature",
|
||||
unit_metric=TEMP_CELSIUS,
|
||||
unit_imperial=TEMP_FAHRENHEIT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
AquaLogicSensorEntityDescription(
|
||||
key="pool_temp",
|
||||
name="Pool Temperature",
|
||||
unit_metric=TEMP_CELSIUS,
|
||||
unit_imperial=TEMP_FAHRENHEIT,
|
||||
icon="mdi:oil-temperature",
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
AquaLogicSensorEntityDescription(
|
||||
key="spa_temp",
|
||||
name="Spa Temperature",
|
||||
unit_metric=TEMP_CELSIUS,
|
||||
unit_imperial=TEMP_FAHRENHEIT,
|
||||
icon="mdi:oil-temperature",
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
AquaLogicSensorEntityDescription(
|
||||
key="pool_chlorinator",
|
||||
name="Pool Chlorinator",
|
||||
unit_metric=PERCENTAGE,
|
||||
unit_imperial=PERCENTAGE,
|
||||
icon="mdi:gauge",
|
||||
),
|
||||
AquaLogicSensorEntityDescription(
|
||||
key="spa_chlorinator",
|
||||
name="Spa Chlorinator",
|
||||
unit_metric=PERCENTAGE,
|
||||
unit_imperial=PERCENTAGE,
|
||||
icon="mdi:gauge",
|
||||
),
|
||||
AquaLogicSensorEntityDescription(
|
||||
key="salt_level",
|
||||
name="Salt Level",
|
||||
unit_metric="g/L",
|
||||
unit_imperial="PPM",
|
||||
icon="mdi:gauge",
|
||||
),
|
||||
AquaLogicSensorEntityDescription(
|
||||
key="pump_speed",
|
||||
name="Pump Speed",
|
||||
unit_metric=PERCENTAGE,
|
||||
unit_imperial=PERCENTAGE,
|
||||
icon="mdi:speedometer",
|
||||
),
|
||||
AquaLogicSensorEntityDescription(
|
||||
key="pump_power",
|
||||
name="Pump Power",
|
||||
unit_metric=POWER_WATT,
|
||||
unit_imperial=POWER_WATT,
|
||||
icon="mdi:gauge",
|
||||
),
|
||||
AquaLogicSensorEntityDescription(
|
||||
key="status",
|
||||
name="Status",
|
||||
icon="mdi:alert",
|
||||
),
|
||||
)
|
||||
|
||||
SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES]
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_MONITORED_CONDITIONS, default=list(SENSOR_TYPES)): vol.All(
|
||||
cv.ensure_list, [vol.In(SENSOR_TYPES)]
|
||||
vol.Required(CONF_MONITORED_CONDITIONS, default=SENSOR_KEYS): vol.All(
|
||||
cv.ensure_list, [vol.In(SENSOR_KEYS)]
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -57,26 +112,29 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up the sensor platform."""
|
||||
sensors = []
|
||||
|
||||
processor = hass.data[DOMAIN]
|
||||
for sensor_type in config[CONF_MONITORED_CONDITIONS]:
|
||||
sensors.append(AquaLogicSensor(processor, sensor_type))
|
||||
monitored_conditions = config[CONF_MONITORED_CONDITIONS]
|
||||
|
||||
async_add_entities(sensors)
|
||||
entities = [
|
||||
AquaLogicSensor(processor, description)
|
||||
for description in SENSOR_TYPES
|
||||
if description.key in monitored_conditions
|
||||
]
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class AquaLogicSensor(SensorEntity):
|
||||
"""Sensor implementation for the AquaLogic component."""
|
||||
|
||||
entity_description: AquaLogicSensorEntityDescription
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(self, processor, sensor_type):
|
||||
def __init__(self, processor, description: AquaLogicSensorEntityDescription):
|
||||
"""Initialize sensor."""
|
||||
self.entity_description = description
|
||||
self._processor = processor
|
||||
self._type = sensor_type
|
||||
self._attr_name = f"AquaLogic {SENSOR_TYPES[sensor_type][0]}"
|
||||
self._attr_icon = SENSOR_TYPES[sensor_type][2]
|
||||
self._attr_name = f"AquaLogic {description.name}"
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
@@ -92,11 +150,15 @@ class AquaLogicSensor(SensorEntity):
|
||||
panel = self._processor.panel
|
||||
if panel is not None:
|
||||
if panel.is_metric:
|
||||
self._attr_native_unit_of_measurement = SENSOR_TYPES[self._type][1][0]
|
||||
self._attr_native_unit_of_measurement = (
|
||||
self.entity_description.unit_metric
|
||||
)
|
||||
else:
|
||||
self._attr_native_unit_of_measurement = SENSOR_TYPES[self._type][1][1]
|
||||
self._attr_native_unit_of_measurement = (
|
||||
self.entity_description.unit_imperial
|
||||
)
|
||||
|
||||
self._attr_native_value = getattr(panel, self._type)
|
||||
self._attr_native_value = getattr(panel, self.entity_description.key)
|
||||
self.async_write_ha_state()
|
||||
else:
|
||||
self._attr_native_unit_of_measurement = None
|
||||
|
||||
@@ -5,7 +5,10 @@ from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.automation import AutomationActionType
|
||||
from homeassistant.components.automation import (
|
||||
AutomationActionType,
|
||||
AutomationTriggerInfo,
|
||||
)
|
||||
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
@@ -57,10 +60,10 @@ async def async_attach_trigger(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
action: AutomationActionType,
|
||||
automation_info: dict,
|
||||
automation_info: AutomationTriggerInfo,
|
||||
) -> CALLBACK_TYPE:
|
||||
"""Attach a trigger."""
|
||||
trigger_data = automation_info.get("trigger_data", {}) if automation_info else {}
|
||||
trigger_data = automation_info["trigger_data"]
|
||||
job = HassJob(action)
|
||||
|
||||
if config[CONF_TYPE] == "turn_on":
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'appareil \u00e9tait d\u00e9j\u00e0 configur\u00e9.",
|
||||
"already_in_progress": "Le flux de configuration de l'appareil est d\u00e9j\u00e0 en cours.",
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"already_in_progress": "La configuration est d\u00e9j\u00e0 en cours",
|
||||
"cannot_connect": "\u00c9chec de connexion"
|
||||
},
|
||||
"error": {
|
||||
|
||||
@@ -88,6 +88,7 @@ class ArestSwitchBase(SwitchEntity):
|
||||
self._resource = resource
|
||||
self._attr_name = f"{location.title()} {name.title()}"
|
||||
self._attr_available = True
|
||||
self._attr_is_on = False
|
||||
|
||||
|
||||
class ArestSwitchFunction(ArestSwitchBase):
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"invalid_host": "\u05e9\u05dd \u05de\u05d0\u05e8\u05d7 \u05d0\u05d5 \u05db\u05ea\u05d5\u05d1\u05ea IP \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05d9\u05dd",
|
||||
"pwd_and_ssh": "\u05e1\u05e4\u05e7 \u05e8\u05e7 \u05e1\u05d9\u05e1\u05de\u05d4 \u05d0\u05d5 \u05e7\u05d5\u05d1\u05e5 \u05de\u05e4\u05ea\u05d7 SSH",
|
||||
"pwd_or_ssh": "\u05d0\u05e0\u05d0 \u05e1\u05e4\u05e7 \u05e1\u05d9\u05e1\u05de\u05d4 \u05d0\u05d5 \u05e7\u05d5\u05d1\u05e5 \u05de\u05e4\u05ea\u05d7 SSH",
|
||||
"ssh_not_file": "\u05e7\u05d5\u05d1\u05e5 \u05de\u05e4\u05ea\u05d7 SSH \u05dc\u05d0 \u05e0\u05de\u05e6\u05d0",
|
||||
"unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4"
|
||||
},
|
||||
"step": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Un seul appareil Atag peut \u00eatre ajout\u00e9 \u00e0 Home Assistant"
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
@@ -10,7 +10,7 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Nom d'h\u00f4te ou adresse IP",
|
||||
"host": "H\u00f4te",
|
||||
"port": "Port"
|
||||
},
|
||||
"title": "Se connecter \u00e0 l'appareil"
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"reauth_successful": "La r\u00e9-authentification a r\u00e9ussi"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Impossible de se connecter, veuillez r\u00e9essayer",
|
||||
"invalid_auth": "Authentification non valide",
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_auth": "Authentification invalide",
|
||||
"unknown": "Erreur inattendue"
|
||||
},
|
||||
"step": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec \u00e0 la connexion"
|
||||
"cannot_connect": "\u00c9chec de connexion"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any, Awaitable, Callable, Dict, cast
|
||||
from typing import Any, Awaitable, Callable, Dict, TypedDict, cast
|
||||
|
||||
import voluptuous as vol
|
||||
from voluptuous.humanize import humanize_error
|
||||
@@ -106,6 +106,23 @@ _LOGGER = logging.getLogger(__name__)
|
||||
AutomationActionType = Callable[[HomeAssistant, TemplateVarsType], Awaitable[None]]
|
||||
|
||||
|
||||
class AutomationTriggerData(TypedDict):
|
||||
"""Automation trigger data."""
|
||||
|
||||
id: str
|
||||
idx: str
|
||||
|
||||
|
||||
class AutomationTriggerInfo(TypedDict):
|
||||
"""Information about automation trigger."""
|
||||
|
||||
domain: str
|
||||
name: str
|
||||
home_assistant_start: bool
|
||||
variables: TemplateVarsType
|
||||
trigger_data: AutomationTriggerData
|
||||
|
||||
|
||||
@bind_hass
|
||||
def is_on(hass, entity_id):
|
||||
"""
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Constants for the Awair component."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
@@ -6,9 +7,8 @@ import logging
|
||||
|
||||
from python_awair.devices import AwairDevice
|
||||
|
||||
from homeassistant.components.sensor import SensorEntityDescription
|
||||
from homeassistant.const import (
|
||||
ATTR_DEVICE_CLASS,
|
||||
ATTR_ICON,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_BILLION,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
@@ -36,10 +36,6 @@ API_VOC = "volatile_organic_compounds"
|
||||
|
||||
ATTRIBUTION = "Awair air quality sensor"
|
||||
|
||||
ATTR_LABEL = "label"
|
||||
ATTR_UNIT = "unit"
|
||||
ATTR_UNIQUE_ID = "unique_id"
|
||||
|
||||
DOMAIN = "awair"
|
||||
|
||||
DUST_ALIASES = [API_PM25, API_PM10]
|
||||
@@ -48,71 +44,89 @@ LOGGER = logging.getLogger(__package__)
|
||||
|
||||
UPDATE_INTERVAL = timedelta(minutes=5)
|
||||
|
||||
SENSOR_TYPES = {
|
||||
API_SCORE: {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:blur",
|
||||
ATTR_UNIT: PERCENTAGE,
|
||||
ATTR_LABEL: "Awair score",
|
||||
ATTR_UNIQUE_ID: "score", # matches legacy format
|
||||
},
|
||||
API_HUMID: {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY,
|
||||
ATTR_ICON: None,
|
||||
ATTR_UNIT: PERCENTAGE,
|
||||
ATTR_LABEL: "Humidity",
|
||||
ATTR_UNIQUE_ID: "HUMID", # matches legacy format
|
||||
},
|
||||
API_LUX: {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_ILLUMINANCE,
|
||||
ATTR_ICON: None,
|
||||
ATTR_UNIT: LIGHT_LUX,
|
||||
ATTR_LABEL: "Illuminance",
|
||||
ATTR_UNIQUE_ID: "illuminance",
|
||||
},
|
||||
API_SPL_A: {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:ear-hearing",
|
||||
ATTR_UNIT: SOUND_PRESSURE_WEIGHTED_DBA,
|
||||
ATTR_LABEL: "Sound level",
|
||||
ATTR_UNIQUE_ID: "sound_level",
|
||||
},
|
||||
API_VOC: {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:cloud",
|
||||
ATTR_UNIT: CONCENTRATION_PARTS_PER_BILLION,
|
||||
ATTR_LABEL: "Volatile organic compounds",
|
||||
ATTR_UNIQUE_ID: "VOC", # matches legacy format
|
||||
},
|
||||
API_TEMP: {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
ATTR_ICON: None,
|
||||
ATTR_UNIT: TEMP_CELSIUS,
|
||||
ATTR_LABEL: "Temperature",
|
||||
ATTR_UNIQUE_ID: "TEMP", # matches legacy format
|
||||
},
|
||||
API_PM25: {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:blur",
|
||||
ATTR_UNIT: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
ATTR_LABEL: "PM2.5",
|
||||
ATTR_UNIQUE_ID: "PM25", # matches legacy format
|
||||
},
|
||||
API_PM10: {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:blur",
|
||||
ATTR_UNIT: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
ATTR_LABEL: "PM10",
|
||||
ATTR_UNIQUE_ID: "PM10", # matches legacy format
|
||||
},
|
||||
API_CO2: {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_CO2,
|
||||
ATTR_ICON: "mdi:cloud",
|
||||
ATTR_UNIT: CONCENTRATION_PARTS_PER_MILLION,
|
||||
ATTR_LABEL: "Carbon dioxide",
|
||||
ATTR_UNIQUE_ID: "CO2", # matches legacy format
|
||||
},
|
||||
}
|
||||
|
||||
@dataclass
|
||||
class AwairRequiredKeysMixin:
|
||||
"""Mixinf for required keys."""
|
||||
|
||||
unique_id_tag: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class AwairSensorEntityDescription(SensorEntityDescription, AwairRequiredKeysMixin):
|
||||
"""Describes Awair sensor entity."""
|
||||
|
||||
|
||||
SENSOR_TYPE_SCORE = AwairSensorEntityDescription(
|
||||
key=API_SCORE,
|
||||
icon="mdi:blur",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
name="Awair score",
|
||||
unique_id_tag="score", # matches legacy format
|
||||
)
|
||||
|
||||
SENSOR_TYPES: tuple[AwairSensorEntityDescription, ...] = (
|
||||
AwairSensorEntityDescription(
|
||||
key=API_HUMID,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
name="Humidity",
|
||||
unique_id_tag="HUMID", # matches legacy format
|
||||
),
|
||||
AwairSensorEntityDescription(
|
||||
key=API_LUX,
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
native_unit_of_measurement=LIGHT_LUX,
|
||||
name="Illuminance",
|
||||
unique_id_tag="illuminance",
|
||||
),
|
||||
AwairSensorEntityDescription(
|
||||
key=API_SPL_A,
|
||||
icon="mdi:ear-hearing",
|
||||
native_unit_of_measurement=SOUND_PRESSURE_WEIGHTED_DBA,
|
||||
name="Sound level",
|
||||
unique_id_tag="sound_level",
|
||||
),
|
||||
AwairSensorEntityDescription(
|
||||
key=API_VOC,
|
||||
icon="mdi:cloud",
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
|
||||
name="Volatile organic compounds",
|
||||
unique_id_tag="VOC", # matches legacy format
|
||||
),
|
||||
AwairSensorEntityDescription(
|
||||
key=API_TEMP,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
name="Temperature",
|
||||
unique_id_tag="TEMP", # matches legacy format
|
||||
),
|
||||
AwairSensorEntityDescription(
|
||||
key=API_CO2,
|
||||
device_class=DEVICE_CLASS_CO2,
|
||||
icon="mdi:cloud",
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
|
||||
name="Carbon dioxide",
|
||||
unique_id_tag="CO2", # matches legacy format
|
||||
),
|
||||
)
|
||||
|
||||
SENSOR_TYPES_DUST: tuple[AwairSensorEntityDescription, ...] = (
|
||||
AwairSensorEntityDescription(
|
||||
key=API_PM25,
|
||||
icon="mdi:blur",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
name="PM2.5",
|
||||
unique_id_tag="PM25", # matches legacy format
|
||||
),
|
||||
AwairSensorEntityDescription(
|
||||
key=API_PM10,
|
||||
icon="mdi:blur",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
name="PM10",
|
||||
unique_id_tag="PM10", # matches legacy format
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -7,7 +7,7 @@ import voluptuous as vol
|
||||
from homeassistant.components.awair import AwairDataUpdateCoordinator, AwairResult
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.config_entries import SOURCE_IMPORT
|
||||
from homeassistant.const import ATTR_ATTRIBUTION, ATTR_DEVICE_CLASS, CONF_ACCESS_TOKEN
|
||||
from homeassistant.const import ATTR_ATTRIBUTION, CONF_ACCESS_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@@ -22,15 +22,14 @@ from .const import (
|
||||
API_SCORE,
|
||||
API_TEMP,
|
||||
API_VOC,
|
||||
ATTR_ICON,
|
||||
ATTR_LABEL,
|
||||
ATTR_UNIQUE_ID,
|
||||
ATTR_UNIT,
|
||||
ATTRIBUTION,
|
||||
DOMAIN,
|
||||
DUST_ALIASES,
|
||||
LOGGER,
|
||||
SENSOR_TYPE_SCORE,
|
||||
SENSOR_TYPES,
|
||||
SENSOR_TYPES_DUST,
|
||||
AwairSensorEntityDescription,
|
||||
)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
@@ -60,16 +59,20 @@ async def async_setup_entry(
|
||||
):
|
||||
"""Set up Awair sensor entity based on a config entry."""
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
sensors = []
|
||||
entities = []
|
||||
|
||||
data: list[AwairResult] = coordinator.data.values()
|
||||
for result in data:
|
||||
if result.air_data:
|
||||
sensors.append(AwairSensor(API_SCORE, result.device, coordinator))
|
||||
entities.append(AwairSensor(result.device, coordinator, SENSOR_TYPE_SCORE))
|
||||
device_sensors = result.air_data.sensors.keys()
|
||||
for sensor in device_sensors:
|
||||
if sensor in SENSOR_TYPES:
|
||||
sensors.append(AwairSensor(sensor, result.device, coordinator))
|
||||
entities.extend(
|
||||
[
|
||||
AwairSensor(result.device, coordinator, description)
|
||||
for description in (*SENSOR_TYPES, *SENSOR_TYPES_DUST)
|
||||
if description.key in device_sensors
|
||||
]
|
||||
)
|
||||
|
||||
# The "DUST" sensor for Awair is a combo pm2.5/pm10 sensor only
|
||||
# present on first-gen devices in lieu of separate pm2.5/pm10 sensors.
|
||||
@@ -78,45 +81,53 @@ async def async_setup_entry(
|
||||
# that data - because we can't really tell what kind of particles the
|
||||
# "DUST" sensor actually detected. However, it's still useful data.
|
||||
if API_DUST in device_sensors:
|
||||
for alias_kind in DUST_ALIASES:
|
||||
sensors.append(AwairSensor(alias_kind, result.device, coordinator))
|
||||
entities.extend(
|
||||
[
|
||||
AwairSensor(result.device, coordinator, description)
|
||||
for description in SENSOR_TYPES_DUST
|
||||
]
|
||||
)
|
||||
|
||||
async_add_entities(sensors)
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class AwairSensor(CoordinatorEntity, SensorEntity):
|
||||
"""Defines an Awair sensor entity."""
|
||||
|
||||
entity_description: AwairSensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
kind: str,
|
||||
device: AwairDevice,
|
||||
coordinator: AwairDataUpdateCoordinator,
|
||||
description: AwairSensorEntityDescription,
|
||||
) -> None:
|
||||
"""Set up an individual AwairSensor."""
|
||||
super().__init__(coordinator)
|
||||
self._kind = kind
|
||||
self.entity_description = description
|
||||
self._device = device
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
def name(self) -> str | None:
|
||||
"""Return the name of the sensor."""
|
||||
name = SENSOR_TYPES[self._kind][ATTR_LABEL]
|
||||
if self._device.name:
|
||||
name = f"{self._device.name} {name}"
|
||||
return f"{self._device.name} {self.entity_description.name}"
|
||||
|
||||
return name
|
||||
return self.entity_description.name
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return the uuid as the unique_id."""
|
||||
unique_id_tag = SENSOR_TYPES[self._kind][ATTR_UNIQUE_ID]
|
||||
unique_id_tag = self.entity_description.unique_id_tag
|
||||
|
||||
# This integration used to create a sensor that was labelled as a "PM2.5"
|
||||
# sensor for first-gen Awair devices, but its unique_id reflected the truth:
|
||||
# under the hood, it was a "DUST" sensor. So we preserve that specific unique_id
|
||||
# for users with first-gen devices that are upgrading.
|
||||
if self._kind == API_PM25 and API_DUST in self._air_data.sensors:
|
||||
if (
|
||||
self.entity_description.key == API_PM25
|
||||
and API_DUST in self._air_data.sensors
|
||||
):
|
||||
unique_id_tag = "DUST"
|
||||
|
||||
return f"{self._device.uuid}_{unique_id_tag}"
|
||||
@@ -127,16 +138,17 @@ class AwairSensor(CoordinatorEntity, SensorEntity):
|
||||
# If the last update was successful...
|
||||
if self.coordinator.last_update_success and self._air_data:
|
||||
# and the results included our sensor type...
|
||||
if self._kind in self._air_data.sensors:
|
||||
sensor_type = self.entity_description.key
|
||||
if sensor_type in self._air_data.sensors:
|
||||
# then we are available.
|
||||
return True
|
||||
|
||||
# or, we're a dust alias
|
||||
if self._kind in DUST_ALIASES and API_DUST in self._air_data.sensors:
|
||||
if sensor_type in DUST_ALIASES and API_DUST in self._air_data.sensors:
|
||||
return True
|
||||
|
||||
# or we are API_SCORE
|
||||
if self._kind == API_SCORE:
|
||||
if sensor_type == API_SCORE:
|
||||
# then we are available.
|
||||
return True
|
||||
|
||||
@@ -147,38 +159,24 @@ class AwairSensor(CoordinatorEntity, SensorEntity):
|
||||
def native_value(self) -> float:
|
||||
"""Return the state, rounding off to reasonable values."""
|
||||
state: float
|
||||
sensor_type = self.entity_description.key
|
||||
|
||||
# Special-case for "SCORE", which we treat as the AQI
|
||||
if self._kind == API_SCORE:
|
||||
if sensor_type == API_SCORE:
|
||||
state = self._air_data.score
|
||||
elif self._kind in DUST_ALIASES and API_DUST in self._air_data.sensors:
|
||||
elif sensor_type in DUST_ALIASES and API_DUST in self._air_data.sensors:
|
||||
state = self._air_data.sensors.dust
|
||||
else:
|
||||
state = self._air_data.sensors[self._kind]
|
||||
state = self._air_data.sensors[sensor_type]
|
||||
|
||||
if self._kind == API_VOC or self._kind == API_SCORE:
|
||||
if sensor_type in {API_VOC, API_SCORE}:
|
||||
return round(state)
|
||||
|
||||
if self._kind == API_TEMP:
|
||||
if sensor_type == API_TEMP:
|
||||
return round(state, 1)
|
||||
|
||||
return round(state, 2)
|
||||
|
||||
@property
|
||||
def icon(self) -> str:
|
||||
"""Return the icon."""
|
||||
return SENSOR_TYPES[self._kind][ATTR_ICON]
|
||||
|
||||
@property
|
||||
def device_class(self) -> str:
|
||||
"""Return the device_class."""
|
||||
return SENSOR_TYPES[self._kind][ATTR_DEVICE_CLASS]
|
||||
|
||||
@property
|
||||
def native_unit_of_measurement(self) -> str:
|
||||
"""Return the unit the value is expressed in."""
|
||||
return SENSOR_TYPES[self._kind][ATTR_UNIT]
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict:
|
||||
"""Return the Awair Index alongside state attributes.
|
||||
@@ -201,10 +199,11 @@ class AwairSensor(CoordinatorEntity, SensorEntity):
|
||||
|
||||
https://docs.developer.getawair.com/?version=latest#awair-score-and-index
|
||||
"""
|
||||
sensor_type = self.entity_description.key
|
||||
attrs = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
if self._kind in self._air_data.indices:
|
||||
attrs["awair_index"] = abs(self._air_data.indices[self._kind])
|
||||
elif self._kind in DUST_ALIASES and API_DUST in self._air_data.indices:
|
||||
if sensor_type in self._air_data.indices:
|
||||
attrs["awair_index"] = abs(self._air_data.indices[sensor_type])
|
||||
elif sensor_type in DUST_ALIASES and API_DUST in self._air_data.indices:
|
||||
attrs["awair_index"] = abs(self._air_data.indices.dust)
|
||||
|
||||
return attrs
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
"abort": {
|
||||
"already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9",
|
||||
"no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau",
|
||||
"reauth_successful": "Jeton d'acc\u00e8s mis \u00e0 jour avec succ\u00e8s"
|
||||
"reauth_successful": "La r\u00e9-authentification a r\u00e9ussi"
|
||||
},
|
||||
"error": {
|
||||
"invalid_access_token": "Jeton d'acc\u00e8s non valide",
|
||||
"unknown": "Erreur d'API Awair inconnue."
|
||||
"unknown": "Erreur inattendue"
|
||||
},
|
||||
"step": {
|
||||
"reauth": {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"already_in_progress": "Le flux de configuration de l'appareil est d\u00e9j\u00e0 en cours.",
|
||||
"already_in_progress": "La configuration est d\u00e9j\u00e0 en cours",
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_auth": "Authentification invalide"
|
||||
},
|
||||
@@ -15,7 +15,7 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Nom d'h\u00f4te ou adresse IP",
|
||||
"host": "H\u00f4te",
|
||||
"password": "Mot de passe",
|
||||
"port": "Port",
|
||||
"username": "Nom d'utilisateur"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9",
|
||||
"reauth_successful": "Jeton d'acc\u00e8s mis \u00e0 jour avec succ\u00e8s"
|
||||
"reauth_successful": "La r\u00e9-authentification a r\u00e9ussi"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
{
|
||||
"device_automation": {
|
||||
"condition_type": {
|
||||
"is_no_update": "{entity_name} \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03c9\u03bc\u03ad\u03bd\u03bf",
|
||||
"is_update": "{entity_name} \u03ad\u03c7\u03b5\u03b9 \u03bc\u03b9\u03b1 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7"
|
||||
},
|
||||
"trigger_type": {
|
||||
"no_update": "{entity_name} \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03b8\u03b7\u03ba\u03b5",
|
||||
"update": "{entity_name} \u03ad\u03bb\u03b1\u03b2\u03b5 \u03bc\u03b9\u03b1 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7"
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
"_": {
|
||||
"off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc\u03c2",
|
||||
@@ -72,6 +82,10 @@
|
||||
"off": "\u0394\u03b5\u03bd \u0395\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03c4\u03b7\u03ba\u03b5",
|
||||
"on": "\u0395\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03c4\u03b7\u03ba\u03b5"
|
||||
},
|
||||
"update": {
|
||||
"off": "\u03a0\u03bb\u03ae\u03c1\u03c9\u03c2 \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03c9\u03bc\u03ad\u03bd\u03bf",
|
||||
"on": "\u0394\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7"
|
||||
},
|
||||
"vibration": {
|
||||
"off": "\u0394\u03b5\u03bd \u0395\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03c4\u03b7\u03ba\u03b5",
|
||||
"on": "\u0395\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03c4\u03b7\u03ba\u03b5"
|
||||
|
||||
@@ -178,6 +178,10 @@
|
||||
"off": "No detectado",
|
||||
"on": "Detectado"
|
||||
},
|
||||
"update": {
|
||||
"off": "Actualizado",
|
||||
"on": "Actualizaci\u00f3n disponible"
|
||||
},
|
||||
"vibration": {
|
||||
"off": "No detectado",
|
||||
"on": "Detectado"
|
||||
|
||||
@@ -115,12 +115,12 @@
|
||||
"on": "Connect\u00e9"
|
||||
},
|
||||
"door": {
|
||||
"off": "Ferm\u00e9e",
|
||||
"on": "Ouverte"
|
||||
"off": "Ferm\u00e9",
|
||||
"on": "Ouvert"
|
||||
},
|
||||
"garage_door": {
|
||||
"off": "Ferm\u00e9e",
|
||||
"on": "Ouverte"
|
||||
"off": "Ferm\u00e9",
|
||||
"on": "Ouvert"
|
||||
},
|
||||
"gas": {
|
||||
"off": "Non d\u00e9tect\u00e9",
|
||||
@@ -191,8 +191,8 @@
|
||||
"on": "D\u00e9tect\u00e9e"
|
||||
},
|
||||
"window": {
|
||||
"off": "Ferm\u00e9e",
|
||||
"on": "Ouverte"
|
||||
"off": "Ferm\u00e9",
|
||||
"on": "Ouvert"
|
||||
}
|
||||
},
|
||||
"title": "Capteur binaire"
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"is_no_problem": "sensor {entity_name} nie wykrywa problemu",
|
||||
"is_no_smoke": "sensor {entity_name} nie wykrywa dymu",
|
||||
"is_no_sound": "sensor {entity_name} nie wykrywa d\u017awi\u0119ku",
|
||||
"is_no_update": "{entity_name} jest aktualny(-a)",
|
||||
"is_no_update": "dla {entity_name} nie ma dost\u0119pnej aktualizacji",
|
||||
"is_no_vibration": "sensor {entity_name} nie wykrywa wibracji",
|
||||
"is_not_bat_low": "bateria {entity_name} nie jest roz\u0142adowana",
|
||||
"is_not_cold": "sensor {entity_name} nie wykrywa zimna",
|
||||
@@ -43,7 +43,7 @@
|
||||
"is_smoke": "sensor {entity_name} wykrywa dym",
|
||||
"is_sound": "sensor {entity_name} wykrywa d\u017awi\u0119k",
|
||||
"is_unsafe": "sensor {entity_name} wykrywa zagro\u017cenie",
|
||||
"is_update": "{entity_name} ma dost\u0119pn\u0105 aktualizacj\u0119",
|
||||
"is_update": "dla {entity_name} jest dost\u0119pna aktualizacja",
|
||||
"is_vibration": "sensor {entity_name} wykrywa wibracje"
|
||||
},
|
||||
"trigger_type": {
|
||||
@@ -63,7 +63,7 @@
|
||||
"no_problem": "sensor {entity_name} przestanie wykrywa\u0107 problem",
|
||||
"no_smoke": "sensor {entity_name} przestanie wykrywa\u0107 dym",
|
||||
"no_sound": "sensor {entity_name} przestanie wykrywa\u0107 d\u017awi\u0119k",
|
||||
"no_update": "{entity_name} zosta\u0142 zaktualizowany(-a)",
|
||||
"no_update": "wykonano aktualizacj\u0119 dla {entity_name}",
|
||||
"no_vibration": "sensor {entity_name} przestanie wykrywa\u0107 wibracje",
|
||||
"not_bat_low": "nast\u0105pi na\u0142adowanie baterii {entity_name}",
|
||||
"not_cold": "sensor {entity_name} przestanie wykrywa\u0107 zimno",
|
||||
@@ -183,8 +183,8 @@
|
||||
"on": "wykryto"
|
||||
},
|
||||
"update": {
|
||||
"off": "Aktualny(-a)",
|
||||
"on": "Dost\u0119pna aktualizacja"
|
||||
"off": "brak aktualizacji",
|
||||
"on": "dost\u0119pna aktualizacja"
|
||||
},
|
||||
"vibration": {
|
||||
"off": "brak",
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
"config": {
|
||||
"abort": {
|
||||
"address_already_configured": "Un p\u00e9riph\u00e9rique BleBox est d\u00e9j\u00e0 configur\u00e9 \u00e0 {address}.",
|
||||
"already_configured": "Ce p\u00e9riph\u00e9rique BleBox est d\u00e9j\u00e0 configur\u00e9."
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Impossible de connecter le p\u00e9riph\u00e9rique BleBox. (V\u00e9rifiez les journaux pour les erreurs.)",
|
||||
"unknown": "Erreur inconnue lors de la connexion au p\u00e9riph\u00e9rique BleBox. (V\u00e9rifiez les journaux pour les erreurs.)",
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"unknown": "Erreur inattendue",
|
||||
"unsupported_version": "L'appareil BleBox a un micrologiciel obsol\u00e8te. Veuillez d'abord le mettre \u00e0 jour."
|
||||
},
|
||||
"flow_title": "P\u00e9riph\u00e9rique Blebox: {name} ({host)}",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "P\u00e9riph\u00e9rique d\u00e9j\u00e0 configur\u00e9"
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
@@ -20,7 +20,7 @@
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "Mot de passe",
|
||||
"username": "Identifiant"
|
||||
"username": "Nom d'utilisateur"
|
||||
},
|
||||
"title": "Connectez-vous avec un compte Blink"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "bmw_connected_drive",
|
||||
"name": "BMW Connected Drive",
|
||||
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
|
||||
"requirements": ["bimmer_connected==0.7.19"],
|
||||
"requirements": ["bimmer_connected==0.7.20"],
|
||||
"codeowners": ["@gerard33", "@rikroe"],
|
||||
"config_flow": true,
|
||||
"iot_class": "cloud_polling"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec \u00e0 la connexion",
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_auth": "Authentification invalide"
|
||||
},
|
||||
"step": {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Echec de connexion",
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_auth": "Authentification invalide",
|
||||
"old_firmware": "Ancien micrologiciel non pris en charge sur l'appareil Bond - veuillez mettre \u00e0 niveau avant de continuer",
|
||||
"unknown": "Erreur inattendue"
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"access_token": "Token d'acc\u00e8s",
|
||||
"access_token": "Jeton d'acc\u00e8s",
|
||||
"host": "H\u00f4te"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"config": {
|
||||
"error": {
|
||||
"pairing_failed": "El emparejamiento ha fallado; compruebe que el Bosch Smart Home Controller est\u00e1 en modo de emparejamiento (el LED parpadea) y que su contrase\u00f1a es correcta.",
|
||||
"session_error": "Error de sesi\u00f3n: La API devuelve un resultado no correcto."
|
||||
"session_error": "Error de sesi\u00f3n: La API devuelve un resultado no correcto.",
|
||||
"unknown": "Error inesperado"
|
||||
},
|
||||
"flow_title": "Bosch SHC: {name}",
|
||||
"step": {
|
||||
@@ -18,6 +19,9 @@
|
||||
"description": "La integraci\u00f3n bosch_shc necesita volver a autentificar su cuenta"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Anfitri\u00f3n"
|
||||
},
|
||||
"description": "Configura tu Bosch Smart Home Controller para permitir la supervisi\u00f3n y el control con Home Assistant.",
|
||||
"title": "Par\u00e1metros de autenticaci\u00f3n SHC"
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"reauth_successful": "R\u00e9-authentification r\u00e9ussie"
|
||||
"reauth_successful": "La r\u00e9-authentification a r\u00e9ussi"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_auth": "Authentification incorrecte",
|
||||
"invalid_auth": "Authentification invalide",
|
||||
"pairing_failed": "L'appairage a \u00e9chou\u00e9\u00a0; veuillez v\u00e9rifier que le Bosch Smart Home Controller est en mode d'appairage (voyant clignotant) et que votre mot de passe est correct.",
|
||||
"session_error": "Erreur de session\u00a0: l'API renvoie un r\u00e9sultat non-OK.",
|
||||
"unknown": "Erreur inattendue"
|
||||
@@ -23,7 +23,7 @@
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"description": "L'int\u00e9gration bosch_shc doit r\u00e9-authentifier votre compte",
|
||||
"title": "R\u00e9authentification de l'int\u00e9gration"
|
||||
"title": "R\u00e9-authentifier l'int\u00e9gration"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Ce t\u00e9l\u00e9viseur est d\u00e9j\u00e0 configur\u00e9.",
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"no_ip_control": "Le contr\u00f4le IP est d\u00e9sactiv\u00e9 sur votre t\u00e9l\u00e9viseur ou le t\u00e9l\u00e9viseur n'est pas pris en charge."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion, h\u00f4te ou code PIN non valide.",
|
||||
"invalid_host": "Nom d'h\u00f4te ou adresse IP invalide.",
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_host": "Nom d'h\u00f4te ou adresse IP non valide",
|
||||
"unsupported_model": "Votre mod\u00e8le de t\u00e9l\u00e9viseur n'est pas pris en charge."
|
||||
},
|
||||
"step": {
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Nom d'h\u00f4te ou adresse IP"
|
||||
"host": "H\u00f4te"
|
||||
},
|
||||
"description": "Configurez l'int\u00e9gration du t\u00e9l\u00e9viseur Sony Bravia. Si vous rencontrez des probl\u00e8mes de configuration, rendez-vous sur: https://www.home-assistant.io/integrations/braviatv \n\n Assurez-vous que votre t\u00e9l\u00e9viseur est allum\u00e9.",
|
||||
"title": "Sony Bravia TV"
|
||||
|
||||
@@ -142,9 +142,6 @@ class BroadlinkSwitch(BroadlinkEntity, SwitchEntity, RestoreEntity, ABC):
|
||||
super().__init__(device)
|
||||
self._command_on = command_on
|
||||
self._command_off = command_off
|
||||
|
||||
self._attr_assumed_state = True
|
||||
self._attr_device_class = DEVICE_CLASS_SWITCH
|
||||
self._attr_name = f"{device.name} Switch"
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"already_in_progress": "Il y a d\u00e9j\u00e0 un processus de configuration en cours pour cet appareil",
|
||||
"already_in_progress": "La configuration est d\u00e9j\u00e0 en cours",
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_host": "Nom d'h\u00f4te ou adresse IP non valide",
|
||||
"not_supported": "Dispositif non pris en charge",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Cette imprimante est d\u00e9j\u00e0 configur\u00e9e.",
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"unsupported_model": "Ce mod\u00e8le d'imprimante n'est pas pris en charge."
|
||||
},
|
||||
"error": {
|
||||
@@ -13,7 +13,7 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Nom d'h\u00f4te ou adresse IP",
|
||||
"host": "H\u00f4te",
|
||||
"type": "Type d'imprimante"
|
||||
},
|
||||
"description": "Configurez l'int\u00e9gration de l'imprimante Brother. Si vous avez des probl\u00e8mes avec la configuration, allez \u00e0 : https://www.home-assistant.io/integrations/brother"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Nom d'h\u00f4te ou adresse IP",
|
||||
"host": "H\u00f4te",
|
||||
"passkey": "Cha\u00eene de cl\u00e9 d'acc\u00e8s",
|
||||
"password": "Mot de passe",
|
||||
"port": "Port",
|
||||
|
||||
@@ -699,7 +699,7 @@ class BrSensor(SensorEntity):
|
||||
@callback
|
||||
def data_updated(self, data):
|
||||
"""Update data."""
|
||||
if self._load_data(data) and self.hass:
|
||||
if self.hass and self._load_data(data):
|
||||
self.async_write_ha_state()
|
||||
|
||||
@callback
|
||||
|
||||
@@ -165,10 +165,7 @@ async def _async_get_image(
|
||||
width=width, height=height
|
||||
)
|
||||
else:
|
||||
_LOGGER.warning(
|
||||
"The camera entity %s does not support requesting width and height, please open an issue with the integration author",
|
||||
camera.entity_id,
|
||||
)
|
||||
camera.async_warn_old_async_camera_image_signature()
|
||||
image_bytes = await camera.async_camera_image()
|
||||
|
||||
if image_bytes:
|
||||
@@ -381,6 +378,7 @@ class Camera(Entity):
|
||||
self.stream_options: dict[str, str] = {}
|
||||
self.content_type: str = DEFAULT_CONTENT_TYPE
|
||||
self.access_tokens: collections.deque = collections.deque([], 2)
|
||||
self._warned_old_signature = False
|
||||
self.async_update_token()
|
||||
|
||||
@property
|
||||
@@ -455,11 +453,20 @@ class Camera(Entity):
|
||||
return await self.hass.async_add_executor_job(
|
||||
partial(self.camera_image, width=width, height=height)
|
||||
)
|
||||
self.async_warn_old_async_camera_image_signature()
|
||||
return await self.hass.async_add_executor_job(self.camera_image)
|
||||
|
||||
# Remove in 2022.1 after all custom components have had a chance to change their signature
|
||||
@callback
|
||||
def async_warn_old_async_camera_image_signature(self) -> None:
|
||||
"""Warn once when calling async_camera_image with the function old signature."""
|
||||
if self._warned_old_signature:
|
||||
return
|
||||
_LOGGER.warning(
|
||||
"The camera entity %s does not support requesting width and height, please open an issue with the integration author",
|
||||
self.entity_id,
|
||||
)
|
||||
return await self.hass.async_add_executor_job(self.camera_image)
|
||||
self._warned_old_signature = True
|
||||
|
||||
async def handle_async_still_stream(
|
||||
self, request: web.Request, interval: float
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"single_instance_allowed": "Une seule configuration de Google Cast est n\u00e9cessaire."
|
||||
"single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible."
|
||||
},
|
||||
"error": {
|
||||
"invalid_known_hosts": "Les h\u00f4tes connus doivent \u00eatre une liste d'h\u00f4tes s\u00e9par\u00e9s par des virgules."
|
||||
@@ -15,7 +15,7 @@
|
||||
"title": "Google Cast"
|
||||
},
|
||||
"confirm": {
|
||||
"description": "Voulez-vous configurer Google Cast?"
|
||||
"description": "Voulez-vous commencer la configuration ?"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT
|
||||
@@ -44,7 +45,7 @@ async def async_unload_entry(hass, entry):
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
class CertExpiryDataUpdateCoordinator(DataUpdateCoordinator[datetime]):
|
||||
class CertExpiryDataUpdateCoordinator(DataUpdateCoordinator[Optional[datetime]]):
|
||||
"""Class to manage fetching Cert Expiry data from single endpoint."""
|
||||
|
||||
def __init__(self, hass, host, port):
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
"""Config flow for the Cert Expiry platform."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
@@ -25,7 +27,7 @@ class CertexpiryConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the config flow."""
|
||||
self._errors = {}
|
||||
self._errors: dict[str, str] = {}
|
||||
|
||||
async def _test_connection(self, user_input=None):
|
||||
"""Test connection to the server and try to get the certificate."""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Cette combinaison h\u00f4te et port est d\u00e9j\u00e0 configur\u00e9e",
|
||||
"already_configured": "Le service est d\u00e9j\u00e0 configur\u00e9",
|
||||
"import_failed": "\u00c9chec de l'importation \u00e0 partir de la configuration"
|
||||
},
|
||||
"error": {
|
||||
|
||||
@@ -5,7 +5,10 @@ from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.automation import AutomationActionType
|
||||
from homeassistant.components.automation import (
|
||||
AutomationActionType,
|
||||
AutomationTriggerInfo,
|
||||
)
|
||||
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA
|
||||
from homeassistant.components.homeassistant.triggers import (
|
||||
numeric_state as numeric_state_trigger,
|
||||
@@ -112,7 +115,7 @@ async def async_attach_trigger(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
action: AutomationActionType,
|
||||
automation_info: dict,
|
||||
automation_info: AutomationTriggerInfo,
|
||||
) -> CALLBACK_TYPE:
|
||||
"""Attach a trigger."""
|
||||
trigger_type = config[CONF_TYPE]
|
||||
|
||||
@@ -228,7 +228,7 @@ async def async_setup(hass, config):
|
||||
|
||||
cloud.iot.register_on_connect(_on_connect)
|
||||
|
||||
await cloud.start()
|
||||
await cloud.initialize()
|
||||
await http_api.async_setup(hass)
|
||||
|
||||
account_link.async_setup(hass)
|
||||
|
||||
@@ -4,9 +4,10 @@ import logging
|
||||
from typing import Any
|
||||
|
||||
import aiohttp
|
||||
from awesomeversion import AwesomeVersion
|
||||
from hass_nabucasa import account_link
|
||||
|
||||
from homeassistant.const import MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION
|
||||
from homeassistant.const import __version__ as HA_VERSION
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_entry_oauth2_flow, event
|
||||
|
||||
@@ -16,6 +17,8 @@ DATA_SERVICES = "cloud_account_link_services"
|
||||
CACHE_TIMEOUT = 3600
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CURRENT_VERSION = AwesomeVersion(HA_VERSION)
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup(hass: HomeAssistant):
|
||||
@@ -30,43 +33,12 @@ async def async_provide_implementation(hass: HomeAssistant, domain: str):
|
||||
services = await _get_services(hass)
|
||||
|
||||
for service in services:
|
||||
if service["service"] == domain and _is_older(service["min_version"]):
|
||||
if service["service"] == domain and CURRENT_VERSION >= service["min_version"]:
|
||||
return CloudOAuth2Implementation(hass, domain)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@callback
|
||||
def _is_older(version: str) -> bool:
|
||||
"""Test if a version is older than the current HA version."""
|
||||
version_parts = version.split(".")
|
||||
|
||||
if len(version_parts) != 3:
|
||||
return False
|
||||
|
||||
try:
|
||||
version_parts = [int(val) for val in version_parts]
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
patch_number_str = ""
|
||||
|
||||
for char in PATCH_VERSION:
|
||||
if char.isnumeric():
|
||||
patch_number_str += char
|
||||
else:
|
||||
break
|
||||
|
||||
try:
|
||||
patch_number = int(patch_number_str)
|
||||
except ValueError:
|
||||
patch_number = 0
|
||||
|
||||
cur_version_parts = [MAJOR_VERSION, MINOR_VERSION, patch_number]
|
||||
|
||||
return version_parts <= cur_version_parts
|
||||
|
||||
|
||||
async def _get_services(hass):
|
||||
"""Get the available services."""
|
||||
services = hass.data.get(DATA_SERVICES)
|
||||
|
||||
@@ -108,8 +108,8 @@ class CloudClient(Interface):
|
||||
|
||||
return self._google_config
|
||||
|
||||
async def logged_in(self) -> None:
|
||||
"""When user logs in."""
|
||||
async def cloud_started(self) -> None:
|
||||
"""When cloud is started."""
|
||||
is_new_user = await self.prefs.async_set_username(self.cloud.username)
|
||||
|
||||
async def enable_alexa(_):
|
||||
@@ -150,7 +150,10 @@ class CloudClient(Interface):
|
||||
if tasks:
|
||||
await asyncio.gather(*(task(None) for task in tasks))
|
||||
|
||||
async def cleanups(self) -> None:
|
||||
async def cloud_stopped(self) -> None:
|
||||
"""When the cloud is stopped."""
|
||||
|
||||
async def logout_cleanups(self) -> None:
|
||||
"""Cleanup some stuff after logout."""
|
||||
await self.prefs.async_set_username(None)
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class CloudGoogleConfig(AbstractConfig):
|
||||
@property
|
||||
def should_report_state(self):
|
||||
"""Return if states should be proactively reported."""
|
||||
return self._cloud.is_logged_in and self._prefs.google_report_state
|
||||
return self.enabled and self._prefs.google_report_state
|
||||
|
||||
@property
|
||||
def local_sdk_webhook_id(self):
|
||||
|
||||
@@ -6,7 +6,7 @@ import logging
|
||||
import aiohttp
|
||||
import async_timeout
|
||||
import attr
|
||||
from hass_nabucasa import Cloud, auth, thingtalk
|
||||
from hass_nabucasa import Cloud, auth, cloud_api, thingtalk
|
||||
from hass_nabucasa.const import STATE_DISCONNECTED
|
||||
from hass_nabucasa.voice import MAP_VOICE
|
||||
import voluptuous as vol
|
||||
@@ -24,7 +24,6 @@ from homeassistant.const import (
|
||||
HTTP_BAD_GATEWAY,
|
||||
HTTP_BAD_REQUEST,
|
||||
HTTP_INTERNAL_SERVER_ERROR,
|
||||
HTTP_OK,
|
||||
HTTP_UNAUTHORIZED,
|
||||
)
|
||||
|
||||
@@ -47,30 +46,6 @@ from .const import (
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
WS_TYPE_STATUS = "cloud/status"
|
||||
SCHEMA_WS_STATUS = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||
{vol.Required("type"): WS_TYPE_STATUS}
|
||||
)
|
||||
|
||||
|
||||
WS_TYPE_SUBSCRIPTION = "cloud/subscription"
|
||||
SCHEMA_WS_SUBSCRIPTION = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||
{vol.Required("type"): WS_TYPE_SUBSCRIPTION}
|
||||
)
|
||||
|
||||
|
||||
WS_TYPE_HOOK_CREATE = "cloud/cloudhook/create"
|
||||
SCHEMA_WS_HOOK_CREATE = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||
{vol.Required("type"): WS_TYPE_HOOK_CREATE, vol.Required("webhook_id"): str}
|
||||
)
|
||||
|
||||
|
||||
WS_TYPE_HOOK_DELETE = "cloud/cloudhook/delete"
|
||||
SCHEMA_WS_HOOK_DELETE = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||
{vol.Required("type"): WS_TYPE_HOOK_DELETE, vol.Required("webhook_id"): str}
|
||||
)
|
||||
|
||||
|
||||
_CLOUD_ERRORS = {
|
||||
InvalidTrustedNetworks: (
|
||||
HTTP_INTERNAL_SERVER_ERROR,
|
||||
@@ -94,17 +69,11 @@ _CLOUD_ERRORS = {
|
||||
async def async_setup(hass):
|
||||
"""Initialize the HTTP API."""
|
||||
async_register_command = hass.components.websocket_api.async_register_command
|
||||
async_register_command(WS_TYPE_STATUS, websocket_cloud_status, SCHEMA_WS_STATUS)
|
||||
async_register_command(
|
||||
WS_TYPE_SUBSCRIPTION, websocket_subscription, SCHEMA_WS_SUBSCRIPTION
|
||||
)
|
||||
async_register_command(websocket_cloud_status)
|
||||
async_register_command(websocket_subscription)
|
||||
async_register_command(websocket_update_prefs)
|
||||
async_register_command(
|
||||
WS_TYPE_HOOK_CREATE, websocket_hook_create, SCHEMA_WS_HOOK_CREATE
|
||||
)
|
||||
async_register_command(
|
||||
WS_TYPE_HOOK_DELETE, websocket_hook_delete, SCHEMA_WS_HOOK_DELETE
|
||||
)
|
||||
async_register_command(websocket_hook_create)
|
||||
async_register_command(websocket_hook_delete)
|
||||
async_register_command(websocket_remote_connect)
|
||||
async_register_command(websocket_remote_disconnect)
|
||||
|
||||
@@ -311,6 +280,7 @@ class CloudForgotPasswordView(HomeAssistantView):
|
||||
|
||||
|
||||
@websocket_api.async_response
|
||||
@websocket_api.websocket_command({vol.Required("type"): "cloud/status"})
|
||||
async def websocket_cloud_status(hass, connection, msg):
|
||||
"""Handle request for account info.
|
||||
|
||||
@@ -344,36 +314,19 @@ def _require_cloud_login(handler):
|
||||
|
||||
@_require_cloud_login
|
||||
@websocket_api.async_response
|
||||
@websocket_api.websocket_command({vol.Required("type"): "cloud/subscription"})
|
||||
async def websocket_subscription(hass, connection, msg):
|
||||
"""Handle request for account info."""
|
||||
|
||||
cloud = hass.data[DOMAIN]
|
||||
|
||||
with async_timeout.timeout(REQUEST_TIMEOUT):
|
||||
response = await cloud.fetch_subscription_info()
|
||||
|
||||
if response.status != HTTP_OK:
|
||||
connection.send_message(
|
||||
websocket_api.error_message(
|
||||
msg["id"], "request_failed", "Failed to request subscription"
|
||||
)
|
||||
try:
|
||||
with async_timeout.timeout(REQUEST_TIMEOUT):
|
||||
data = await cloud_api.async_subscription_info(cloud)
|
||||
except aiohttp.ClientError:
|
||||
connection.send_error(
|
||||
msg["id"], "request_failed", "Failed to request subscription"
|
||||
)
|
||||
|
||||
data = await response.json()
|
||||
|
||||
# Check if a user is subscribed but local info is outdated
|
||||
# In that case, let's refresh and reconnect
|
||||
if data.get("provider") and not cloud.is_connected:
|
||||
_LOGGER.debug("Found disconnected account with valid subscriotion, connecting")
|
||||
await cloud.auth.async_renew_access_token()
|
||||
|
||||
# Cancel reconnect in progress
|
||||
if cloud.iot.state != STATE_DISCONNECTED:
|
||||
await cloud.iot.disconnect()
|
||||
|
||||
hass.async_create_task(cloud.iot.connect())
|
||||
|
||||
connection.send_message(websocket_api.result_message(msg["id"], data))
|
||||
else:
|
||||
connection.send_result(msg["id"], data)
|
||||
|
||||
|
||||
@_require_cloud_login
|
||||
@@ -429,6 +382,12 @@ async def websocket_update_prefs(hass, connection, msg):
|
||||
@_require_cloud_login
|
||||
@websocket_api.async_response
|
||||
@_ws_handle_cloud_errors
|
||||
@websocket_api.websocket_command(
|
||||
{
|
||||
vol.Required("type"): "cloud/cloudhook/create",
|
||||
vol.Required("webhook_id"): str,
|
||||
}
|
||||
)
|
||||
async def websocket_hook_create(hass, connection, msg):
|
||||
"""Handle request for account info."""
|
||||
cloud = hass.data[DOMAIN]
|
||||
@@ -439,6 +398,12 @@ async def websocket_hook_create(hass, connection, msg):
|
||||
@_require_cloud_login
|
||||
@websocket_api.async_response
|
||||
@_ws_handle_cloud_errors
|
||||
@websocket_api.websocket_command(
|
||||
{
|
||||
vol.Required("type"): "cloud/cloudhook/delete",
|
||||
vol.Required("webhook_id"): str,
|
||||
}
|
||||
)
|
||||
async def websocket_hook_delete(hass, connection, msg):
|
||||
"""Handle request for account info."""
|
||||
cloud = hass.data[DOMAIN]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "cloud",
|
||||
"name": "Home Assistant Cloud",
|
||||
"documentation": "https://www.home-assistant.io/integrations/cloud",
|
||||
"requirements": ["hass-nabucasa==0.46.0"],
|
||||
"requirements": ["hass-nabucasa==0.49.0"],
|
||||
"dependencies": ["http", "webhook"],
|
||||
"after_dependencies": ["google_assistant", "alexa"],
|
||||
"codeowners": ["@home-assistant/cloud"],
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
"relayer_connected": "Relayer Connected",
|
||||
"remote_connected": "Remote Connected",
|
||||
"remote_enabled": "Remote Enabled",
|
||||
"remote_server": "Remote Server",
|
||||
"alexa_enabled": "Alexa Enabled",
|
||||
"google_enabled": "Google Enabled",
|
||||
"logged_in": "Logged In",
|
||||
"subscription_expiration": "Subscription Expiration"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ async def system_health_info(hass):
|
||||
data["remote_connected"] = cloud.remote.is_connected
|
||||
data["alexa_enabled"] = client.prefs.alexa_enabled
|
||||
data["google_enabled"] = client.prefs.google_enabled
|
||||
data["remote_server"] = cloud.remote.snitun_server
|
||||
|
||||
data["can_reach_cert_server"] = system_health.async_check_can_reach_url(
|
||||
hass, cloud.acme_directory_server
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"relayer_connected": "Encaminador connectat",
|
||||
"remote_connected": "Connexi\u00f3 remota establerta",
|
||||
"remote_enabled": "Connexi\u00f3 remota activada",
|
||||
"remote_server": "Servidor remot",
|
||||
"subscription_expiration": "Caducitat de la subscripci\u00f3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"relayer_connected": "Relay Verbunden",
|
||||
"remote_connected": "Remote verbunden",
|
||||
"remote_enabled": "Remote aktiviert",
|
||||
"remote_server": "Remote-Server",
|
||||
"subscription_expiration": "Ablauf des Abonnements"
|
||||
}
|
||||
}
|
||||
|
||||
7
homeassistant/components/cloud/translations/el.json
Normal file
7
homeassistant/components/cloud/translations/el.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"system_health": {
|
||||
"info": {
|
||||
"remote_server": "\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2 \u0394\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae\u03c2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
"relayer_connected": "Relayer Connected",
|
||||
"remote_connected": "Remote Connected",
|
||||
"remote_enabled": "Remote Enabled",
|
||||
"remote_server": "Remote Server",
|
||||
"subscription_expiration": "Subscription Expiration"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"relayer_connected": "Relayer conectado",
|
||||
"remote_connected": "Remoto conectado",
|
||||
"remote_enabled": "Remoto habilitado",
|
||||
"remote_server": "Servidor remoto",
|
||||
"subscription_expiration": "Caducidad de la suscripci\u00f3n"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"relayer_connected": "Edastaja on \u00fchendatud",
|
||||
"remote_connected": "Kaug\u00fchendus on loodud",
|
||||
"remote_enabled": "Kaug\u00fchendus on lubatud",
|
||||
"remote_server": "Kaugserver",
|
||||
"subscription_expiration": "Tellimuse aegumine"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
"info": {
|
||||
"alexa_enabled": "Alexa \u05de\u05d5\u05e4\u05e2\u05dc\u05ea",
|
||||
"google_enabled": "Google \u05de\u05d5\u05e4\u05e2\u05dc",
|
||||
"logged_in": "\u05de\u05d7\u05d5\u05d1\u05e8"
|
||||
"logged_in": "\u05de\u05d7\u05d5\u05d1\u05e8",
|
||||
"remote_server": "\u05e9\u05e8\u05ea \u05de\u05e8\u05d5\u05d7\u05e7"
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user