529 lines
18 KiB
Python
529 lines
18 KiB
Python
"""Constants."""
|
||
|
||
from __future__ import annotations
|
||
|
||
from enum import StrEnum
|
||
from typing import Final
|
||
|
||
# Integration specific constants.
|
||
DOMAIN = "sws12500"
|
||
DEV_DBG: Final = "dev_debug_checkbox"
|
||
|
||
|
||
# Common constants
|
||
API_KEY = "API_KEY"
|
||
API_ID = "API_ID"
|
||
|
||
SENSORS_TO_LOAD: Final = "sensors_to_load"
|
||
|
||
INVALID_CREDENTIALS: Final = [
|
||
"API",
|
||
"API_ID",
|
||
"API ID",
|
||
"_ID",
|
||
"ID",
|
||
"API KEY",
|
||
"API_KEY",
|
||
"KEY",
|
||
"_KEY",
|
||
]
|
||
|
||
|
||
# Sensor constants
|
||
BARO_PRESSURE: Final = "baro_pressure"
|
||
OUTSIDE_TEMP: Final = "outside_temp"
|
||
DEW_POINT: Final = "dew_point"
|
||
OUTSIDE_HUMIDITY: Final = "outside_humidity"
|
||
OUTSIDE_CONNECTION: Final = "outside_connection"
|
||
OUTSIDE_BATTERY: Final = "outside_battery"
|
||
WIND_SPEED: Final = "wind_speed"
|
||
WIND_GUST: Final = "wind_gust"
|
||
WIND_DIR: Final = "wind_dir"
|
||
WIND_AZIMUT: Final = "wind_azimut"
|
||
RAIN: Final = "rain"
|
||
HOURLY_RAIN: Final = "hourly_rain"
|
||
WEEKLY_RAIN: Final = "weekly_rain"
|
||
MONTHLY_RAIN: Final = "monthly_rain"
|
||
YEARLY_RAIN: Final = "yearly_rain"
|
||
DAILY_RAIN: Final = "daily_rain"
|
||
SOLAR_RADIATION: Final = "solar_radiation"
|
||
INDOOR_TEMP: Final = "indoor_temp"
|
||
INDOOR_HUMIDITY: Final = "indoor_humidity"
|
||
INDOOR_BATTERY: Final = "indoor_battery"
|
||
UV: Final = "uv"
|
||
CH2_TEMP: Final = "ch2_temp"
|
||
CH2_HUMIDITY: Final = "ch2_humidity"
|
||
CH2_CONNECTION: Final = "ch2_connection"
|
||
CH2_BATTERY: Final = "ch2_battery"
|
||
CH3_TEMP: Final = "ch3_temp"
|
||
CH3_HUMIDITY: Final = "ch3_humidity"
|
||
CH3_CONNECTION: Final = "ch3_connection"
|
||
CH3_BATTERY: Final = "ch3_battery"
|
||
CH4_TEMP: Final = "ch4_temp"
|
||
CH4_HUMIDITY: Final = "ch4_humidity"
|
||
CH4_CONNECTION: Final = "ch4_connection"
|
||
CH4_BATTERY: Final = "ch4_battery"
|
||
CH5_TEMP: Final = "ch5_temp"
|
||
CH5_HUMIDITY: Final = "ch5_humidity"
|
||
CH5_CONNECTION: Final = "ch5_connection"
|
||
CH5_BATTERY: Final = "ch5_battery"
|
||
CH6_TEMP: Final = "ch6_temp"
|
||
CH6_HUMIDITY: Final = "ch6_humidity"
|
||
CH6_CONNECTION: Final = "ch6_connection"
|
||
CH6_BATTERY: Final = "ch6_battery"
|
||
CH7_TEMP: Final = "ch7_temp"
|
||
CH7_HUMIDITY: Final = "ch7_humidity"
|
||
CH7_CONNECTION: Final = "ch7_connection"
|
||
CH7_BATTERY: Final = "ch7_battery"
|
||
CH8_TEMP: Final = "ch8_temp"
|
||
CH8_HUMIDITY: Final = "ch8_humidity"
|
||
CH8_CONNECTION: Final = "ch8_connection"
|
||
CH8_BATTERY: Final = "ch8_battery"
|
||
HEAT_INDEX: Final = "heat_index"
|
||
CHILL_INDEX: Final = "chill_index"
|
||
WBGT_TEMP: Final = "wbgt_temp"
|
||
HCHO: Final = "hcho"
|
||
VOC: Final = "voc"
|
||
T9_BATTERY: Final = "t9_battery" # T9 sensors are HCHO and VOC
|
||
T9_CONN: Final = "t9_conn"
|
||
|
||
|
||
# Health specific constants
|
||
HEALTH_URL = "/station/health"
|
||
|
||
|
||
# PWS specific constants
|
||
DEFAULT_URL = "/weatherstation/updateweatherstation.php"
|
||
|
||
PURGE_DATA: Final = [
|
||
"ID",
|
||
"PASSWORD",
|
||
"wsid",
|
||
"wspw",
|
||
"passkey",
|
||
"PASSKEY",
|
||
"action",
|
||
"rtfreq",
|
||
"realtime",
|
||
"dateutc",
|
||
"solarradiation",
|
||
"indoortempf",
|
||
"indoorhumidity",
|
||
"dailyrainin",
|
||
]
|
||
|
||
"""NOTE: These are sensors that should be available with PWS protocol according to https://support.weather.com/s/article/PWS-Upload-Protocol?language=en_US:
|
||
|
||
I have no option to test, if it will work correctly. So their implementation will be in future releases.
|
||
|
||
leafwetness - [%]
|
||
+ for sensor 2 use leafwetness2
|
||
visibility - [nm visibility]
|
||
pweather - [text] -- metar style (+RA)
|
||
clouds - [text] -- SKC, FEW, SCT, BKN, OVC
|
||
Pollution Fields:
|
||
|
||
AqNO - [ NO (nitric oxide) ppb ]
|
||
AqNO2T - (nitrogen dioxide), true measure ppb
|
||
AqNO2 - NO2 computed, NOx-NO ppb
|
||
AqNO2Y - NO2 computed, NOy-NO ppb
|
||
AqNOX - NOx (nitrogen oxides) - ppb
|
||
AqNOY - NOy (total reactive nitrogen) - ppb
|
||
AqNO3 - NO3 ion (nitrate, not adjusted for ammonium ion) UG/M3
|
||
AqSO4 - SO4 ion (sulfate, not adjusted for ammonium ion) UG/M3
|
||
AqSO2 - (sulfur dioxide), conventional ppb
|
||
AqSO2T - trace levels ppb
|
||
AqCO - CO (carbon monoxide), conventional ppm
|
||
AqCOT -CO trace levels ppb
|
||
AqEC - EC (elemental carbon) – PM2.5 UG/M3
|
||
AqOC - OC (organic carbon, not adjusted for oxygen and hydrogen) – PM2.5 UG/M3
|
||
AqBC - BC (black carbon at 880 nm) UG/M3
|
||
AqUV-AETH - UV-AETH (second channel of Aethalometer at 370 nm) UG/M3
|
||
AqPM2.5 - PM2.5 mass - UG/M3
|
||
AqPM10 - PM10 mass - PM10 mass
|
||
AqOZONE - Ozone - ppb
|
||
|
||
"""
|
||
REMAP_ITEMS: dict[str, str] = {
|
||
"baromin": BARO_PRESSURE,
|
||
"tempf": OUTSIDE_TEMP,
|
||
"dewptf": DEW_POINT,
|
||
"humidity": OUTSIDE_HUMIDITY,
|
||
"windspeedmph": WIND_SPEED,
|
||
"windgustmph": WIND_GUST,
|
||
"winddir": WIND_DIR,
|
||
"rainin": RAIN,
|
||
"dailyrainin": DAILY_RAIN,
|
||
"solarradiation": SOLAR_RADIATION,
|
||
"indoortempf": INDOOR_TEMP,
|
||
"indoorhumidity": INDOOR_HUMIDITY,
|
||
"UV": UV,
|
||
"soiltempf": CH2_TEMP,
|
||
"soilmoisture": CH2_HUMIDITY,
|
||
"soiltemp2f": CH3_TEMP,
|
||
"soilmoisture2": CH3_HUMIDITY,
|
||
"soiltemp3f": CH4_TEMP,
|
||
"soilmoisture3": CH4_HUMIDITY,
|
||
"soiltemp4f": CH5_TEMP,
|
||
"soilmoisture4": CH5_HUMIDITY,
|
||
"soiltemp5f": CH6_TEMP,
|
||
"soilmoisture5": CH6_HUMIDITY,
|
||
}
|
||
|
||
|
||
WSLINK_URL = "/data/upload.php"
|
||
|
||
WINDY_URL = "https://stations.windy.com/api/v2/observation/update"
|
||
|
||
POCASI_CZ_URL: Final = "http://ms.pocasimeteo.cz"
|
||
POCASI_CZ_SEND_MINIMUM: Final = 12 # minimal time to resend data
|
||
# Failed sends in a row before resending is disabled. Timeouts are excluded on purpose -
|
||
# a slow upstream is transient, see the TimeoutError branch in `PocasiPush`.
|
||
POCASI_CZ_MAX_RETRIES: Final = 3
|
||
|
||
# Pocasi Meteo accepts Ecowitt only as a POST in the Ecowitt protocol itself - the
|
||
# station payload is forwarded verbatim rather than translated to PWS. Per their
|
||
# integration notes the account credentials go in the query string, and the password
|
||
# parameter is `PAS` (not `PASS` / `PASSWORD` as on the PWS endpoint):
|
||
# ms.pocasimeteo.cz/ws_ecowitt/ws.php?ID=xxxx&PAS=yyyy
|
||
POCASI_CZ_ECOWITT_URL: Final = "/ws_ecowitt/ws.php"
|
||
POCASI_CZ_ECOWITT_ID_PARAM: Final = "ID"
|
||
POCASI_CZ_ECOWITT_PW_PARAM: Final = "PAS"
|
||
|
||
|
||
WSLINK: Final = "wslink"
|
||
LEGACY_ENABLED: Final = "legacy_enabled"
|
||
|
||
WINDY_MAX_RETRIES: Final = 3
|
||
WSLINK_ADDON_PORT: Final = "WSLINK_ADDON_PORT"
|
||
|
||
# Forwarding to Windy / Pocasi Meteo is awaited inline in the station's own request
|
||
# path, so an upstream that accepts the connection and never answers would hold the
|
||
# webhook open. aiohttp's default is a 5 minute total, long past the point where the
|
||
# station itself gives up; cut the send short instead and retry on the next push.
|
||
FORWARD_TIMEOUT: Final = 15 # seconds
|
||
|
||
ECOWITT: Final = "ecowitt"
|
||
ECOWITT_WEBHOOK_ID: Final = "ecowitt_webhook_id"
|
||
ECOWITT_ENABLED: Final = "ecowitt_enabled"
|
||
ECOWITT_URL_PREFIX: Final = "/weatherhub"
|
||
|
||
# Ecowitt field names -> the field names Windy accepts.
|
||
#
|
||
# Windy has no Ecowitt endpoint, so an Ecowitt payload has to be translated before it
|
||
# can be forwarded there. The target vocabulary is mostly the PWS/WU one Windy already
|
||
# receives from a real PWS station, but not exactly: Windy documents the UV index as
|
||
# lowercase `uv` (https://stations.windy.com/api-reference) while WU spells it `UV`
|
||
# (see REMAP_ITEMS). This table follows Windy, which is its only consumer.
|
||
#
|
||
# Deliberately an allowlist, not a "strip the bad keys" denylist: the Ecowitt payload
|
||
# also carries device metadata (stationtype, model, freq, runtime, heap, interval) that
|
||
# is meaningless upstream, and a denylist would forward whatever fields a future
|
||
# firmware adds.
|
||
#
|
||
# This is the *full* mapping, not the set of fields that end up on the wire: five of
|
||
# the entries below (`dateutc`, `solarradiation`, `dailyrainin`, `indoortempf`,
|
||
# `indoorhumidity`) are in PURGE_DATA and are stripped by `push_data_to_windy` right
|
||
# after the conversion. They are listed so the translation itself stays complete and
|
||
# keeps working if the purge list ever changes.
|
||
#
|
||
# Multi-channel temp/humidity (temp1f..temp7f) are intentionally absent - the PWS
|
||
# protocol has no equivalent, and mapping them onto its soil fields would send wrong
|
||
# data.
|
||
REMAP_ECOWITT_TO_WINDY: Final[dict[str, str]] = {
|
||
# same name on both sides, listed so the allowlist is complete
|
||
"tempf": "tempf",
|
||
"humidity": "humidity",
|
||
"windspeedmph": "windspeedmph",
|
||
"windgustmph": "windgustmph",
|
||
"winddir": "winddir",
|
||
"dailyrainin": "dailyrainin",
|
||
"solarradiation": "solarradiation",
|
||
"dateutc": "dateutc",
|
||
"uv": "uv",
|
||
# renamed between the two protocols
|
||
"dewpointf": "dewptf",
|
||
"baromrelin": "baromin",
|
||
"tempinf": "indoortempf",
|
||
"humidityin": "indoorhumidity",
|
||
"hourlyrainin": "rainin", # WU `rainin` is the accumulation over the past hour
|
||
}
|
||
|
||
REMAP_ECOWITT_COMPAT: dict[str, str] = {
|
||
"tempf": OUTSIDE_TEMP,
|
||
"humidity": OUTSIDE_HUMIDITY,
|
||
"dewpointf": DEW_POINT,
|
||
"windspeedmph": WIND_SPEED,
|
||
"windgustmph": WIND_GUST,
|
||
"winddir": WIND_DIR,
|
||
"dailyrainin": DAILY_RAIN,
|
||
"solarradiation": SOLAR_RADIATION,
|
||
"tempinf": INDOOR_TEMP,
|
||
"humidityin": INDOOR_HUMIDITY,
|
||
"uv": UV,
|
||
"baromrelin": BARO_PRESSURE,
|
||
"temp1f": CH2_TEMP,
|
||
"humidity1": CH2_HUMIDITY,
|
||
"temp2f": CH3_TEMP,
|
||
"humidity2": CH3_HUMIDITY,
|
||
"temp3f": CH4_TEMP,
|
||
"humidity3": CH4_HUMIDITY,
|
||
"temp4f": CH5_TEMP,
|
||
"humidity4": CH5_HUMIDITY,
|
||
"temp5f": CH6_TEMP,
|
||
"humidity5": CH6_HUMIDITY,
|
||
"temp6f": CH7_TEMP,
|
||
"humidity6": CH7_HUMIDITY,
|
||
"temp7f": CH8_TEMP,
|
||
"humidity7": CH8_HUMIDITY,
|
||
}
|
||
|
||
POCASI_CZ_API_KEY = "POCASI_CZ_API_KEY"
|
||
POCASI_CZ_API_ID = "POCASI_CZ_API_ID"
|
||
POCASI_CZ_SEND_INTERVAL = "POCASI_SEND_INTERVAL"
|
||
POCASI_CZ_ENABLED = "pocasi_enabled_checkbox"
|
||
# Misspelled key used by config entries created before version 2 (see `async_migrate_entry`).
|
||
POCASI_CZ_ENABLED_LEGACY: Final = "pocasi_enabled_chcekbox"
|
||
POCASI_CZ_LOGGER_ENABLED = "pocasi_logger_checkbox"
|
||
POCASI_INVALID_KEY: Final = "Pocasi Meteo refused to accept data. Invalid ID/Key combination?"
|
||
POCASI_CZ_SUCCESS: Final = "Successfully sent data to Pocasi Meteo"
|
||
POCASI_CZ_UNEXPECTED: Final = (
|
||
f"Pocasi Meteo responded unexpectedly {POCASI_CZ_MAX_RETRIES} times in row. Resending is now disabled!"
|
||
)
|
||
|
||
WINDY_STATION_ID = "WINDY_STATION_ID"
|
||
WINDY_STATION_PW = "WINDY_STATION_PWD"
|
||
WINDY_ENABLED: Final = "windy_enabled_checkbox"
|
||
WINDY_LOGGER_ENABLED: Final = "windy_logger_checkbox"
|
||
WINDY_NOT_INSERTED: Final = "Windy responded with 400 error. Invalid ID/password combination?"
|
||
WINDY_INVALID_KEY: Final = (
|
||
"Windy API KEY is invalid. Send data to Windy is now disabled. Check your API KEY and try again."
|
||
)
|
||
WINDY_SUCCESS: Final = "Windy successfully sent data and data was successfully inserted by Windy API"
|
||
WINDY_UNEXPECTED: Final = (
|
||
f"Windy responded unexpectedly {WINDY_MAX_RETRIES} times in a row. Send to Windy is now disabled!"
|
||
)
|
||
|
||
|
||
|
||
REMAP_WSLINK_ITEMS: dict[str, str] = {
|
||
"intem": INDOOR_TEMP,
|
||
"inhum": INDOOR_HUMIDITY,
|
||
"t1tem": OUTSIDE_TEMP,
|
||
"t1hum": OUTSIDE_HUMIDITY,
|
||
"t1dew": DEW_POINT,
|
||
"t1wdir": WIND_DIR,
|
||
"t1ws": WIND_SPEED,
|
||
"t1wgust": WIND_GUST,
|
||
"t1rainra": RAIN,
|
||
"t1raindy": DAILY_RAIN,
|
||
"t1solrad": SOLAR_RADIATION,
|
||
"rbar": BARO_PRESSURE,
|
||
"t1uvi": UV,
|
||
"t234c1tem": CH2_TEMP,
|
||
"t234c1hum": CH2_HUMIDITY,
|
||
# NOTE: connection flags (t1cn / t234cXcn / t9cn) are intentionally NOT remapped.
|
||
# They are used only as gating inputs (see CONNECTION_GATED_SENSORS), which read the
|
||
# raw payload keys. Remapping them used to leak ghost "*_connection" keys (with no
|
||
# entity) into the coordinator data and into persisted SENSORS_TO_LOAD.
|
||
"t1chill": CHILL_INDEX,
|
||
"t1heat": HEAT_INDEX,
|
||
"t1rainhr": HOURLY_RAIN,
|
||
"t1rainwy": WEEKLY_RAIN,
|
||
"t1rainmth": MONTHLY_RAIN,
|
||
"t1rainyr": YEARLY_RAIN,
|
||
"t234c2tem": CH3_TEMP,
|
||
"t234c2hum": CH3_HUMIDITY,
|
||
"t234c3tem": CH4_TEMP,
|
||
"t234c3hum": CH4_HUMIDITY,
|
||
"t234c4tem": CH5_TEMP,
|
||
"t234c4hum": CH5_HUMIDITY,
|
||
"t234c5tem": CH6_TEMP,
|
||
"t234c5hum": CH6_HUMIDITY,
|
||
"t234c6tem": CH7_TEMP,
|
||
"t234c6hum": CH7_HUMIDITY,
|
||
"t234c7tem": CH8_TEMP,
|
||
"t234c7hum": CH8_HUMIDITY,
|
||
"t1bat": OUTSIDE_BATTERY,
|
||
"inbat": INDOOR_BATTERY,
|
||
"t234c1bat": CH2_BATTERY,
|
||
"t234c2bat": CH3_BATTERY,
|
||
"t234c3bat": CH4_BATTERY,
|
||
"t234c4bat": CH5_BATTERY,
|
||
"t234c5bat": CH6_BATTERY,
|
||
"t234c6bat": CH7_BATTERY,
|
||
"t234c7bat": CH8_BATTERY,
|
||
"t1wbgt": WBGT_TEMP,
|
||
"t9hcho": HCHO,
|
||
"t9voclv": VOC,
|
||
"t9bat": T9_BATTERY, # T9 battery is 0-5, where 5 is full
|
||
}
|
||
|
||
# NOTE: Add more sensors
|
||
#
|
||
# 'inbat' indoor battery level (1 normal, 0 low)
|
||
# 't1bat': outdoor battery level (1 normal, 0 low)
|
||
# 't234c1bat': CH2 battery level (1 normal, 0 low) CH2 in integration is CH1 in WSLink
|
||
#
|
||
# In the following there are sensors that should be available by WSLink.
|
||
# We need to compare them to PWS API to make sure, we have the same internal
|
||
# representation of same sensors.
|
||
|
||
### TODO: These are sensors, that should be supported in WSLink API according to their API documentation:
|
||
# &t5lst= Last Lightning strike time integer
|
||
# &t5lskm= Lightning distance integer km
|
||
# &t5lsf= Lightning strike count last 1 Hours integer
|
||
# &t5ls5mtc= Lightning count total of during 5 minutes integer
|
||
# &t5ls30mtc= Lightning count total of during 30 minutes integer
|
||
# &t5ls1htc= Lightning count total of during 1 Hour integer
|
||
# &t5ls1dtc= Lightning count total of during 1 day integer
|
||
# &t5lsbat= Lightning Sensor battery (Normal=1, Low battery=0) integer
|
||
# &t5lscn= Lightning Sensor connection (Connected=1, No connect=0) integer
|
||
# &t6c1wls= Water leak sensor CH1 (Leak=1, No leak=0) integer
|
||
# &t6c1bat= Water leak sensor CH1 battery (Normal=1, Low battery=0) integer
|
||
# &t6c1cn= Water leak sensor CH1 connection (Connected=1, No connect=0) integer
|
||
# &t6c2wls= Water leak sensor CH2 (Leak=1, No leak=0) integer
|
||
# &t6c2bat= Water leak sensor CH2 battery (Normal=1, Low battery=0) integer
|
||
# &t6c2cn= Water leak sensor CH2 connection (Connected=1, No connect=0) integer
|
||
# &t6c3wls= Water leak sensor CH3 (Leak=1, No leak=0) integer
|
||
# &t6c3bat= Water leak sensor CH3 battery (Normal=1, Low battery=0) integer
|
||
# &t6c3cn= Water leak sensor CH3 connection (Connected=1, No connect=0) integer
|
||
# &t6c4wls= Water leak sensor CH4 (Leak=1, No leak=0) integer
|
||
# &t6c4bat= Water leak sensor CH4 battery (Normal=1, Low battery=0) integer
|
||
# &t6c4cn= Water leak sensor CH4 connection (Connected=1, No connect=0) integer
|
||
# &t6c5wls= Water leak sensor CH5 (Leak=1, No leak=0) integer
|
||
# &t6c5bat= Water leak sensor CH5 battery (Normal=1, Low battery=0) integer
|
||
# &t6c5cn= Water leak sensor CH5 connection (Connected=1, No connect=0) integer
|
||
# &t6c6wls= Water leak sensor CH6 (Leak=1, No leak=0) integer
|
||
# &t6c6bat= Water leak sensor CH6 battery (Normal=1, Low battery=0) integer
|
||
# &t6c6cn= Water leak sensor CH6 connection (Connected=1, No connect=0) integer
|
||
# &t6c7wls= Water leak sensor CH7 (Leak=1, No leak=0) integer
|
||
# &t6c7bat= Water leak sensor CH7 battery (Normal=1, Low battery=0) integer
|
||
# &t6c7cn= Water leak sensor CH7 connection (Connected=1, No connect=0) integer
|
||
# &t8pm25= PM2.5 concentration integer ug/m3
|
||
# &t8pm10= PM10 concentration integer ug/m3
|
||
# &t8pm25ai= PM2.5 AQI integer
|
||
# &t8pm10ai = PM10 AQI integer
|
||
# &t8bat= PM sensor battery level (0~5) remark: 5 is full integer
|
||
# &t8cn= PM sensor connection (Connected=1, No connect=0) integer
|
||
# &t9hcho= HCHO concentration integer ppb
|
||
# &t9voclv= VOC level (1~5) 1 is the highest level, 5 is the lowest VOC level integer
|
||
# &t9bat= HCHO / VOC sensor battery level (0~5) remark: 5 is full integer
|
||
# &t9cn= HCHO / VOC sensor connection (Connected=1, No connect=0) integer
|
||
# &t10co2= CO2 concentration integer ppm
|
||
# &t10bat= CO2 sensor battery level (0~5) remark: 5 is full integer
|
||
# &t10cn= CO2 sensor connection (Connected=1, No connect=0) integer
|
||
# &t11co= CO concentration integer ppm
|
||
# &t11bat= CO sensor battery level (0~5) remark: 5 is full integer
|
||
# &t11cn= CO sensor connection (Connected=1, No connect=0) integer
|
||
#
|
||
|
||
|
||
# How the station reports each battery decides which entity it becomes. The two tuples
|
||
# below are the single source of truth for that split - `battery_sensors_def` generates
|
||
# both entity description sets from them, so a key cannot end up with two entities.
|
||
#
|
||
# They must stay disjoint, and every `*_BATTERY` constant must appear in exactly one of
|
||
# them; `tests/test_battery_classification.py` enforces both. That matters because the
|
||
# WSLink API has more of each kind still to be implemented (see the TODO block above):
|
||
# `t5lsbat` / `t6c1-7bat` are 0/1, while `t8bat` / `t10bat` / `t11bat` are 0-5.
|
||
|
||
# Reported as 0/1 (low/normal) -> BinarySensorDeviceClass.BATTERY.
|
||
BATTERY_LIST: Final[tuple[str, ...]] = (
|
||
OUTSIDE_BATTERY,
|
||
INDOOR_BATTERY,
|
||
CH2_BATTERY,
|
||
CH3_BATTERY,
|
||
CH4_BATTERY,
|
||
CH5_BATTERY,
|
||
CH6_BATTERY,
|
||
CH7_BATTERY,
|
||
CH8_BATTERY,
|
||
)
|
||
|
||
# Reported as a 0-5 level, 5 being full -> percentage SensorDeviceClass.BATTERY.
|
||
BATTERY_NON_BINARY: Final[tuple[str, ...]] = (T9_BATTERY,)
|
||
|
||
|
||
CONNECTION_GATED_SENSORS: Final[dict[str, list[str]]] = {
|
||
# Multi-channel temp/humidity probes (CH2 - CH8)
|
||
"t234c1cn": [CH2_TEMP, CH2_HUMIDITY, CH2_BATTERY],
|
||
"t234c2cn": [CH3_TEMP, CH3_HUMIDITY, CH3_BATTERY],
|
||
"t234c3cn": [CH4_TEMP, CH4_HUMIDITY, CH4_BATTERY],
|
||
"t234c4cn": [CH5_TEMP, CH5_HUMIDITY, CH5_BATTERY],
|
||
"t234c5cn": [CH6_TEMP, CH6_HUMIDITY, CH6_BATTERY],
|
||
"t234c6cn": [CH7_TEMP, CH7_HUMIDITY, CH7_BATTERY],
|
||
"t234c7cn": [CH8_TEMP, CH8_HUMIDITY, CH8_BATTERY],
|
||
# T9 HCHO/VOC probe
|
||
"t9cn": [HCHO, VOC, T9_BATTERY],
|
||
}
|
||
|
||
|
||
class VOCLevel(StrEnum):
|
||
"""WSLink VOC Level 1-5 (1-worst)."""
|
||
|
||
UNHEALTHY = "unhealthy"
|
||
POOR = "poor"
|
||
MODERATE = "moderate"
|
||
GOOD = "good"
|
||
EXCELLENT = "excellent"
|
||
|
||
|
||
VOC_LEVEL_MAP: dict[int, VOCLevel] = {
|
||
1: VOCLevel.UNHEALTHY,
|
||
2: VOCLevel.POOR,
|
||
3: VOCLevel.MODERATE,
|
||
4: VOCLevel.GOOD,
|
||
5: VOCLevel.EXCELLENT,
|
||
}
|
||
|
||
|
||
class UnitOfDir(StrEnum):
|
||
"""Wind direrction azimut."""
|
||
|
||
NNE = "nne"
|
||
NE = "ne"
|
||
ENE = "ene"
|
||
E = "e"
|
||
ESE = "ese"
|
||
SE = "se"
|
||
SSE = "sse"
|
||
S = "s"
|
||
SSW = "ssw"
|
||
SW = "sw"
|
||
WSW = "wsw"
|
||
W = "w"
|
||
WNW = "wnw"
|
||
NW = "nw"
|
||
NNW = "nnw"
|
||
N = "n"
|
||
|
||
|
||
AZIMUT: list[UnitOfDir] = [
|
||
UnitOfDir.NNE,
|
||
UnitOfDir.NE,
|
||
UnitOfDir.ENE,
|
||
UnitOfDir.E,
|
||
UnitOfDir.ESE,
|
||
UnitOfDir.SE,
|
||
UnitOfDir.SSE,
|
||
UnitOfDir.S,
|
||
UnitOfDir.SSW,
|
||
UnitOfDir.SW,
|
||
UnitOfDir.WSW,
|
||
UnitOfDir.W,
|
||
UnitOfDir.WNW,
|
||
UnitOfDir.NW,
|
||
UnitOfDir.NNW,
|
||
UnitOfDir.N,
|
||
]
|
||
|
||
|
||
class UnitOfBat(StrEnum):
|
||
"""Battery level unit of measure."""
|
||
|
||
LOW = "low"
|
||
NORMAL = "normal"
|
||
UNKNOWN = "drained"
|
||
|