forked from home-assistant/core
Compare commits
95 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
245eec7041 | ||
|
|
493309daa7 | ||
|
|
af68802c17 | ||
|
|
576cece7a9 | ||
|
|
3b9859940f | ||
|
|
a315fd059a | ||
|
|
ba9ef004c8 | ||
|
|
22f745b17c | ||
|
|
05cf223146 | ||
|
|
d4aadd8af0 | ||
|
|
4045eee2e5 | ||
|
|
83a51f7f30 | ||
|
|
29110fe157 | ||
|
|
e87b7e24b4 | ||
|
|
d9056c01a6 | ||
|
|
a724bc21b6 | ||
|
|
ef00178339 | ||
|
|
b8770c3958 | ||
|
|
f0c0cfcac0 | ||
|
|
4c48ad9108 | ||
|
|
92b0453749 | ||
|
|
8ab801a7b4 | ||
|
|
f92c7b1aea | ||
|
|
0d9fbf864f | ||
|
|
275f9c8a28 | ||
|
|
84f3b1514f | ||
|
|
802f5613c4 | ||
|
|
8be40cbb00 | ||
|
|
46ce4e92f6 | ||
|
|
39f11bb46d | ||
|
|
3b0fe9adde | ||
|
|
707778229b | ||
|
|
a474534c08 | ||
|
|
65ad99d51c | ||
|
|
4052a0db89 | ||
|
|
b546fc5067 | ||
|
|
5dcc760755 | ||
|
|
fb06acf39d | ||
|
|
948f191f16 | ||
|
|
2c0d9105ac | ||
|
|
10df9f3542 | ||
|
|
6cf799459b | ||
|
|
47e2d1caa5 | ||
|
|
69d8f94e3b | ||
|
|
4b7803ed03 | ||
|
|
ff6015ff89 | ||
|
|
fbd144de46 | ||
|
|
adaebdeea8 | ||
|
|
910cb5865a | ||
|
|
baf0d9b2d9 | ||
|
|
c1bce68549 | ||
|
|
bde4c0e46f | ||
|
|
a275e7aa67 | ||
|
|
d96e416d26 | ||
|
|
efc3894303 | ||
|
|
06b47ee2f5 | ||
|
|
08ca43221f | ||
|
|
8641740ed8 | ||
|
|
d0ada6c6e2 | ||
|
|
76bb036968 | ||
|
|
d8b64be41c | ||
|
|
b3e0b7b86e | ||
|
|
e097e4c1c2 | ||
|
|
34f0fecef8 | ||
|
|
f53a10d39a | ||
|
|
5b993129d6 | ||
|
|
865656d436 | ||
|
|
fb25c6c115 | ||
|
|
c963cf8743 | ||
|
|
ddb28db21a | ||
|
|
bfc98b444f | ||
|
|
f9a0f44137 | ||
|
|
93750d71ce | ||
|
|
06e4003640 | ||
|
|
97ff5e2085 | ||
|
|
8a2c07ce19 | ||
|
|
9f7398e0df | ||
|
|
7df84dadad | ||
|
|
2a1e943b18 | ||
|
|
e6e72bfa82 | ||
|
|
219868b308 | ||
|
|
67dd861d8c | ||
|
|
f2765ba320 | ||
|
|
aefd3df914 | ||
|
|
3658eeb8d1 | ||
|
|
080cb6b6e9 | ||
|
|
20796303da | ||
|
|
dff6151ff4 | ||
|
|
6f24f4e302 | ||
|
|
175febe635 | ||
|
|
aa907f4d10 | ||
|
|
3d09478aea | ||
|
|
05df9b4b8b | ||
|
|
1865a28083 | ||
|
|
f78d57515a |
@@ -1032,8 +1032,6 @@ 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,6 +65,7 @@ jobs:
|
||||
matrix:
|
||||
arch: ${{ fromJson(needs.init.outputs.architectures) }}
|
||||
tag:
|
||||
- "3.9-alpine3.13"
|
||||
- "3.9-alpine3.14"
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
@@ -105,6 +106,7 @@ 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 @Aohzan
|
||||
homeassistant/components/harmony/* @ehendrix23 @bramkragten @bdraco @mkeesey
|
||||
homeassistant/components/hassio/* @home-assistant/supervisor
|
||||
homeassistant/components/heatmiser/* @andylockran
|
||||
homeassistant/components/heos/* @andrewsayre
|
||||
@@ -553,7 +553,6 @@ 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,21 +16,6 @@ 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,8 +6,6 @@ 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,6 +118,14 @@ 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,11 +342,7 @@ def async_enable_logging(
|
||||
err_log_path, backupCount=1
|
||||
)
|
||||
|
||||
try:
|
||||
err_handler.doRollover()
|
||||
except OSError as err:
|
||||
_LOGGER.error("Error rolling over log file: %s", err)
|
||||
|
||||
err_handler.doRollover()
|
||||
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\u00e9j\u00e0 configur\u00e9. Une seule configuration possible."
|
||||
"single_instance_allowed": "D\u00e9ja 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"
|
||||
"name": "Nom de l'int\u00e9gration"
|
||||
},
|
||||
"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,9 +3,7 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"account_id": "ID de la cuenta",
|
||||
"host": "Anfitri\u00f3n",
|
||||
"password": "Contrase\u00f1a"
|
||||
"account_id": "ID de la cuenta"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
"host": "H\u00f4te",
|
||||
"password": "Mot de passe",
|
||||
"port": "Port",
|
||||
"ssl": "Utilise un certificat SSL",
|
||||
"ssl": "AdGuard Home utilise un certificat SSL",
|
||||
"username": "Nom d'utilisateur",
|
||||
"verify_ssl": "V\u00e9rifier le certificat SSL"
|
||||
"verify_ssl": "AdGuard Home utilise un certificat appropri\u00e9"
|
||||
},
|
||||
"description": "Configurez votre instance AdGuard Home pour permettre la surveillance et le contr\u00f4le."
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"""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,
|
||||
@@ -42,6 +40,9 @@ 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"
|
||||
@@ -199,145 +200,118 @@ FORECAST_MODE_ATTR_API = {
|
||||
FORECAST_MODE_HOURLY: ATTR_API_FORECAST_HOURLY,
|
||||
}
|
||||
|
||||
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,
|
||||
),
|
||||
)
|
||||
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,
|
||||
},
|
||||
}
|
||||
|
||||
WIND_BEARING_MAP = {
|
||||
"C": None,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"""Support for the AEMET OpenData service."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.const import ATTR_ATTRIBUTION
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
@@ -16,6 +14,9 @@ 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
|
||||
@@ -27,30 +28,37 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
name = domain_data[ENTRY_NAME]
|
||||
weather_coordinator = domain_data[ENTRY_WEATHER_COORDINATOR]
|
||||
|
||||
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_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],
|
||||
weather_coordinator,
|
||||
mode,
|
||||
description,
|
||||
)
|
||||
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 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 description in FORECAST_SENSOR_TYPES
|
||||
if description.key in FORECAST_MONITORED_CONDITIONS
|
||||
]
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
@@ -64,14 +72,20 @@ class AbstractAemetSensor(CoordinatorEntity, SensorEntity):
|
||||
self,
|
||||
name,
|
||||
unique_id,
|
||||
sensor_type,
|
||||
sensor_configuration,
|
||||
coordinator: WeatherUpdateCoordinator,
|
||||
description: SensorEntityDescription,
|
||||
):
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(coordinator)
|
||||
self.entity_description = description
|
||||
self._attr_name = f"{name} {description.name}"
|
||||
self._attr_unique_id = unique_id
|
||||
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)
|
||||
|
||||
|
||||
class AemetSensor(AbstractAemetSensor):
|
||||
@@ -81,21 +95,20 @@ class AemetSensor(AbstractAemetSensor):
|
||||
self,
|
||||
name,
|
||||
unique_id,
|
||||
sensor_type,
|
||||
sensor_configuration,
|
||||
weather_coordinator: WeatherUpdateCoordinator,
|
||||
description: SensorEntityDescription,
|
||||
):
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(
|
||||
name=name,
|
||||
unique_id=f"{unique_id}-{description.key}",
|
||||
coordinator=weather_coordinator,
|
||||
description=description,
|
||||
name, unique_id, sensor_type, sensor_configuration, weather_coordinator
|
||||
)
|
||||
self._weather_coordinator = weather_coordinator
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
"""Return the state of the device."""
|
||||
return self.coordinator.data.get(self.entity_description.key)
|
||||
return self._weather_coordinator.data.get(self._sensor_type)
|
||||
|
||||
|
||||
class AemetForecastSensor(AbstractAemetSensor):
|
||||
@@ -105,17 +118,16 @@ class AemetForecastSensor(AbstractAemetSensor):
|
||||
self,
|
||||
name,
|
||||
unique_id,
|
||||
sensor_type,
|
||||
sensor_configuration,
|
||||
weather_coordinator: WeatherUpdateCoordinator,
|
||||
forecast_mode,
|
||||
description: SensorEntityDescription,
|
||||
):
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(
|
||||
name=name,
|
||||
unique_id=f"{unique_id}-{description.key}",
|
||||
coordinator=weather_coordinator,
|
||||
description=description,
|
||||
name, unique_id, sensor_type, sensor_configuration, weather_coordinator
|
||||
)
|
||||
self._weather_coordinator = weather_coordinator
|
||||
self._forecast_mode = forecast_mode
|
||||
self._attr_entity_registry_enabled_default = (
|
||||
self._forecast_mode == FORECAST_MODE_DAILY
|
||||
@@ -125,9 +137,9 @@ class AemetForecastSensor(AbstractAemetSensor):
|
||||
def native_value(self):
|
||||
"""Return the state of the device."""
|
||||
forecast = None
|
||||
forecasts = self.coordinator.data.get(
|
||||
forecasts = self._weather_coordinator.data.get(
|
||||
FORECAST_MODE_ATTR_API[self._forecast_mode]
|
||||
)
|
||||
if forecasts:
|
||||
forecast = forecasts[0].get(self.entity_description.key)
|
||||
forecast = forecasts[0].get(self._sensor_type)
|
||||
return forecast
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"already_in_progress": "La configuration est d\u00e9j\u00e0 en cours",
|
||||
"already_in_progress": "La configuration de l'appareil est d\u00e9j\u00e0 en cours.",
|
||||
"cannot_connect": "\u00c9chec de connexion"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "H\u00f4te",
|
||||
"host": "Nom d'h\u00f4te ou adresse IP",
|
||||
"port": "Port"
|
||||
},
|
||||
"title": "Configurer l'agent DVR"
|
||||
|
||||
@@ -3,6 +3,23 @@ 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"
|
||||
@@ -32,3 +49,56 @@ 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),
|
||||
),
|
||||
)
|
||||
|
||||
14
homeassistant/components/airly/model.py
Normal file
14
homeassistant/components/airly/model.py
Normal file
@@ -0,0 +1,14 @@
|
||||
"""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,30 +1,11 @@
|
||||
"""Support for the Airly sensor service."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Callable, cast
|
||||
from typing import Any, cast
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
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.const import ATTR_ATTRIBUTION, CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
@@ -37,12 +18,8 @@ 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,
|
||||
@@ -51,74 +28,15 @@ 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'emplacement est d\u00e9j\u00e0 configur\u00e9"
|
||||
"already_configured": "L'int\u00e9gration des coordonn\u00e9es d'Airly 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"
|
||||
"name": "Nom de l'int\u00e9gration"
|
||||
},
|
||||
"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,15 +1,14 @@
|
||||
"""Support for the AirNow sensor service."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
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,
|
||||
@@ -23,69 +22,69 @@ from .const import (
|
||||
|
||||
ATTRIBUTION = "Data provided by AirNow"
|
||||
|
||||
ATTR_LABEL = "label"
|
||||
ATTR_UNIT = "unit"
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
|
||||
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,
|
||||
),
|
||||
)
|
||||
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,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
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]
|
||||
|
||||
entities = [AirNowSensor(coordinator, description) for description in SENSOR_TYPES]
|
||||
sensors = []
|
||||
for sensor in SENSOR_TYPES:
|
||||
sensors.append(AirNowSensor(coordinator, sensor))
|
||||
|
||||
async_add_entities(entities, False)
|
||||
async_add_entities(sensors, False)
|
||||
|
||||
|
||||
class AirNowSensor(CoordinatorEntity, SensorEntity):
|
||||
"""Define an AirNow sensor."""
|
||||
|
||||
coordinator: AirNowDataUpdateCoordinator
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AirNowDataUpdateCoordinator,
|
||||
description: SensorEntityDescription,
|
||||
) -> None:
|
||||
def __init__(self, coordinator, kind):
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator)
|
||||
self.entity_description = description
|
||||
self.kind = kind
|
||||
self._state = None
|
||||
self._attrs = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
self._attr_name = f"AirNow {description.name}"
|
||||
self._attr_unique_id = (
|
||||
f"{coordinator.latitude}-{coordinator.longitude}-{description.key.lower()}"
|
||||
)
|
||||
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()}"
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
"""Return the state."""
|
||||
self._state = self.coordinator.data[self.entity_description.key]
|
||||
self._state = self.coordinator.data[self.kind]
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
if self.entity_description.key == ATTR_API_AQI:
|
||||
if self.kind == 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 de connexion",
|
||||
"cannot_connect": "\u00c9chec \u00e0 la 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 d'API",
|
||||
"api_key": "Cl\u00e9 API",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"radius": "Rayon d'action de la station (en miles, facultatif)"
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"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}."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"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": "Cl\u00e9 d'API",
|
||||
"api_key": "Clef d'API",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude"
|
||||
},
|
||||
@@ -22,7 +22,7 @@
|
||||
},
|
||||
"geography_by_name": {
|
||||
"data": {
|
||||
"api_key": "Cl\u00e9 d'API",
|
||||
"api_key": "Clef d'API",
|
||||
"city": "Ville",
|
||||
"country": "Pays",
|
||||
"state": "Etat"
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
"s2": "Di\u00f3xido de azufre"
|
||||
},
|
||||
"airvisual__pollutant_level": {
|
||||
"good": "Bueno",
|
||||
"hazardous": "Da\u00f1ino",
|
||||
"good": "Bien",
|
||||
"hazardous": "Peligroso",
|
||||
"moderate": "Moderado",
|
||||
"unhealthy": "Insalubre",
|
||||
"unhealthy_sensitive": "Insalubre para grupos sensibles",
|
||||
"unhealthy_sensitive": "Incorrecto 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,10 +11,7 @@ from homeassistant.components.alarm_control_panel.const import (
|
||||
SUPPORT_ALARM_ARM_NIGHT,
|
||||
SUPPORT_ALARM_ARM_VACATION,
|
||||
)
|
||||
from homeassistant.components.automation import (
|
||||
AutomationActionType,
|
||||
AutomationTriggerInfo,
|
||||
)
|
||||
from homeassistant.components.automation import AutomationActionType
|
||||
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA
|
||||
from homeassistant.components.homeassistant.triggers import state as state_trigger
|
||||
from homeassistant.const import (
|
||||
@@ -132,7 +129,7 @@ async def async_attach_trigger(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
action: AutomationActionType,
|
||||
automation_info: AutomationTriggerInfo,
|
||||
automation_info: dict,
|
||||
) -> 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 voor vakantie",
|
||||
"arm_vacation": "Schakel {entity_name} in op 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 ingeschakeld voor vakantie",
|
||||
"is_armed_vacation": "{entity_name} is in vakantie geschakeld",
|
||||
"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 in voor vakantie",
|
||||
"armed_vacation": "{entity_name} schakelde vakantie in",
|
||||
"disarmed": "{entity_name} uitgeschakeld",
|
||||
"triggered": "{entity_name} afgegaan"
|
||||
}
|
||||
@@ -40,5 +40,5 @@
|
||||
"triggered": "Gaat af"
|
||||
}
|
||||
},
|
||||
"title": "Alarmbedieningspaneel"
|
||||
"title": "Alarm bedieningspaneel"
|
||||
}
|
||||
@@ -1483,6 +1483,16 @@ 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,6 +535,10 @@ 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,8 +1091,24 @@ 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
|
||||
if instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
elif instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
range_value = int(range_value)
|
||||
if range_value == 0:
|
||||
service = cover.SERVICE_CLOSE_COVER
|
||||
@@ -1168,8 +1184,29 @@ 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
|
||||
if instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
elif 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": "\u00c9chec de connexion",
|
||||
"missing_configuration": "Le composant n'est pas configur\u00e9. Veuillez suivre la documentation.",
|
||||
"cannot_connect": "Impossible de se connecter au serveur Almond",
|
||||
"missing_configuration": "Veuillez consulter la documentation pour savoir comment configurer Almond.",
|
||||
"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": "La r\u00e9-authentification a r\u00e9ussi"
|
||||
"reauth_successful": "R\u00e9-authentification r\u00e9ussie"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_api_key": "Cl\u00e9 API invalide"
|
||||
"invalid_api_key": "Cl\u00e9 API non valide"
|
||||
},
|
||||
"step": {
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_key": "Cl\u00e9 d'API",
|
||||
"api_key": "cl\u00e9 API",
|
||||
"description": "R\u00e9-authentifiez-vous avec votre compte Ambee."
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "Cl\u00e9 d'API",
|
||||
"api_key": "cl\u00e9 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": "Authentification r\u00e9ussie"
|
||||
"default": "Authentifi\u00e9 avec succ\u00e8s avec Ambiclimate"
|
||||
},
|
||||
"error": {
|
||||
"follow_link": "Veuillez suivre le lien et vous authentifier avant d'appuyer sur Soumettre.",
|
||||
|
||||
@@ -1,17 +1,38 @@
|
||||
"""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
|
||||
@@ -20,43 +41,266 @@ from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
async_dispatcher_send,
|
||||
)
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
from homeassistant.helpers.entity import Entity
|
||||
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: {}})
|
||||
@@ -162,14 +406,13 @@ class AmbientStation:
|
||||
|
||||
def on_data(data: dict) -> None:
|
||||
"""Define a handler to fire when the data is received."""
|
||||
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}")
|
||||
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}"
|
||||
)
|
||||
|
||||
def on_disconnect() -> None:
|
||||
"""Define a handler to fire when the websocket is disconnected."""
|
||||
@@ -178,19 +421,26 @@ class AmbientStation:
|
||||
def on_subscribed(data: dict) -> None:
|
||||
"""Define a handler to fire when the subscription is set."""
|
||||
for station in data["devices"]:
|
||||
mac = station["macAddress"]
|
||||
|
||||
if mac in self.stations:
|
||||
if station["macAddress"] in self.stations:
|
||||
continue
|
||||
|
||||
LOGGER.debug("New station subscription: %s", data)
|
||||
|
||||
self.stations[mac] = {
|
||||
ATTR_LAST_DATA: async_hydrate_station_data(station["lastData"]),
|
||||
# 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"],
|
||||
ATTR_LOCATION: station.get("info", {}).get("location"),
|
||||
ATTR_NAME: station.get("info", {}).get("name", mac),
|
||||
ATTR_MONITORED_CONDITIONS: monitored_conditions,
|
||||
ATTR_NAME: station.get("info", {}).get(
|
||||
"name", station["macAddress"]
|
||||
),
|
||||
}
|
||||
|
||||
# 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
|
||||
@@ -217,26 +467,28 @@ 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,
|
||||
description: EntityDescription,
|
||||
sensor_type: str,
|
||||
sensor_name: str,
|
||||
device_class: str | None,
|
||||
) -> 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}_{description.name}"
|
||||
self._attr_unique_id = f"{mac_address}_{description.key}"
|
||||
self._attr_name = f"{station_name}_{sensor_name}"
|
||||
self._attr_should_poll = False
|
||||
self._attr_unique_id = f"{mac_address}_{sensor_type}"
|
||||
self._mac_address = mac_address
|
||||
self.entity_description = description
|
||||
self._sensor_type = sensor_type
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Register callbacks."""
|
||||
@@ -244,18 +496,18 @@ class AmbientWeatherEntity(Entity):
|
||||
@callback
|
||||
def update() -> None:
|
||||
"""Update the state."""
|
||||
if self.entity_description.key == TYPE_SOLARRADIATION_LX:
|
||||
if self._sensor_type == TYPE_SOLARRADIATION_LX:
|
||||
self._attr_available = (
|
||||
self._ambient.stations[self._mac_address][ATTR_LAST_DATA][
|
||||
self._ambient.stations[self._mac_address][ATTR_LAST_DATA].get(
|
||||
TYPE_SOLARRADIATION
|
||||
]
|
||||
)
|
||||
is not None
|
||||
)
|
||||
else:
|
||||
self._attr_available = (
|
||||
self._ambient.stations[self._mac_address][ATTR_LAST_DATA][
|
||||
self.entity_description.key
|
||||
]
|
||||
self._ambient.stations[self._mac_address][ATTR_LAST_DATA].get(
|
||||
self._sensor_type
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
||||
|
||||
@@ -1,209 +1,34 @@
|
||||
"""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 (
|
||||
DEVICE_CLASS_BATTERY,
|
||||
DEVICE_CLASS_CONNECTIVITY,
|
||||
DOMAIN as BINARY_SENSOR,
|
||||
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 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 . 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 .const import ATTR_LAST_DATA, ATTR_MONITORED_CONDITIONS, DATA_CLIENT, DOMAIN
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -212,29 +37,51 @@ 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]
|
||||
|
||||
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]
|
||||
]
|
||||
)
|
||||
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)
|
||||
|
||||
|
||||
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."""
|
||||
self._attr_is_on = (
|
||||
self._ambient.stations[self._mac_address][ATTR_LAST_DATA][
|
||||
self.entity_description.key
|
||||
]
|
||||
== self.entity_description.on_state
|
||||
state = self._ambient.stations[self._mac_address][ATTR_LAST_DATA].get(
|
||||
self._sensor_type
|
||||
)
|
||||
|
||||
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,10 +5,8 @@ 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"
|
||||
|
||||
@@ -1,554 +1,20 @@
|
||||
"""Support for Ambient Weather Station sensors."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR, SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
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.const import ATTR_NAME
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
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 . import (
|
||||
SENSOR_TYPES,
|
||||
TYPE_SOLARRADIATION,
|
||||
TYPE_SOLARRADIATION_LX,
|
||||
AmbientStation,
|
||||
AmbientWeatherEntity,
|
||||
)
|
||||
from .const import ATTR_LAST_DATA, ATTR_MONITORED_CONDITIONS, DATA_CLIENT, DOMAIN
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -557,22 +23,62 @@ async def async_setup_entry(
|
||||
"""Set up Ambient PWS sensors based on a config entry."""
|
||||
ambient = hass.data[DOMAIN][DATA_CLIENT][entry.entry_id]
|
||||
|
||||
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]
|
||||
]
|
||||
)
|
||||
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)
|
||||
|
||||
|
||||
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."""
|
||||
self._attr_native_value = self._ambient.stations[self._mac_address][
|
||||
ATTR_LAST_DATA
|
||||
][self.entity_description.key]
|
||||
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)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Le service est d\u00e9j\u00e0 configur\u00e9"
|
||||
"already_configured": "Cette cl\u00e9 d'application est d\u00e9j\u00e0 utilis\u00e9e."
|
||||
},
|
||||
"error": {
|
||||
"invalid_key": "Cl\u00e9 API invalide",
|
||||
"invalid_key": "Cl\u00e9 d'API et / ou cl\u00e9 d'application non valide",
|
||||
"no_devices": "Aucun appareil trouv\u00e9 dans le compte"
|
||||
},
|
||||
"step": {
|
||||
|
||||
@@ -232,6 +232,8 @@ 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,6 +180,7 @@ 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.
|
||||
@@ -200,6 +201,7 @@ 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.
|
||||
@@ -224,6 +226,7 @@ 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)
|
||||
@@ -341,6 +344,7 @@ 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,
|
||||
@@ -360,6 +364,7 @@ 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()
|
||||
@@ -374,16 +379,15 @@ 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("Assigned model=%s", resp)
|
||||
if resp:
|
||||
self._model = resp
|
||||
_LOGGER.debug("Device_type=%s", resp)
|
||||
if resp.startswith("type="):
|
||||
self._model = resp.split("=")[-1]
|
||||
else:
|
||||
self._model = "unknown"
|
||||
if self._attr_unique_id is None:
|
||||
@@ -424,46 +428,57 @@ 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,6 +129,7 @@ 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,16 +1,10 @@
|
||||
"""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,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.const import (
|
||||
CONF_RESOURCES,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
@@ -31,360 +25,74 @@ from . import DOMAIN
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SENSOR_PREFIX = "UPS "
|
||||
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]
|
||||
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],
|
||||
}
|
||||
|
||||
SPECIFIC_UNITS = {"ITEMP": TEMP_CELSIUS}
|
||||
INFERRED_UNITS = {
|
||||
@@ -403,7 +111,7 @@ INFERRED_UNITS = {
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_RESOURCES, default=[]): vol.All(
|
||||
cv.ensure_list, [vol.In(SENSOR_KEYS)]
|
||||
cv.ensure_list, [vol.In(SENSOR_TYPES)]
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -412,20 +120,25 @@ 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]
|
||||
resources = config[CONF_RESOURCES]
|
||||
entities = []
|
||||
|
||||
for resource in resources:
|
||||
if resource.upper() not in apcups_data.status:
|
||||
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:
|
||||
_LOGGER.warning(
|
||||
"Sensor type: %s does not appear in the APCUPSd status output",
|
||||
resource,
|
||||
sensor_type,
|
||||
)
|
||||
|
||||
entities = [
|
||||
APCUPSdSensor(apcups_data, description)
|
||||
for description in SENSOR_TYPES
|
||||
if description.key in resources
|
||||
]
|
||||
entities.append(APCUPSdSensor(apcups_data, sensor_type))
|
||||
|
||||
add_entities(entities, True)
|
||||
|
||||
@@ -446,18 +159,22 @@ def infer_unit(value):
|
||||
class APCUPSdSensor(SensorEntity):
|
||||
"""Representation of a sensor entity for APCUPSd status values."""
|
||||
|
||||
def __init__(self, data, description: SensorEntityDescription):
|
||||
def __init__(self, data, sensor_type):
|
||||
"""Initialize the sensor."""
|
||||
self.entity_description = description
|
||||
self._data = data
|
||||
self._attr_name = f"{SENSOR_PREFIX}{description.name}"
|
||||
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]
|
||||
|
||||
def update(self):
|
||||
"""Get the latest status and use it to update our sensor state."""
|
||||
key = self.entity_description.key.upper()
|
||||
if key not in self._data.status:
|
||||
if self.type.upper() not in self._data.status:
|
||||
self._attr_native_value = None
|
||||
else:
|
||||
self._attr_native_value, inferred_unit = infer_unit(self._data.status[key])
|
||||
if not self.native_unit_of_measurement:
|
||||
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_unit_of_measurement = inferred_unit
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"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": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"already_configured_device": "Le p\u00e9riph\u00e9rique 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": "Authentification invalide",
|
||||
"no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau",
|
||||
"invalid_auth": "Autentification invalide",
|
||||
"no_devices_found": "Aucun appareil d\u00e9tect\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 inattendue"
|
||||
"unknown": "Erreur innatendue"
|
||||
},
|
||||
"flow_title": "Apple TV: {name}",
|
||||
"step": {
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
"""Support for AquaLogic sensors."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.const import (
|
||||
CONF_MONITORED_CONDITIONS,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
@@ -23,88 +16,40 @@ 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]
|
||||
|
||||
@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]
|
||||
# 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],
|
||||
}
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_MONITORED_CONDITIONS, default=SENSOR_KEYS): vol.All(
|
||||
cv.ensure_list, [vol.In(SENSOR_KEYS)]
|
||||
vol.Required(CONF_MONITORED_CONDITIONS, default=list(SENSOR_TYPES)): vol.All(
|
||||
cv.ensure_list, [vol.In(SENSOR_TYPES)]
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -112,29 +57,26 @@ 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]
|
||||
monitored_conditions = config[CONF_MONITORED_CONDITIONS]
|
||||
for sensor_type in config[CONF_MONITORED_CONDITIONS]:
|
||||
sensors.append(AquaLogicSensor(processor, sensor_type))
|
||||
|
||||
entities = [
|
||||
AquaLogicSensor(processor, description)
|
||||
for description in SENSOR_TYPES
|
||||
if description.key in monitored_conditions
|
||||
]
|
||||
|
||||
async_add_entities(entities)
|
||||
async_add_entities(sensors)
|
||||
|
||||
|
||||
class AquaLogicSensor(SensorEntity):
|
||||
"""Sensor implementation for the AquaLogic component."""
|
||||
|
||||
entity_description: AquaLogicSensorEntityDescription
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(self, processor, description: AquaLogicSensorEntityDescription):
|
||||
def __init__(self, processor, sensor_type):
|
||||
"""Initialize sensor."""
|
||||
self.entity_description = description
|
||||
self._processor = processor
|
||||
self._attr_name = f"AquaLogic {description.name}"
|
||||
self._type = sensor_type
|
||||
self._attr_name = f"AquaLogic {SENSOR_TYPES[sensor_type][0]}"
|
||||
self._attr_icon = SENSOR_TYPES[sensor_type][2]
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
@@ -150,15 +92,11 @@ class AquaLogicSensor(SensorEntity):
|
||||
panel = self._processor.panel
|
||||
if panel is not None:
|
||||
if panel.is_metric:
|
||||
self._attr_native_unit_of_measurement = (
|
||||
self.entity_description.unit_metric
|
||||
)
|
||||
self._attr_native_unit_of_measurement = SENSOR_TYPES[self._type][1][0]
|
||||
else:
|
||||
self._attr_native_unit_of_measurement = (
|
||||
self.entity_description.unit_imperial
|
||||
)
|
||||
self._attr_native_unit_of_measurement = SENSOR_TYPES[self._type][1][1]
|
||||
|
||||
self._attr_native_value = getattr(panel, self.entity_description.key)
|
||||
self._attr_native_value = getattr(panel, self._type)
|
||||
self.async_write_ha_state()
|
||||
else:
|
||||
self._attr_native_unit_of_measurement = None
|
||||
|
||||
@@ -5,10 +5,7 @@ from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.automation import (
|
||||
AutomationActionType,
|
||||
AutomationTriggerInfo,
|
||||
)
|
||||
from homeassistant.components.automation import AutomationActionType
|
||||
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
@@ -60,10 +57,10 @@ async def async_attach_trigger(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
action: AutomationActionType,
|
||||
automation_info: AutomationTriggerInfo,
|
||||
automation_info: dict,
|
||||
) -> CALLBACK_TYPE:
|
||||
"""Attach a trigger."""
|
||||
trigger_data = automation_info["trigger_data"]
|
||||
trigger_data = automation_info.get("trigger_data", {}) if automation_info else {}
|
||||
job = HassJob(action)
|
||||
|
||||
if config[CONF_TYPE] == "turn_on":
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"already_in_progress": "La configuration est d\u00e9j\u00e0 en cours",
|
||||
"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.",
|
||||
"cannot_connect": "\u00c9chec de connexion"
|
||||
},
|
||||
"error": {
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
"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": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
"already_configured": "Un seul appareil Atag peut \u00eatre ajout\u00e9 \u00e0 Home Assistant"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
@@ -10,7 +10,7 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "H\u00f4te",
|
||||
"host": "Nom d'h\u00f4te ou adresse IP",
|
||||
"port": "Port"
|
||||
},
|
||||
"title": "Se connecter \u00e0 l'appareil"
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"reauth_successful": "La r\u00e9-authentification a r\u00e9ussi"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_auth": "Authentification invalide",
|
||||
"cannot_connect": "Impossible de se connecter, veuillez r\u00e9essayer",
|
||||
"invalid_auth": "Authentification non valide",
|
||||
"unknown": "Erreur inattendue"
|
||||
},
|
||||
"step": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion"
|
||||
"cannot_connect": "\u00c9chec \u00e0 la connexion"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any, Awaitable, Callable, Dict, TypedDict, cast
|
||||
from typing import Any, Awaitable, Callable, Dict, cast
|
||||
|
||||
import voluptuous as vol
|
||||
from voluptuous.humanize import humanize_error
|
||||
@@ -106,23 +106,6 @@ _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,5 +1,4 @@
|
||||
"""Constants for the Awair component."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
@@ -7,8 +6,9 @@ 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,6 +36,10 @@ 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]
|
||||
@@ -44,89 +48,71 @@ LOGGER = logging.getLogger(__package__)
|
||||
|
||||
UPDATE_INTERVAL = timedelta(minutes=5)
|
||||
|
||||
|
||||
@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
|
||||
),
|
||||
)
|
||||
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
|
||||
|
||||
@@ -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, CONF_ACCESS_TOKEN
|
||||
from homeassistant.const import ATTR_ATTRIBUTION, ATTR_DEVICE_CLASS, CONF_ACCESS_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@@ -22,14 +22,15 @@ 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(
|
||||
@@ -59,20 +60,16 @@ async def async_setup_entry(
|
||||
):
|
||||
"""Set up Awair sensor entity based on a config entry."""
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
entities = []
|
||||
sensors = []
|
||||
|
||||
data: list[AwairResult] = coordinator.data.values()
|
||||
for result in data:
|
||||
if result.air_data:
|
||||
entities.append(AwairSensor(result.device, coordinator, SENSOR_TYPE_SCORE))
|
||||
sensors.append(AwairSensor(API_SCORE, result.device, coordinator))
|
||||
device_sensors = result.air_data.sensors.keys()
|
||||
entities.extend(
|
||||
[
|
||||
AwairSensor(result.device, coordinator, description)
|
||||
for description in (*SENSOR_TYPES, *SENSOR_TYPES_DUST)
|
||||
if description.key in device_sensors
|
||||
]
|
||||
)
|
||||
for sensor in device_sensors:
|
||||
if sensor in SENSOR_TYPES:
|
||||
sensors.append(AwairSensor(sensor, result.device, coordinator))
|
||||
|
||||
# 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.
|
||||
@@ -81,53 +78,45 @@ 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:
|
||||
entities.extend(
|
||||
[
|
||||
AwairSensor(result.device, coordinator, description)
|
||||
for description in SENSOR_TYPES_DUST
|
||||
]
|
||||
)
|
||||
for alias_kind in DUST_ALIASES:
|
||||
sensors.append(AwairSensor(alias_kind, result.device, coordinator))
|
||||
|
||||
async_add_entities(entities)
|
||||
async_add_entities(sensors)
|
||||
|
||||
|
||||
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.entity_description = description
|
||||
self._kind = kind
|
||||
self._device = device
|
||||
|
||||
@property
|
||||
def name(self) -> str | None:
|
||||
def name(self) -> str:
|
||||
"""Return the name of the sensor."""
|
||||
name = SENSOR_TYPES[self._kind][ATTR_LABEL]
|
||||
if self._device.name:
|
||||
return f"{self._device.name} {self.entity_description.name}"
|
||||
name = f"{self._device.name} {name}"
|
||||
|
||||
return self.entity_description.name
|
||||
return name
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return the uuid as the unique_id."""
|
||||
unique_id_tag = self.entity_description.unique_id_tag
|
||||
unique_id_tag = SENSOR_TYPES[self._kind][ATTR_UNIQUE_ID]
|
||||
|
||||
# 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.entity_description.key == API_PM25
|
||||
and API_DUST in self._air_data.sensors
|
||||
):
|
||||
if self._kind == API_PM25 and API_DUST in self._air_data.sensors:
|
||||
unique_id_tag = "DUST"
|
||||
|
||||
return f"{self._device.uuid}_{unique_id_tag}"
|
||||
@@ -138,17 +127,16 @@ 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...
|
||||
sensor_type = self.entity_description.key
|
||||
if sensor_type in self._air_data.sensors:
|
||||
if self._kind in self._air_data.sensors:
|
||||
# then we are available.
|
||||
return True
|
||||
|
||||
# or, we're a dust alias
|
||||
if sensor_type in DUST_ALIASES and API_DUST in self._air_data.sensors:
|
||||
if self._kind in DUST_ALIASES and API_DUST in self._air_data.sensors:
|
||||
return True
|
||||
|
||||
# or we are API_SCORE
|
||||
if sensor_type == API_SCORE:
|
||||
if self._kind == API_SCORE:
|
||||
# then we are available.
|
||||
return True
|
||||
|
||||
@@ -159,24 +147,38 @@ 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 sensor_type == API_SCORE:
|
||||
if self._kind == API_SCORE:
|
||||
state = self._air_data.score
|
||||
elif sensor_type in DUST_ALIASES and API_DUST in self._air_data.sensors:
|
||||
elif self._kind in DUST_ALIASES and API_DUST in self._air_data.sensors:
|
||||
state = self._air_data.sensors.dust
|
||||
else:
|
||||
state = self._air_data.sensors[sensor_type]
|
||||
state = self._air_data.sensors[self._kind]
|
||||
|
||||
if sensor_type in {API_VOC, API_SCORE}:
|
||||
if self._kind == API_VOC or self._kind == API_SCORE:
|
||||
return round(state)
|
||||
|
||||
if sensor_type == API_TEMP:
|
||||
if self._kind == 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.
|
||||
@@ -199,11 +201,10 @@ 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 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:
|
||||
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:
|
||||
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": "La r\u00e9-authentification a r\u00e9ussi"
|
||||
"reauth_successful": "Jeton d'acc\u00e8s mis \u00e0 jour avec succ\u00e8s"
|
||||
},
|
||||
"error": {
|
||||
"invalid_access_token": "Jeton d'acc\u00e8s non valide",
|
||||
"unknown": "Erreur inattendue"
|
||||
"unknown": "Erreur d'API Awair inconnue."
|
||||
},
|
||||
"step": {
|
||||
"reauth": {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"already_in_progress": "La configuration est d\u00e9j\u00e0 en cours",
|
||||
"already_in_progress": "Le flux de configuration de l'appareil est d\u00e9j\u00e0 en cours.",
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_auth": "Authentification invalide"
|
||||
},
|
||||
@@ -15,7 +15,7 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "H\u00f4te",
|
||||
"host": "Nom d'h\u00f4te ou adresse IP",
|
||||
"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": "La r\u00e9-authentification a r\u00e9ussi"
|
||||
"reauth_successful": "Jeton d'acc\u00e8s mis \u00e0 jour avec succ\u00e8s"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
|
||||
@@ -1,14 +1,4 @@
|
||||
{
|
||||
"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",
|
||||
@@ -82,10 +72,6 @@
|
||||
"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,10 +178,6 @@
|
||||
"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\u00e9",
|
||||
"on": "Ouvert"
|
||||
"off": "Ferm\u00e9e",
|
||||
"on": "Ouverte"
|
||||
},
|
||||
"garage_door": {
|
||||
"off": "Ferm\u00e9",
|
||||
"on": "Ouvert"
|
||||
"off": "Ferm\u00e9e",
|
||||
"on": "Ouverte"
|
||||
},
|
||||
"gas": {
|
||||
"off": "Non d\u00e9tect\u00e9",
|
||||
@@ -191,8 +191,8 @@
|
||||
"on": "D\u00e9tect\u00e9e"
|
||||
},
|
||||
"window": {
|
||||
"off": "Ferm\u00e9",
|
||||
"on": "Ouvert"
|
||||
"off": "Ferm\u00e9e",
|
||||
"on": "Ouverte"
|
||||
}
|
||||
},
|
||||
"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": "dla {entity_name} nie ma dost\u0119pnej aktualizacji",
|
||||
"is_no_update": "{entity_name} jest aktualny(-a)",
|
||||
"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": "dla {entity_name} jest dost\u0119pna aktualizacja",
|
||||
"is_update": "{entity_name} ma dost\u0119pn\u0105 aktualizacj\u0119",
|
||||
"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": "wykonano aktualizacj\u0119 dla {entity_name}",
|
||||
"no_update": "{entity_name} zosta\u0142 zaktualizowany(-a)",
|
||||
"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": "brak aktualizacji",
|
||||
"on": "dost\u0119pna aktualizacja"
|
||||
"off": "Aktualny(-a)",
|
||||
"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": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
"already_configured": "Ce p\u00e9riph\u00e9rique BleBox est d\u00e9j\u00e0 configur\u00e9."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"unknown": "Erreur inattendue",
|
||||
"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.)",
|
||||
"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": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
"already_configured": "P\u00e9riph\u00e9rique d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
@@ -20,7 +20,7 @@
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "Mot de passe",
|
||||
"username": "Nom d'utilisateur"
|
||||
"username": "Identifiant"
|
||||
},
|
||||
"title": "Connectez-vous avec un compte Blink"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"cannot_connect": "\u00c9chec \u00e0 la connexion",
|
||||
"invalid_auth": "Authentification invalide"
|
||||
},
|
||||
"step": {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"cannot_connect": "Echec 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": "Jeton d'acc\u00e8s",
|
||||
"access_token": "Token d'acc\u00e8s",
|
||||
"host": "H\u00f4te"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
"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.",
|
||||
"unknown": "Error inesperado"
|
||||
"session_error": "Error de sesi\u00f3n: La API devuelve un resultado no correcto."
|
||||
},
|
||||
"flow_title": "Bosch SHC: {name}",
|
||||
"step": {
|
||||
@@ -19,9 +18,6 @@
|
||||
"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": "La r\u00e9-authentification a r\u00e9ussi"
|
||||
"reauth_successful": "R\u00e9-authentification r\u00e9ussie"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_auth": "Authentification invalide",
|
||||
"invalid_auth": "Authentification incorrecte",
|
||||
"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\u00e9-authentifier l'int\u00e9gration"
|
||||
"title": "R\u00e9authentification de l'int\u00e9gration"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"already_configured": "Ce t\u00e9l\u00e9viseur 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",
|
||||
"invalid_host": "Nom d'h\u00f4te ou adresse IP non valide",
|
||||
"cannot_connect": "\u00c9chec de connexion, h\u00f4te ou code PIN non valide.",
|
||||
"invalid_host": "Nom d'h\u00f4te ou adresse IP invalide.",
|
||||
"unsupported_model": "Votre mod\u00e8le de t\u00e9l\u00e9viseur n'est pas pris en charge."
|
||||
},
|
||||
"step": {
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "H\u00f4te"
|
||||
"host": "Nom d'h\u00f4te ou adresse IP"
|
||||
},
|
||||
"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"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"already_in_progress": "La configuration est d\u00e9j\u00e0 en cours",
|
||||
"already_in_progress": "Il y a d\u00e9j\u00e0 un processus de configuration en cours pour cet appareil",
|
||||
"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": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"already_configured": "Cette imprimante est d\u00e9j\u00e0 configur\u00e9e.",
|
||||
"unsupported_model": "Ce mod\u00e8le d'imprimante n'est pas pris en charge."
|
||||
},
|
||||
"error": {
|
||||
@@ -13,7 +13,7 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "H\u00f4te",
|
||||
"host": "Nom d'h\u00f4te ou adresse IP",
|
||||
"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": "H\u00f4te",
|
||||
"host": "Nom d'h\u00f4te ou adresse IP",
|
||||
"passkey": "Cha\u00eene de cl\u00e9 d'acc\u00e8s",
|
||||
"password": "Mot de passe",
|
||||
"port": "Port",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible."
|
||||
"single_instance_allowed": "Une seule configuration de Google Cast est n\u00e9cessaire."
|
||||
},
|
||||
"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 commencer la configuration ?"
|
||||
"description": "Voulez-vous configurer Google Cast?"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3,7 +3,6 @@ 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
|
||||
@@ -45,7 +44,7 @@ async def async_unload_entry(hass, entry):
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
class CertExpiryDataUpdateCoordinator(DataUpdateCoordinator[Optional[datetime]]):
|
||||
class CertExpiryDataUpdateCoordinator(DataUpdateCoordinator[datetime]):
|
||||
"""Class to manage fetching Cert Expiry data from single endpoint."""
|
||||
|
||||
def __init__(self, hass, host, port):
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
"""Config flow for the Cert Expiry platform."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
@@ -27,7 +25,7 @@ class CertexpiryConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the config flow."""
|
||||
self._errors: dict[str, str] = {}
|
||||
self._errors = {}
|
||||
|
||||
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": "Le service est d\u00e9j\u00e0 configur\u00e9",
|
||||
"already_configured": "Cette combinaison h\u00f4te et port est d\u00e9j\u00e0 configur\u00e9e",
|
||||
"import_failed": "\u00c9chec de l'importation \u00e0 partir de la configuration"
|
||||
},
|
||||
"error": {
|
||||
|
||||
@@ -5,10 +5,7 @@ from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.automation import (
|
||||
AutomationActionType,
|
||||
AutomationTriggerInfo,
|
||||
)
|
||||
from homeassistant.components.automation import AutomationActionType
|
||||
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA
|
||||
from homeassistant.components.homeassistant.triggers import (
|
||||
numeric_state as numeric_state_trigger,
|
||||
@@ -115,7 +112,7 @@ async def async_attach_trigger(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
action: AutomationActionType,
|
||||
automation_info: AutomationTriggerInfo,
|
||||
automation_info: dict,
|
||||
) -> 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.initialize()
|
||||
await cloud.start()
|
||||
await http_api.async_setup(hass)
|
||||
|
||||
account_link.async_setup(hass)
|
||||
|
||||
@@ -4,10 +4,9 @@ import logging
|
||||
from typing import Any
|
||||
|
||||
import aiohttp
|
||||
from awesomeversion import AwesomeVersion
|
||||
from hass_nabucasa import account_link
|
||||
|
||||
from homeassistant.const import __version__ as HA_VERSION
|
||||
from homeassistant.const import MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_entry_oauth2_flow, event
|
||||
|
||||
@@ -17,8 +16,6 @@ DATA_SERVICES = "cloud_account_link_services"
|
||||
CACHE_TIMEOUT = 3600
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CURRENT_VERSION = AwesomeVersion(HA_VERSION)
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup(hass: HomeAssistant):
|
||||
@@ -33,12 +30,43 @@ async def async_provide_implementation(hass: HomeAssistant, domain: str):
|
||||
services = await _get_services(hass)
|
||||
|
||||
for service in services:
|
||||
if service["service"] == domain and CURRENT_VERSION >= service["min_version"]:
|
||||
if service["service"] == domain and _is_older(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 cloud_started(self) -> None:
|
||||
"""When cloud is started."""
|
||||
async def logged_in(self) -> None:
|
||||
"""When user logs in."""
|
||||
is_new_user = await self.prefs.async_set_username(self.cloud.username)
|
||||
|
||||
async def enable_alexa(_):
|
||||
@@ -150,10 +150,7 @@ class CloudClient(Interface):
|
||||
if tasks:
|
||||
await asyncio.gather(*(task(None) for task in tasks))
|
||||
|
||||
async def cloud_stopped(self) -> None:
|
||||
"""When the cloud is stopped."""
|
||||
|
||||
async def logout_cleanups(self) -> None:
|
||||
async def 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.enabled and self._prefs.google_report_state
|
||||
return self._cloud.is_logged_in 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, cloud_api, thingtalk
|
||||
from hass_nabucasa import Cloud, auth, thingtalk
|
||||
from hass_nabucasa.const import STATE_DISCONNECTED
|
||||
from hass_nabucasa.voice import MAP_VOICE
|
||||
import voluptuous as vol
|
||||
@@ -24,6 +24,7 @@ from homeassistant.const import (
|
||||
HTTP_BAD_GATEWAY,
|
||||
HTTP_BAD_REQUEST,
|
||||
HTTP_INTERNAL_SERVER_ERROR,
|
||||
HTTP_OK,
|
||||
HTTP_UNAUTHORIZED,
|
||||
)
|
||||
|
||||
@@ -46,6 +47,30 @@ 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,
|
||||
@@ -69,11 +94,17 @@ _CLOUD_ERRORS = {
|
||||
async def async_setup(hass):
|
||||
"""Initialize the HTTP API."""
|
||||
async_register_command = hass.components.websocket_api.async_register_command
|
||||
async_register_command(websocket_cloud_status)
|
||||
async_register_command(websocket_subscription)
|
||||
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_update_prefs)
|
||||
async_register_command(websocket_hook_create)
|
||||
async_register_command(websocket_hook_delete)
|
||||
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_remote_connect)
|
||||
async_register_command(websocket_remote_disconnect)
|
||||
|
||||
@@ -280,7 +311,6 @@ 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.
|
||||
|
||||
@@ -314,19 +344,36 @@ 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]
|
||||
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"
|
||||
|
||||
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"
|
||||
)
|
||||
)
|
||||
else:
|
||||
connection.send_result(msg["id"], data)
|
||||
|
||||
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))
|
||||
|
||||
|
||||
@_require_cloud_login
|
||||
@@ -382,12 +429,6 @@ 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]
|
||||
@@ -398,12 +439,6 @@ 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.49.0"],
|
||||
"requirements": ["hass-nabucasa==0.46.0"],
|
||||
"dependencies": ["http", "webhook"],
|
||||
"after_dependencies": ["google_assistant", "alexa"],
|
||||
"codeowners": ["@home-assistant/cloud"],
|
||||
|
||||
@@ -7,11 +7,10 @@
|
||||
"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,7 +33,6 @@ 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,7 +10,6 @@
|
||||
"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,7 +10,6 @@
|
||||
"relayer_connected": "Relay Verbunden",
|
||||
"remote_connected": "Remote verbunden",
|
||||
"remote_enabled": "Remote aktiviert",
|
||||
"remote_server": "Remote-Server",
|
||||
"subscription_expiration": "Ablauf des Abonnements"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"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,7 +10,6 @@
|
||||
"relayer_connected": "Relayer Connected",
|
||||
"remote_connected": "Remote Connected",
|
||||
"remote_enabled": "Remote Enabled",
|
||||
"remote_server": "Remote Server",
|
||||
"subscription_expiration": "Subscription Expiration"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"relayer_connected": "Relayer conectado",
|
||||
"remote_connected": "Remoto conectado",
|
||||
"remote_enabled": "Remoto habilitado",
|
||||
"remote_server": "Servidor remoto",
|
||||
"subscription_expiration": "Caducidad de la suscripci\u00f3n"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"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,8 +3,7 @@
|
||||
"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",
|
||||
"remote_server": "\u05e9\u05e8\u05ea \u05de\u05e8\u05d5\u05d7\u05e7"
|
||||
"logged_in": "\u05de\u05d7\u05d5\u05d1\u05e8"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@
|
||||
"relayer_connected": "K\u00f6zvet\u00edt\u0151 csatlakoztatva",
|
||||
"remote_connected": "T\u00e1voli csatlakoz\u00e1s",
|
||||
"remote_enabled": "T\u00e1voli hozz\u00e1f\u00e9r\u00e9s enged\u00e9lyezve",
|
||||
"remote_server": "T\u00e1voli szerver",
|
||||
"subscription_expiration": "El\u0151fizet\u00e9s lej\u00e1rata"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"relayer_connected": "Relayer connesso",
|
||||
"remote_connected": "Connesso in remoto",
|
||||
"remote_enabled": "Remoto abilitato",
|
||||
"remote_server": "Server remoto",
|
||||
"subscription_expiration": "Scadenza abbonamento"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"relayer_connected": "Relayer verbonden",
|
||||
"remote_connected": "Op afstand verbonden",
|
||||
"remote_enabled": "Op afstand ingeschakeld",
|
||||
"remote_server": "Externe server",
|
||||
"subscription_expiration": "Afloop abonnement"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"relayer_connected": "Relayer tilkoblet",
|
||||
"remote_connected": "Ekstern tilkobling",
|
||||
"remote_enabled": "Ekstern aktivert",
|
||||
"remote_server": "Ekstern server",
|
||||
"subscription_expiration": "Abonnementets utl\u00f8p"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"relayer_connected": "Relayer pod\u0142\u0105czony",
|
||||
"remote_connected": "Zdalny dost\u0119p pod\u0142\u0105czony",
|
||||
"remote_enabled": "Zdalny dost\u0119p w\u0142\u0105czony",
|
||||
"remote_server": "Zdalny serwer",
|
||||
"subscription_expiration": "Wyga\u015bni\u0119cie subskrypcji"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"relayer_connected": "Relayer \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d",
|
||||
"remote_connected": "\u0423\u0434\u0430\u043b\u0451\u043d\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d",
|
||||
"remote_enabled": "\u0423\u0434\u0430\u043b\u0451\u043d\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u043d",
|
||||
"remote_server": "\u0423\u0434\u0430\u043b\u0451\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440",
|
||||
"subscription_expiration": "\u0421\u0440\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"system_health": {
|
||||
"info": {
|
||||
"remote_server": "\u0e40\u0e0b\u0e34\u0e23\u0e4c\u0e1f\u0e40\u0e27\u0e2d\u0e23\u0e4c\u0e23\u0e30\u0e22\u0e30\u0e44\u0e01\u0e25"
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user