meteocat 0.1.25 → 0.1.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yml +33 -33
- package/.pre-commit-config.yaml +37 -37
- package/.releaserc +23 -23
- package/.releaserc.toml +14 -14
- package/AUTHORS.md +12 -12
- package/CHANGELOG.md +430 -401
- package/README.md +40 -40
- package/custom_components/meteocat/__init__.py +136 -107
- package/custom_components/meteocat/condition.py +28 -28
- package/custom_components/meteocat/config_flow.py +199 -192
- package/custom_components/meteocat/const.py +55 -55
- package/custom_components/meteocat/coordinator.py +194 -195
- package/custom_components/meteocat/entity.py +98 -98
- package/custom_components/meteocat/helpers.py +42 -42
- package/custom_components/meteocat/manifest.json +12 -12
- package/custom_components/meteocat/options_flow.py +71 -71
- package/custom_components/meteocat/sensor.py +325 -303
- package/custom_components/meteocat/strings.json +25 -25
- package/custom_components/meteocat/translations/ca.json +25 -25
- package/custom_components/meteocat/translations/en.json +25 -25
- package/custom_components/meteocat/translations/es.json +25 -25
- package/custom_components/meteocat/version.py +2 -2
- package/filetree.py +48 -48
- package/filetree.txt +46 -46
- package/hacs.json +5 -5
- package/package.json +22 -22
- package/poetry.lock +3210 -3216
- package/pyproject.toml +64 -64
- package/releaserc.json +17 -17
- package/requirements.test.txt +3 -3
- package/setup.cfg +64 -64
- package/setup.py +10 -10
- package/tests/bandit.yaml +17 -17
- package/tests/conftest.py +19 -19
- package/tests/test_init.py +9 -9
|
@@ -1,192 +1,199 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
from homeassistant.
|
|
14
|
-
from homeassistant.
|
|
15
|
-
from homeassistant.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
from
|
|
30
|
-
from meteocatpy.
|
|
31
|
-
from meteocatpy.
|
|
32
|
-
from meteocatpy.
|
|
33
|
-
from meteocatpy.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
self.
|
|
45
|
-
self.
|
|
46
|
-
self.
|
|
47
|
-
self.
|
|
48
|
-
self.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
assets_dir
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
errors
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import voluptuous as vol
|
|
10
|
+
from aiohttp import ClientError
|
|
11
|
+
import aiofiles
|
|
12
|
+
|
|
13
|
+
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
|
|
14
|
+
from homeassistant.core import callback
|
|
15
|
+
from homeassistant.exceptions import HomeAssistantError
|
|
16
|
+
from homeassistant.helpers import aiohttp_client, config_validation as cv
|
|
17
|
+
|
|
18
|
+
from .const import (
|
|
19
|
+
DOMAIN,
|
|
20
|
+
CONF_API_KEY,
|
|
21
|
+
TOWN_NAME,
|
|
22
|
+
TOWN_ID,
|
|
23
|
+
VARIABLE_NAME,
|
|
24
|
+
VARIABLE_ID,
|
|
25
|
+
STATION_NAME,
|
|
26
|
+
STATION_ID
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from .options_flow import MeteocatOptionsFlowHandler
|
|
30
|
+
from meteocatpy.town import MeteocatTown
|
|
31
|
+
from meteocatpy.symbols import MeteocatSymbols
|
|
32
|
+
from meteocatpy.variables import MeteocatVariables
|
|
33
|
+
from meteocatpy.townstations import MeteocatTownStations
|
|
34
|
+
from meteocatpy.exceptions import BadRequestError, ForbiddenError, TooManyRequestsError, InternalServerError, UnknownAPIError
|
|
35
|
+
|
|
36
|
+
_LOGGER = logging.getLogger(__name__)
|
|
37
|
+
|
|
38
|
+
class MeteocatConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
39
|
+
"""Flujo de configuración para Meteocat."""
|
|
40
|
+
|
|
41
|
+
VERSION = 1
|
|
42
|
+
|
|
43
|
+
def __init__(self):
|
|
44
|
+
self.api_key: str | None = None
|
|
45
|
+
self.municipis: list[dict[str, Any]] = []
|
|
46
|
+
self.selected_municipi: dict[str, Any] | None = None
|
|
47
|
+
self.variable_id: str | None = None
|
|
48
|
+
self.station_id: str | None = None
|
|
49
|
+
self.station_name: str | None = None
|
|
50
|
+
|
|
51
|
+
async def async_step_user(
|
|
52
|
+
self, user_input: dict[str, Any] | None = None
|
|
53
|
+
) -> ConfigFlowResult:
|
|
54
|
+
"""Primer paso: Solicitar la API Key."""
|
|
55
|
+
errors = {}
|
|
56
|
+
|
|
57
|
+
if user_input is not None:
|
|
58
|
+
self.api_key = user_input[CONF_API_KEY]
|
|
59
|
+
|
|
60
|
+
town_client = MeteocatTown(self.api_key)
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
self.municipis = await town_client.get_municipis()
|
|
64
|
+
except (BadRequestError, ForbiddenError, TooManyRequestsError, InternalServerError, UnknownAPIError) as ex:
|
|
65
|
+
_LOGGER.error("Error al conectar con la API de Meteocat: %s", ex)
|
|
66
|
+
errors["base"] = "cannot_connect"
|
|
67
|
+
except Exception as ex:
|
|
68
|
+
_LOGGER.error("Error inesperado al validar la API Key: %s", ex)
|
|
69
|
+
errors["base"] = "unknown"
|
|
70
|
+
|
|
71
|
+
if not errors:
|
|
72
|
+
return await self.async_step_select_municipi()
|
|
73
|
+
|
|
74
|
+
schema = vol.Schema({vol.Required(CONF_API_KEY): str})
|
|
75
|
+
return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
|
|
76
|
+
|
|
77
|
+
async def async_step_select_municipi(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
|
|
78
|
+
"""Segundo paso: Seleccionar el municipio."""
|
|
79
|
+
errors = {}
|
|
80
|
+
|
|
81
|
+
if user_input is not None:
|
|
82
|
+
selected_codi = user_input["municipi"]
|
|
83
|
+
self.selected_municipi = next(
|
|
84
|
+
(m for m in self.municipis if m["codi"] == selected_codi), None
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if self.selected_municipi:
|
|
88
|
+
await self.fetch_symbols_and_variables()
|
|
89
|
+
|
|
90
|
+
if not errors and self.selected_municipi:
|
|
91
|
+
return await self.async_step_select_station()
|
|
92
|
+
|
|
93
|
+
schema = vol.Schema(
|
|
94
|
+
{vol.Required("municipi"): vol.In({m["codi"]: m["nom"] for m in self.municipis})}
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return self.async_show_form(step_id="select_municipi", data_schema=schema, errors=errors)
|
|
98
|
+
|
|
99
|
+
async def fetch_symbols_and_variables(self):
|
|
100
|
+
"""Descarga los símbolos y las variables después de seleccionar el municipio."""
|
|
101
|
+
|
|
102
|
+
errors = {}
|
|
103
|
+
|
|
104
|
+
# Descargar y guardar los símbolos
|
|
105
|
+
assets_dir = Path(__file__).parent / "assets"
|
|
106
|
+
assets_dir.mkdir(parents=True, exist_ok=True)
|
|
107
|
+
symbols_file = assets_dir / "symbols.json"
|
|
108
|
+
symbols_client = MeteocatSymbols(self.api_key)
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
symbols_data = await symbols_client.fetch_symbols()
|
|
112
|
+
async with aiofiles.open(symbols_file, "w", encoding="utf-8") as file:
|
|
113
|
+
await file.write(json.dumps({"symbols": symbols_data}, ensure_ascii=False, indent=4))
|
|
114
|
+
except (BadRequestError, ForbiddenError, TooManyRequestsError, InternalServerError, UnknownAPIError) as ex:
|
|
115
|
+
_LOGGER.error("Error al descargar o guardar los símbolos: %s", ex)
|
|
116
|
+
errors["base"] = "symbols_download_failed"
|
|
117
|
+
|
|
118
|
+
if not errors:
|
|
119
|
+
# Configurar la ruta para la caché en la carpeta `custom_components/meteocat`
|
|
120
|
+
cache_dir = os.path.join(os.path.dirname(__file__), ".meteocat_cache")
|
|
121
|
+
|
|
122
|
+
# Crear la carpeta de caché si no existe
|
|
123
|
+
os.makedirs(cache_dir, exist_ok=True)
|
|
124
|
+
|
|
125
|
+
variables_client = MeteocatVariables(self.api_key, cache_dir=cache_dir)
|
|
126
|
+
try:
|
|
127
|
+
variables_data = await variables_client.get_variables()
|
|
128
|
+
self.variable_id = next(
|
|
129
|
+
(v["codi"] for v in variables_data if v["nom"].lower() == "temperatura"), None
|
|
130
|
+
)
|
|
131
|
+
if not self.variable_id:
|
|
132
|
+
_LOGGER.error("No se encontró la variable 'Temperatura'")
|
|
133
|
+
errors["base"] = "variable_not_found"
|
|
134
|
+
except (BadRequestError, ForbiddenError, TooManyRequestsError, InternalServerError, UnknownAPIError) as ex:
|
|
135
|
+
_LOGGER.error("Error al obtener las variables: %s", ex)
|
|
136
|
+
errors["base"] = "variables_fetch_failed"
|
|
137
|
+
|
|
138
|
+
if errors:
|
|
139
|
+
raise HomeAssistantError(errors)
|
|
140
|
+
|
|
141
|
+
async def async_step_select_station(
|
|
142
|
+
self, user_input: dict[str, Any] | None = None
|
|
143
|
+
) -> ConfigFlowResult:
|
|
144
|
+
"""Tercer paso: Seleccionar la estación para la variable seleccionada."""
|
|
145
|
+
errors = {}
|
|
146
|
+
|
|
147
|
+
townstations_client = MeteocatTownStations(self.api_key)
|
|
148
|
+
try:
|
|
149
|
+
stations_data = await townstations_client.get_town_stations(
|
|
150
|
+
self.selected_municipi["codi"], self.variable_id
|
|
151
|
+
)
|
|
152
|
+
except Exception as ex:
|
|
153
|
+
_LOGGER.error("Error al obtener las estaciones: %s", ex)
|
|
154
|
+
errors["base"] = "stations_fetch_failed"
|
|
155
|
+
stations_data = []
|
|
156
|
+
|
|
157
|
+
if user_input is not None:
|
|
158
|
+
selected_station_codi = user_input["station"]
|
|
159
|
+
selected_station = next(
|
|
160
|
+
(station for station in stations_data[0]["variables"][0]["estacions"] if station["codi"] == selected_station_codi),
|
|
161
|
+
None
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
if selected_station:
|
|
165
|
+
self.station_id = selected_station["codi"]
|
|
166
|
+
self.station_name = selected_station["nom"]
|
|
167
|
+
|
|
168
|
+
return self.async_create_entry(
|
|
169
|
+
title=self.selected_municipi["nom"],
|
|
170
|
+
data={
|
|
171
|
+
CONF_API_KEY: self.api_key,
|
|
172
|
+
TOWN_NAME: self.selected_municipi["nom"],
|
|
173
|
+
TOWN_ID: self.selected_municipi["codi"],
|
|
174
|
+
VARIABLE_NAME: "Temperatura",
|
|
175
|
+
VARIABLE_ID: str(self.variable_id),
|
|
176
|
+
STATION_NAME: self.station_name,
|
|
177
|
+
STATION_ID: self.station_id
|
|
178
|
+
},
|
|
179
|
+
)
|
|
180
|
+
else:
|
|
181
|
+
errors["base"] = "station_not_found"
|
|
182
|
+
|
|
183
|
+
schema = vol.Schema(
|
|
184
|
+
{
|
|
185
|
+
vol.Required("station"): vol.In(
|
|
186
|
+
{station["codi"]: station["nom"] for station in stations_data[0]["variables"][0]["estacions"]}
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
return self.async_show_form(
|
|
192
|
+
step_id="select_station", data_schema=schema, errors=errors
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
@callback
|
|
197
|
+
def async_get_options_flow(config_entry: ConfigEntry) -> MeteocatOptionsFlowHandler:
|
|
198
|
+
"""Devuelve el flujo de opciones para esta configuración."""
|
|
199
|
+
return MeteocatOptionsFlowHandler(config_entry)
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
# Constantes generales
|
|
2
|
-
DOMAIN = "meteocat"
|
|
3
|
-
BASE_URL = "https://api.meteo.cat"
|
|
4
|
-
CONF_API_KEY = "api_key"
|
|
5
|
-
TOWN_NAME = "town_name"
|
|
6
|
-
TOWN_ID = "town_id"
|
|
7
|
-
VARIABLE_NAME = "variable_name"
|
|
8
|
-
VARIABLE_ID = "variable_id"
|
|
9
|
-
STATION_NAME = "station_name"
|
|
10
|
-
STATION_ID = "station_id"
|
|
11
|
-
|
|
12
|
-
# Códigos de sensores de la API
|
|
13
|
-
WIND_SPEED = "wind_speed" # Velocidad del viento
|
|
14
|
-
WIND_DIRECTION = "wind_direction" # Dirección del viento
|
|
15
|
-
TEMPERATURE = "temperature" # Temperatura
|
|
16
|
-
HUMIDITY = "humidity" # Humedad relativa
|
|
17
|
-
PRESSURE = "pressure" # Presión atmosférica
|
|
18
|
-
PRECIPITATION = "precipitation" # Precipitación
|
|
19
|
-
SOLAR_GLOBAL_IRRADIANCE = "solar_global_irradiance" # Irradiación solar global
|
|
20
|
-
UV_INDEX = "uv_index" # UV
|
|
21
|
-
MAX_TEMPERATURE = "max_temperature" # Temperatura máxima
|
|
22
|
-
MIN_TEMPERATURE = "min_temperature" # Temperatura mínima
|
|
23
|
-
WIND_GUST = "wind_gust" # Racha de viento
|
|
24
|
-
STATION_TIMESTAMP = "station_timestamp" # Código de tiempo de la estación
|
|
25
|
-
|
|
26
|
-
# Definición de códigos para variables
|
|
27
|
-
WIND_SPEED_CODE = 30
|
|
28
|
-
WIND_DIRECTION_CODE = 31
|
|
29
|
-
TEMPERATURE_CODE = 32
|
|
30
|
-
HUMIDITY_CODE = 33
|
|
31
|
-
PRESSURE_CODE = 34
|
|
32
|
-
PRECIPITATION_CODE = 35
|
|
33
|
-
SOLAR_GLOBAL_IRRADIANCE_CODE = 36
|
|
34
|
-
UV_INDEX_CODE = 39
|
|
35
|
-
MAX_TEMPERATURE_CODE = 40
|
|
36
|
-
MIN_TEMPERATURE_CODE = 42
|
|
37
|
-
WIND_GUST_CODE = 50
|
|
38
|
-
|
|
39
|
-
# Mapeo de códigos 'estatCel' a condiciones de Home Assistant
|
|
40
|
-
CONDITION_MAPPING = {
|
|
41
|
-
"sunny": [1],
|
|
42
|
-
"clear-night": [1],
|
|
43
|
-
"partlycloudy": [2, 3],
|
|
44
|
-
"cloudy": [4, 20, 21, 22],
|
|
45
|
-
"rainy": [5, 6, 23],
|
|
46
|
-
"pouring": [7, 8, 25],
|
|
47
|
-
"lightning-rainy": [8, 24],
|
|
48
|
-
"hail": [9],
|
|
49
|
-
"snowy": [10, 26, 27, 28],
|
|
50
|
-
"fog": [11, 12],
|
|
51
|
-
"snow-rainy": [27, 29, 30],
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
# Platforms
|
|
55
|
-
PLATFORMS = ["sensor"]
|
|
1
|
+
# Constantes generales
|
|
2
|
+
DOMAIN = "meteocat"
|
|
3
|
+
BASE_URL = "https://api.meteo.cat"
|
|
4
|
+
CONF_API_KEY = "api_key"
|
|
5
|
+
TOWN_NAME = "town_name"
|
|
6
|
+
TOWN_ID = "town_id"
|
|
7
|
+
VARIABLE_NAME = "variable_name"
|
|
8
|
+
VARIABLE_ID = "variable_id"
|
|
9
|
+
STATION_NAME = "station_name"
|
|
10
|
+
STATION_ID = "station_id"
|
|
11
|
+
|
|
12
|
+
# Códigos de sensores de la API
|
|
13
|
+
WIND_SPEED = "wind_speed" # Velocidad del viento
|
|
14
|
+
WIND_DIRECTION = "wind_direction" # Dirección del viento
|
|
15
|
+
TEMPERATURE = "temperature" # Temperatura
|
|
16
|
+
HUMIDITY = "humidity" # Humedad relativa
|
|
17
|
+
PRESSURE = "pressure" # Presión atmosférica
|
|
18
|
+
PRECIPITATION = "precipitation" # Precipitación
|
|
19
|
+
SOLAR_GLOBAL_IRRADIANCE = "solar_global_irradiance" # Irradiación solar global
|
|
20
|
+
UV_INDEX = "uv_index" # UV
|
|
21
|
+
MAX_TEMPERATURE = "max_temperature" # Temperatura máxima
|
|
22
|
+
MIN_TEMPERATURE = "min_temperature" # Temperatura mínima
|
|
23
|
+
WIND_GUST = "wind_gust" # Racha de viento
|
|
24
|
+
STATION_TIMESTAMP = "station_timestamp" # Código de tiempo de la estación
|
|
25
|
+
|
|
26
|
+
# Definición de códigos para variables
|
|
27
|
+
WIND_SPEED_CODE = 30
|
|
28
|
+
WIND_DIRECTION_CODE = 31
|
|
29
|
+
TEMPERATURE_CODE = 32
|
|
30
|
+
HUMIDITY_CODE = 33
|
|
31
|
+
PRESSURE_CODE = 34
|
|
32
|
+
PRECIPITATION_CODE = 35
|
|
33
|
+
SOLAR_GLOBAL_IRRADIANCE_CODE = 36
|
|
34
|
+
UV_INDEX_CODE = 39
|
|
35
|
+
MAX_TEMPERATURE_CODE = 40
|
|
36
|
+
MIN_TEMPERATURE_CODE = 42
|
|
37
|
+
WIND_GUST_CODE = 50
|
|
38
|
+
|
|
39
|
+
# Mapeo de códigos 'estatCel' a condiciones de Home Assistant
|
|
40
|
+
CONDITION_MAPPING = {
|
|
41
|
+
"sunny": [1],
|
|
42
|
+
"clear-night": [1],
|
|
43
|
+
"partlycloudy": [2, 3],
|
|
44
|
+
"cloudy": [4, 20, 21, 22],
|
|
45
|
+
"rainy": [5, 6, 23],
|
|
46
|
+
"pouring": [7, 8, 25],
|
|
47
|
+
"lightning-rainy": [8, 24],
|
|
48
|
+
"hail": [9],
|
|
49
|
+
"snowy": [10, 26, 27, 28],
|
|
50
|
+
"fog": [11, 12],
|
|
51
|
+
"snow-rainy": [27, 29, 30],
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Platforms
|
|
55
|
+
PLATFORMS = ["sensor"]
|