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 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 #, MeteocatEntityCoordinator
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.27"
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", DOMAIN)
93
- assets_folder = os.path.join(custom_components_path, "assets")
94
- files_folder = os.path.join(custom_components_path, "files")
95
- symbols_file = os.path.join(assets_folder, "symbols.json")
96
- station_data_file = os.path.join(files_folder, "station_data.json")
97
-
98
- # Eliminar el archivo symbols.json si existe
99
- try:
100
- if os.path.exists(symbols_file):
101
- os.remove(symbols_file)
102
- _LOGGER.info("Archivo symbols.json eliminado correctamente.")
103
- else:
104
- _LOGGER.info("El archivo symbols.json no se encontró.")
105
- except OSError as e:
106
- _LOGGER.error(f"Error al intentar eliminar el archivo symbols.json: {e}")
107
-
108
- # Eliminar el archivo station_data.json si existe
109
- try:
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 las variables después de seleccionar el municipio."""
101
+ """Descarga y guarda los símbolos y variables después de seleccionar el municipio."""
101
102
 
102
103
  errors = {}
103
104
 
104
- # Descargar y guardar los símbolos
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
- symbols_client = MeteocatSymbols(self.api_key)
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
- 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"
118
+
119
+ _LOGGER.info(f"Símbolos guardados en {symbols_file}")
117
120
 
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
+ # Descargar y guardar las variables
122
+ variables_client = MeteocatVariables(self.api_key)
123
+ variables_data = await variables_client.get_variables()
121
124
 
122
- # Crear la carpeta de caché si no existe
123
- os.makedirs(cache_dir, exist_ok=True)
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
- 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"
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", "packaging>=20.3", "wrapt>=1.14.0"],
11
- "version": "0.1.27"
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.27"
2
+ __version__ = "0.1.29"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meteocat",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "description": "[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\r [![Python version compatibility](https://img.shields.io/pypi/pyversions/meteocat)](https://pypi.org/project/meteocat)\r [![pipeline status](https://gitlab.com/figorr/meteocat/badges/master/pipeline.svg)](https://gitlab.com/figorr/meteocat/commits/master)",
5
5
  "main": "index.js",
6
6
  "directories": {
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "meteocat"
3
- version = "0.1.27"
3
+ version = "0.1.29"
4
4
  description = "Script para obtener datos meteorológicos de la API de Meteocat"
5
5
  authors = ["figorr <jdcuartero@yahoo.es>"]
6
6
  license = "Apache-2.0"