meteocat 0.1.27 → 0.1.29
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/CHANGELOG.md +23 -0
- package/custom_components/meteocat/__init__.py +34 -53
- package/custom_components/meteocat/config_flow.py +38 -23
- package/custom_components/meteocat/manifest.json +2 -2
- package/custom_components/meteocat/sensor.py +1 -1
- package/custom_components/meteocat/version.py +1 -1
- package/package.json +1 -1
- package/pyproject.toml +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
## [0.1.29](https://github.com/figorr/meteocat/compare/v0.1.28...v0.1.29) (2024-12-12)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* 0.1.29 ([f80cb9e](https://github.com/figorr/meteocat/commit/f80cb9eda9bd7d731489b51c161bc16a00ac57f0))
|
|
7
|
+
* bump meteocatpy to 0.0.14 ([74cc591](https://github.com/figorr/meteocat/commit/74cc591ac7e9911c77649a20088af15d3af2d350))
|
|
8
|
+
* remove cache ([8c4f29b](https://github.com/figorr/meteocat/commit/8c4f29b0b2c2cfa33f2b45f72bed57bf45b9a3dd))
|
|
9
|
+
* remove cache tests ([6082096](https://github.com/figorr/meteocat/commit/6082096a92ade5a033e0493a819b20e950aff7a3))
|
|
10
|
+
|
|
11
|
+
## [0.1.28](https://github.com/figorr/meteocat/compare/v0.1.27...v0.1.28) (2024-12-11)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* 0.1.28 ([2c329cb](https://github.com/figorr/meteocat/commit/2c329cb960aa7bd6ec6bec0c6145db791349758f))
|
|
17
|
+
* bump meteocatpy to 0.0.13 ([52c11c6](https://github.com/figorr/meteocat/commit/52c11c66f66b0ebff40fea58a8a5c4d4a74482be))
|
|
18
|
+
* fix entity_id ([fa61841](https://github.com/figorr/meteocat/commit/fa61841ba3857a79d6b97d0e0ed12a543525c205))
|
|
19
|
+
* fix name sensors to include station_id ([7b868fd](https://github.com/figorr/meteocat/commit/7b868fd0eea66f8ec2655e92137d9ab6dd9d0149))
|
|
20
|
+
* ignore test_call_api ([976826a](https://github.com/figorr/meteocat/commit/976826a978889fcec304a8b3620ccf738e1a1946))
|
|
21
|
+
* save variables.json in assets folder ([d2c2234](https://github.com/figorr/meteocat/commit/d2c2234621aaabbbdc65367429b56b304855f710))
|
|
22
|
+
* setup cache folder and new async_remove_entry ([4dfddc0](https://github.com/figorr/meteocat/commit/4dfddc03a64f82582b39eff47870c561d0775d6d))
|
|
23
|
+
|
|
1
24
|
## [0.1.27](https://github.com/figorr/meteocat/compare/v0.1.26...v0.1.27) (2024-12-10)
|
|
2
25
|
|
|
3
26
|
|
|
@@ -1,27 +1,37 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
3
|
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
5
|
from homeassistant import core
|
|
6
6
|
from homeassistant.config_entries import ConfigEntry
|
|
7
7
|
from homeassistant.core import HomeAssistant
|
|
8
8
|
from homeassistant.exceptions import HomeAssistantError
|
|
9
9
|
from homeassistant.helpers.entity_platform import async_get_platforms
|
|
10
10
|
|
|
11
|
-
from .coordinator import MeteocatSensorCoordinator
|
|
11
|
+
from .coordinator import MeteocatSensorCoordinator # , MeteocatEntityCoordinator
|
|
12
12
|
from .const import DOMAIN, PLATFORMS
|
|
13
13
|
|
|
14
14
|
_LOGGER = logging.getLogger(__name__)
|
|
15
15
|
|
|
16
16
|
# Versión
|
|
17
|
-
__version__ = "0.1.
|
|
17
|
+
__version__ = "0.1.29"
|
|
18
18
|
|
|
19
|
+
def safe_remove(path: Path, is_folder: bool = False):
|
|
20
|
+
"""Elimina de forma segura un archivo o carpeta si existe."""
|
|
21
|
+
try:
|
|
22
|
+
if is_folder and path.exists() and not any(path.iterdir()):
|
|
23
|
+
path.rmdir()
|
|
24
|
+
_LOGGER.info(f"Carpeta {path.name} eliminada correctamente.")
|
|
25
|
+
elif not is_folder and path.exists():
|
|
26
|
+
path.unlink()
|
|
27
|
+
_LOGGER.info(f"Archivo {path.name} eliminado correctamente.")
|
|
28
|
+
except OSError as e:
|
|
29
|
+
_LOGGER.error(f"Error al intentar eliminar {path.name}: {e}")
|
|
19
30
|
|
|
20
31
|
async def async_setup(hass: core.HomeAssistant, config: dict) -> bool:
|
|
21
32
|
"""Configuración inicial del componente Meteocat."""
|
|
22
33
|
return True
|
|
23
34
|
|
|
24
|
-
|
|
25
35
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|
26
36
|
"""Configura una entrada de configuración para Meteocat."""
|
|
27
37
|
_LOGGER.info("Configurando la integración de Meteocat...")
|
|
@@ -48,9 +58,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|
|
48
58
|
# Inicializar coordinadores
|
|
49
59
|
try:
|
|
50
60
|
sensor_coordinator = MeteocatSensorCoordinator(hass=hass, entry_data=entry_data)
|
|
51
|
-
# entity_coordinator = MeteocatEntityCoordinator(hass=hass, entry_data=entry_data)
|
|
52
|
-
|
|
53
61
|
await sensor_coordinator.async_config_entry_first_refresh()
|
|
62
|
+
|
|
63
|
+
# entity_coordinator = MeteocatEntityCoordinator(hass=hass, entry_data=entry_data)
|
|
54
64
|
# await entity_coordinator.async_config_entry_first_refresh()
|
|
55
65
|
except Exception as err: # Capturar todos los errores
|
|
56
66
|
_LOGGER.exception(f"Error al inicializar los coordinadores: {err}")
|
|
@@ -70,7 +80,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|
|
70
80
|
|
|
71
81
|
return True
|
|
72
82
|
|
|
73
|
-
|
|
74
83
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|
75
84
|
"""Desactiva una entrada de configuración para Meteocat."""
|
|
76
85
|
platforms = async_get_platforms(hass, DOMAIN)
|
|
@@ -83,54 +92,26 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|
|
83
92
|
|
|
84
93
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
|
85
94
|
|
|
86
|
-
|
|
87
95
|
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
|
88
96
|
"""Limpia cualquier dato adicional al desinstalar la integración."""
|
|
89
97
|
_LOGGER.info(f"Eliminando datos residuales de la integración: {entry.entry_id}")
|
|
90
98
|
|
|
91
99
|
# Definir las rutas de los archivos y carpetas a eliminar
|
|
92
|
-
custom_components_path = hass.config.path("custom_components"
|
|
93
|
-
assets_folder =
|
|
94
|
-
files_folder =
|
|
95
|
-
symbols_file =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if os.path.exists(station_data_file):
|
|
111
|
-
os.remove(station_data_file)
|
|
112
|
-
_LOGGER.info("Archivo station_data.json eliminado correctamente.")
|
|
113
|
-
else:
|
|
114
|
-
_LOGGER.info("El archivo station_data.json no se encontró.")
|
|
115
|
-
except OSError as e:
|
|
116
|
-
_LOGGER.error(f"Error al intentar eliminar el archivo station_data.json: {e}")
|
|
117
|
-
|
|
118
|
-
# Eliminar la carpeta assets si está vacía
|
|
119
|
-
try:
|
|
120
|
-
if os.path.exists(assets_folder) and not os.listdir(assets_folder):
|
|
121
|
-
os.rmdir(assets_folder)
|
|
122
|
-
_LOGGER.info("Carpeta assets eliminada correctamente.")
|
|
123
|
-
elif os.path.exists(assets_folder):
|
|
124
|
-
_LOGGER.warning("La carpeta assets no está vacía y no se pudo eliminar.")
|
|
125
|
-
except OSError as e:
|
|
126
|
-
_LOGGER.error(f"Error al intentar eliminar la carpeta assets: {e}")
|
|
127
|
-
|
|
128
|
-
# Eliminar la carpeta files si está vacía
|
|
129
|
-
try:
|
|
130
|
-
if os.path.exists(files_folder) and not os.listdir(files_folder):
|
|
131
|
-
os.rmdir(files_folder)
|
|
132
|
-
_LOGGER.info("Carpeta files eliminada correctamente.")
|
|
133
|
-
elif os.path.exists(files_folder):
|
|
134
|
-
_LOGGER.warning("La carpeta files no está vacía y no se pudo eliminar.")
|
|
135
|
-
except OSError as e:
|
|
136
|
-
_LOGGER.error(f"Error al intentar eliminar la carpeta files: {e}")
|
|
100
|
+
custom_components_path = Path(hass.config.path("custom_components")) / DOMAIN
|
|
101
|
+
assets_folder = custom_components_path / "assets"
|
|
102
|
+
files_folder = custom_components_path / "files"
|
|
103
|
+
symbols_file = assets_folder / "symbols.json"
|
|
104
|
+
variables_file = assets_folder / "variables.json"
|
|
105
|
+
station_data_file = files_folder / "station_data.json"
|
|
106
|
+
|
|
107
|
+
# Validar la ruta base
|
|
108
|
+
if not custom_components_path.exists():
|
|
109
|
+
_LOGGER.warning(f"La ruta {custom_components_path} no existe. No se realizará la limpieza.")
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
# Eliminar archivos y carpetas
|
|
113
|
+
safe_remove(symbols_file)
|
|
114
|
+
safe_remove(variables_file)
|
|
115
|
+
safe_remove(station_data_file)
|
|
116
|
+
safe_remove(assets_folder, is_folder=True)
|
|
117
|
+
safe_remove(files_folder, is_folder=True)
|
|
@@ -47,6 +47,7 @@ class MeteocatConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
|
47
47
|
self.variable_id: str | None = None
|
|
48
48
|
self.station_id: str | None = None
|
|
49
49
|
self.station_name: str | None = None
|
|
50
|
+
self._cache = {}
|
|
50
51
|
|
|
51
52
|
async def async_step_user(
|
|
52
53
|
self, user_input: dict[str, Any] | None = None
|
|
@@ -97,43 +98,57 @@ class MeteocatConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
|
97
98
|
return self.async_show_form(step_id="select_municipi", data_schema=schema, errors=errors)
|
|
98
99
|
|
|
99
100
|
async def fetch_symbols_and_variables(self):
|
|
100
|
-
"""Descarga los símbolos y
|
|
101
|
+
"""Descarga y guarda los símbolos y variables después de seleccionar el municipio."""
|
|
101
102
|
|
|
102
103
|
errors = {}
|
|
103
104
|
|
|
104
|
-
#
|
|
105
|
+
# Crear directorio de activos (assets) si no existe
|
|
105
106
|
assets_dir = Path(__file__).parent / "assets"
|
|
106
107
|
assets_dir.mkdir(parents=True, exist_ok=True)
|
|
107
108
|
symbols_file = assets_dir / "symbols.json"
|
|
108
|
-
|
|
109
|
+
variables_file = assets_dir / "variables.json"
|
|
109
110
|
|
|
110
111
|
try:
|
|
112
|
+
# Descargar y guardar los símbolos
|
|
113
|
+
symbols_client = MeteocatSymbols(self.api_key)
|
|
111
114
|
symbols_data = await symbols_client.fetch_symbols()
|
|
115
|
+
|
|
112
116
|
async with aiofiles.open(symbols_file, "w", encoding="utf-8") as file:
|
|
113
117
|
await file.write(json.dumps({"symbols": symbols_data}, ensure_ascii=False, indent=4))
|
|
114
|
-
|
|
115
|
-
_LOGGER.
|
|
116
|
-
errors["base"] = "symbols_download_failed"
|
|
118
|
+
|
|
119
|
+
_LOGGER.info(f"Símbolos guardados en {symbols_file}")
|
|
117
120
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
# Descargar y guardar las variables
|
|
122
|
+
variables_client = MeteocatVariables(self.api_key)
|
|
123
|
+
variables_data = await variables_client.get_variables()
|
|
121
124
|
|
|
122
|
-
|
|
123
|
-
|
|
125
|
+
async with aiofiles.open(variables_file, "w", encoding="utf-8") as file:
|
|
126
|
+
await file.write(json.dumps({"variables": variables_data}, ensure_ascii=False, indent=4))
|
|
124
127
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
128
|
+
# Actualizar la caché
|
|
129
|
+
cache_data = {
|
|
130
|
+
"symbols": symbols_data,
|
|
131
|
+
"variables": variables_data
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async with aiofiles.open(variables_file, "w", encoding="utf-8") as file:
|
|
135
|
+
await file.write(json.dumps(cache_data, ensure_ascii=False, indent=4))
|
|
136
|
+
|
|
137
|
+
_LOGGER.info(f"Variables guardadas en {variables_file}")
|
|
138
|
+
|
|
139
|
+
# Buscar la variable de temperatura
|
|
140
|
+
self.variable_id = next(
|
|
141
|
+
(v["codi"] for v in variables_data if v["nom"].lower() == "temperatura"), None
|
|
142
|
+
)
|
|
143
|
+
if not self.variable_id:
|
|
144
|
+
_LOGGER.error("No se encontró la variable 'Temperatura'")
|
|
145
|
+
errors["base"] = "variable_not_found"
|
|
146
|
+
except (BadRequestError, ForbiddenError, TooManyRequestsError, InternalServerError, UnknownAPIError) as ex:
|
|
147
|
+
_LOGGER.error("Error al conectar con la API de Meteocat: %s", ex)
|
|
148
|
+
errors["base"] = "cannot_connect"
|
|
149
|
+
except Exception as ex:
|
|
150
|
+
_LOGGER.error("Error inesperado al descargar los datos: %s", ex)
|
|
151
|
+
errors["base"] = "unknown"
|
|
137
152
|
|
|
138
153
|
if errors:
|
|
139
154
|
raise HomeAssistantError(errors)
|
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
"iot_class": "cloud_polling",
|
|
8
8
|
"documentation": "https://gitlab.com/figorr/meteocat",
|
|
9
9
|
"loggers": ["meteocatpy"],
|
|
10
|
-
"requirements": ["meteocatpy==0.0.
|
|
11
|
-
"version": "0.1.
|
|
10
|
+
"requirements": ["meteocatpy==0.0.14", "packaging>=20.3", "wrapt>=1.14.0"],
|
|
11
|
+
"version": "0.1.29"
|
|
12
12
|
}
|
|
@@ -319,7 +319,7 @@ class MeteocatSensor(CoordinatorEntity[MeteocatSensorCoordinator], SensorEntity)
|
|
|
319
319
|
"""Return the device info."""
|
|
320
320
|
return DeviceInfo(
|
|
321
321
|
identifiers={(DOMAIN, self._town_id)},
|
|
322
|
-
name=self._town_name,
|
|
322
|
+
name="Meteocat " + self._station_id + " " + self._town_name,
|
|
323
323
|
manufacturer="Meteocat",
|
|
324
324
|
model="Meteocat API",
|
|
325
325
|
)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# version.py
|
|
2
|
-
__version__ = "0.1.
|
|
2
|
+
__version__ = "0.1.29"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "meteocat",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.29",
|
|
4
4
|
"description": "[](https://opensource.org/licenses/Apache-2.0)\r [](https://pypi.org/project/meteocat)\r [](https://gitlab.com/figorr/meteocat/commits/master)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|