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
package/README.md
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
# Meteocat for Home Assistant
|
|
2
|
-
|
|
3
|
-
[](https://opensource.org/licenses/Apache-2.0)
|
|
4
|
-
[](https://pypi.org/project/meteocat)
|
|
5
|
-
[](https://gitlab.com/figorr/meteocat/commits/master)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
This is a project to obtain meteorological data from the Meteocat API.
|
|
9
|
-
|
|
10
|
-
**NOTE:** Meteocat API requires to use an API_KEY, you should ask to (https://apidocs.meteocat.gencat.cat/documentacio/acces-ciutada-i-administracio/)
|
|
11
|
-
|
|
12
|
-
# Credits
|
|
13
|
-
|
|
14
|
-
This is a personal project.
|
|
15
|
-
|
|
16
|
-
Authors:
|
|
17
|
-
- Figorr
|
|
18
|
-
|
|
19
|
-
# Contributing
|
|
20
|
-
|
|
21
|
-
1. [Check for open features/bugs](https://gitlab.com/figorr/meteocat/issues)
|
|
22
|
-
or [initiate a discussion on one](https://gitlab.com/figorr/meteocat/issues/new).
|
|
23
|
-
2. [Fork the repository](https://gitlab.com/figorr/meteocat/forks/new).
|
|
24
|
-
3. Install the dev environment: `make init`.
|
|
25
|
-
4. Enter the virtual environment: `pipenv shell`
|
|
26
|
-
5. Code your new feature or bug fix.
|
|
27
|
-
6. Write a test that covers your new functionality.
|
|
28
|
-
7. Update `README.md` with any new documentation.
|
|
29
|
-
8. Run tests and ensure 100% code coverage for your contribution: `make coverage`
|
|
30
|
-
9. Ensure you have no linting errors: `make lint`
|
|
31
|
-
10. Ensure you have typed your code correctly: `make typing`
|
|
32
|
-
11. Add yourself to `AUTHORS.md`.
|
|
33
|
-
12. Submit a pull request!
|
|
34
|
-
|
|
35
|
-
# License
|
|
36
|
-
|
|
37
|
-
[Apache-2.0](LICENSE). By providing a contribution, you agree the contribution is licensed under Apache-2.0.
|
|
38
|
-
|
|
39
|
-
# API Reference
|
|
40
|
-
|
|
1
|
+
# Meteocat for Home Assistant
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
4
|
+
[](https://pypi.org/project/meteocat)
|
|
5
|
+
[](https://gitlab.com/figorr/meteocat/commits/master)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
This is a project to obtain meteorological data from the Meteocat API.
|
|
9
|
+
|
|
10
|
+
**NOTE:** Meteocat API requires to use an API_KEY, you should ask to (https://apidocs.meteocat.gencat.cat/documentacio/acces-ciutada-i-administracio/)
|
|
11
|
+
|
|
12
|
+
# Credits
|
|
13
|
+
|
|
14
|
+
This is a personal project.
|
|
15
|
+
|
|
16
|
+
Authors:
|
|
17
|
+
- Figorr
|
|
18
|
+
|
|
19
|
+
# Contributing
|
|
20
|
+
|
|
21
|
+
1. [Check for open features/bugs](https://gitlab.com/figorr/meteocat/issues)
|
|
22
|
+
or [initiate a discussion on one](https://gitlab.com/figorr/meteocat/issues/new).
|
|
23
|
+
2. [Fork the repository](https://gitlab.com/figorr/meteocat/forks/new).
|
|
24
|
+
3. Install the dev environment: `make init`.
|
|
25
|
+
4. Enter the virtual environment: `pipenv shell`
|
|
26
|
+
5. Code your new feature or bug fix.
|
|
27
|
+
6. Write a test that covers your new functionality.
|
|
28
|
+
7. Update `README.md` with any new documentation.
|
|
29
|
+
8. Run tests and ensure 100% code coverage for your contribution: `make coverage`
|
|
30
|
+
9. Ensure you have no linting errors: `make lint`
|
|
31
|
+
10. Ensure you have typed your code correctly: `make typing`
|
|
32
|
+
11. Add yourself to `AUTHORS.md`.
|
|
33
|
+
12. Submit a pull request!
|
|
34
|
+
|
|
35
|
+
# License
|
|
36
|
+
|
|
37
|
+
[Apache-2.0](LICENSE). By providing a contribution, you agree the contribution is licensed under Apache-2.0.
|
|
38
|
+
|
|
39
|
+
# API Reference
|
|
40
|
+
|
|
41
41
|
[See the docs 📚](https://apidocs.meteocat.gencat.cat/section/informacio-general/).
|
|
@@ -1,107 +1,136 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import logging
|
|
5
|
-
from homeassistant import core
|
|
6
|
-
from homeassistant.config_entries import ConfigEntry
|
|
7
|
-
from homeassistant.core import HomeAssistant
|
|
8
|
-
from homeassistant.exceptions import HomeAssistantError
|
|
9
|
-
from .
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
f"(ID: {entry_data['
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
#
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
#
|
|
68
|
-
|
|
69
|
-
hass.
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import logging
|
|
5
|
+
from homeassistant import core
|
|
6
|
+
from homeassistant.config_entries import ConfigEntry
|
|
7
|
+
from homeassistant.core import HomeAssistant
|
|
8
|
+
from homeassistant.exceptions import HomeAssistantError
|
|
9
|
+
from homeassistant.helpers.entity_platform import async_get_platforms
|
|
10
|
+
|
|
11
|
+
from .coordinator import MeteocatSensorCoordinator #, MeteocatEntityCoordinator
|
|
12
|
+
from .const import DOMAIN, PLATFORMS
|
|
13
|
+
|
|
14
|
+
_LOGGER = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
# Versión
|
|
17
|
+
__version__ = "0.1.27"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def async_setup(hass: core.HomeAssistant, config: dict) -> bool:
|
|
21
|
+
"""Configuración inicial del componente Meteocat."""
|
|
22
|
+
return True
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|
26
|
+
"""Configura una entrada de configuración para Meteocat."""
|
|
27
|
+
_LOGGER.info("Configurando la integración de Meteocat...")
|
|
28
|
+
|
|
29
|
+
# Extraer los datos necesarios de la entrada de configuración
|
|
30
|
+
entry_data = entry.data
|
|
31
|
+
|
|
32
|
+
# Validar campos requeridos
|
|
33
|
+
required_fields = [
|
|
34
|
+
"api_key", "town_name", "town_id", "variable_name",
|
|
35
|
+
"variable_id", "station_name", "station_id"
|
|
36
|
+
]
|
|
37
|
+
missing_fields = [field for field in required_fields if field not in entry_data]
|
|
38
|
+
if missing_fields:
|
|
39
|
+
_LOGGER.error(f"Faltan los siguientes campos en la configuración: {missing_fields}")
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
_LOGGER.debug(
|
|
43
|
+
f"Datos de configuración: Municipio '{entry_data['town_name']}' (ID: {entry_data['town_id']}), "
|
|
44
|
+
f"Variable '{entry_data['variable_name']}' (ID: {entry_data['variable_id']}), "
|
|
45
|
+
f"Estación '{entry_data['station_name']}' (ID: {entry_data['station_id']})."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Inicializar coordinadores
|
|
49
|
+
try:
|
|
50
|
+
sensor_coordinator = MeteocatSensorCoordinator(hass=hass, entry_data=entry_data)
|
|
51
|
+
# entity_coordinator = MeteocatEntityCoordinator(hass=hass, entry_data=entry_data)
|
|
52
|
+
|
|
53
|
+
await sensor_coordinator.async_config_entry_first_refresh()
|
|
54
|
+
# await entity_coordinator.async_config_entry_first_refresh()
|
|
55
|
+
except Exception as err: # Capturar todos los errores
|
|
56
|
+
_LOGGER.exception(f"Error al inicializar los coordinadores: {err}")
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
# Guardar coordinadores y datos en hass.data
|
|
60
|
+
hass.data.setdefault(DOMAIN, {})
|
|
61
|
+
hass.data[DOMAIN][entry.entry_id] = {
|
|
62
|
+
"sensor_coordinator": sensor_coordinator,
|
|
63
|
+
# "entity_coordinator": entity_coordinator,
|
|
64
|
+
**entry_data,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# Configurar plataformas
|
|
68
|
+
_LOGGER.debug(f"Cargando plataformas: {PLATFORMS}")
|
|
69
|
+
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
|
70
|
+
|
|
71
|
+
return True
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|
75
|
+
"""Desactiva una entrada de configuración para Meteocat."""
|
|
76
|
+
platforms = async_get_platforms(hass, DOMAIN)
|
|
77
|
+
_LOGGER.info(f"Descargando plataformas: {[p.domain for p in platforms]}")
|
|
78
|
+
|
|
79
|
+
if entry.entry_id in hass.data.get(DOMAIN, {}):
|
|
80
|
+
hass.data[DOMAIN].pop(entry.entry_id, None)
|
|
81
|
+
if not hass.data[DOMAIN]:
|
|
82
|
+
hass.data.pop(DOMAIN)
|
|
83
|
+
|
|
84
|
+
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
|
88
|
+
"""Limpia cualquier dato adicional al desinstalar la integración."""
|
|
89
|
+
_LOGGER.info(f"Eliminando datos residuales de la integración: {entry.entry_id}")
|
|
90
|
+
|
|
91
|
+
# 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}")
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
from .const import CONDITION_MAPPING
|
|
5
|
-
from .helpers import is_night # Importar la función is_night de helpers.py
|
|
6
|
-
|
|
7
|
-
def get_condition_from_statcel(codi_estatcel, current_time: datetime, hass) -> dict:
|
|
8
|
-
"""
|
|
9
|
-
Convierte el código 'estatCel' en condición de Home Assistant.
|
|
10
|
-
|
|
11
|
-
:param codi_estatcel: Código del estado del cielo (celestial state code).
|
|
12
|
-
:param current_time: Fecha y hora actual (datetime).
|
|
13
|
-
:param hass: Instancia de Home Assistant.
|
|
14
|
-
:return: Diccionario con la condición y el icono.
|
|
15
|
-
"""
|
|
16
|
-
# Determinar si es de noche usando la lógica centralizada en helpers.py
|
|
17
|
-
is_night_flag = is_night(current_time, hass)
|
|
18
|
-
|
|
19
|
-
# Identificar la condición basada en el código
|
|
20
|
-
for condition, codes in CONDITION_MAPPING.items():
|
|
21
|
-
if codi_estatcel in codes:
|
|
22
|
-
# Ajustar para condiciones nocturnas si aplica
|
|
23
|
-
if condition == "sunny" and is_night_flag:
|
|
24
|
-
return {"condition": "clear-night", "icon": None}
|
|
25
|
-
return {"condition": condition, "icon": None}
|
|
26
|
-
|
|
27
|
-
# Si no coincide ningún código, devolver condición desconocida
|
|
28
|
-
return {"condition": "unknown", "icon": None}
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from .const import CONDITION_MAPPING
|
|
5
|
+
from .helpers import is_night # Importar la función is_night de helpers.py
|
|
6
|
+
|
|
7
|
+
def get_condition_from_statcel(codi_estatcel, current_time: datetime, hass) -> dict:
|
|
8
|
+
"""
|
|
9
|
+
Convierte el código 'estatCel' en condición de Home Assistant.
|
|
10
|
+
|
|
11
|
+
:param codi_estatcel: Código del estado del cielo (celestial state code).
|
|
12
|
+
:param current_time: Fecha y hora actual (datetime).
|
|
13
|
+
:param hass: Instancia de Home Assistant.
|
|
14
|
+
:return: Diccionario con la condición y el icono.
|
|
15
|
+
"""
|
|
16
|
+
# Determinar si es de noche usando la lógica centralizada en helpers.py
|
|
17
|
+
is_night_flag = is_night(current_time, hass)
|
|
18
|
+
|
|
19
|
+
# Identificar la condición basada en el código
|
|
20
|
+
for condition, codes in CONDITION_MAPPING.items():
|
|
21
|
+
if codi_estatcel in codes:
|
|
22
|
+
# Ajustar para condiciones nocturnas si aplica
|
|
23
|
+
if condition == "sunny" and is_night_flag:
|
|
24
|
+
return {"condition": "clear-night", "icon": None}
|
|
25
|
+
return {"condition": condition, "icon": None}
|
|
26
|
+
|
|
27
|
+
# Si no coincide ningún código, devolver condición desconocida
|
|
28
|
+
return {"condition": "unknown", "icon": None}
|