meteocat 0.1.25 → 0.1.26
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 +414 -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 +192 -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 +3216 -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,42 +1,42 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from datetime import datetime, time
|
|
4
|
-
from homeassistant.helpers import entity_registry
|
|
5
|
-
|
|
6
|
-
def get_sun_times(hass) -> tuple:
|
|
7
|
-
"""
|
|
8
|
-
Obtiene las horas de amanecer y atardecer desde la integración sun.
|
|
9
|
-
|
|
10
|
-
:param hass: Instancia de Home Assistant.
|
|
11
|
-
:return: Tupla con las horas de amanecer y atardecer (datetime).
|
|
12
|
-
"""
|
|
13
|
-
sun_entity = hass.states.get("sun.sun")
|
|
14
|
-
if sun_entity:
|
|
15
|
-
sunrise = sun_entity.attributes.get("next_rising")
|
|
16
|
-
sunset = sun_entity.attributes.get("next_setting")
|
|
17
|
-
if sunrise and sunset:
|
|
18
|
-
return (
|
|
19
|
-
hass.util.dt.as_local(hass.util.dt.parse_datetime(sunrise)),
|
|
20
|
-
hass.util.dt.as_local(hass.util.dt.parse_datetime(sunset)),
|
|
21
|
-
)
|
|
22
|
-
raise ValueError("No se pudo obtener las horas de amanecer y atardecer de sun.sun")
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def is_night(current_time: datetime, hass) -> bool:
|
|
26
|
-
"""
|
|
27
|
-
Determina si la hora actual está fuera del rango entre el amanecer y el atardecer.
|
|
28
|
-
|
|
29
|
-
:param current_time: Hora actual como objeto datetime.
|
|
30
|
-
:param hass: Instancia de Home Assistant.
|
|
31
|
-
:return: True si es de noche, False si es de día.
|
|
32
|
-
"""
|
|
33
|
-
# Obtener las horas de amanecer y atardecer de la integración sun
|
|
34
|
-
sunrise, sunset = get_sun_times(hass)
|
|
35
|
-
|
|
36
|
-
# Convertimos a objetos time para comparar solo las horas
|
|
37
|
-
current_time_only = current_time.time()
|
|
38
|
-
sunrise_time_only = sunrise.time()
|
|
39
|
-
sunset_time_only = sunset.time()
|
|
40
|
-
|
|
41
|
-
# Devuelve True si la hora está fuera del rango del día
|
|
42
|
-
return current_time_only < sunrise_time_only or current_time_only > sunset_time_only
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, time
|
|
4
|
+
from homeassistant.helpers import entity_registry
|
|
5
|
+
|
|
6
|
+
def get_sun_times(hass) -> tuple:
|
|
7
|
+
"""
|
|
8
|
+
Obtiene las horas de amanecer y atardecer desde la integración sun.
|
|
9
|
+
|
|
10
|
+
:param hass: Instancia de Home Assistant.
|
|
11
|
+
:return: Tupla con las horas de amanecer y atardecer (datetime).
|
|
12
|
+
"""
|
|
13
|
+
sun_entity = hass.states.get("sun.sun")
|
|
14
|
+
if sun_entity:
|
|
15
|
+
sunrise = sun_entity.attributes.get("next_rising")
|
|
16
|
+
sunset = sun_entity.attributes.get("next_setting")
|
|
17
|
+
if sunrise and sunset:
|
|
18
|
+
return (
|
|
19
|
+
hass.util.dt.as_local(hass.util.dt.parse_datetime(sunrise)),
|
|
20
|
+
hass.util.dt.as_local(hass.util.dt.parse_datetime(sunset)),
|
|
21
|
+
)
|
|
22
|
+
raise ValueError("No se pudo obtener las horas de amanecer y atardecer de sun.sun")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def is_night(current_time: datetime, hass) -> bool:
|
|
26
|
+
"""
|
|
27
|
+
Determina si la hora actual está fuera del rango entre el amanecer y el atardecer.
|
|
28
|
+
|
|
29
|
+
:param current_time: Hora actual como objeto datetime.
|
|
30
|
+
:param hass: Instancia de Home Assistant.
|
|
31
|
+
:return: True si es de noche, False si es de día.
|
|
32
|
+
"""
|
|
33
|
+
# Obtener las horas de amanecer y atardecer de la integración sun
|
|
34
|
+
sunrise, sunset = get_sun_times(hass)
|
|
35
|
+
|
|
36
|
+
# Convertimos a objetos time para comparar solo las horas
|
|
37
|
+
current_time_only = current_time.time()
|
|
38
|
+
sunrise_time_only = sunrise.time()
|
|
39
|
+
sunset_time_only = sunset.time()
|
|
40
|
+
|
|
41
|
+
# Devuelve True si la hora está fuera del rango del día
|
|
42
|
+
return current_time_only < sunrise_time_only or current_time_only > sunset_time_only
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
{
|
|
2
|
-
"domain": "meteocat",
|
|
3
|
-
"name": "Meteocat",
|
|
4
|
-
"codeowners": ["@figorr"],
|
|
5
|
-
"config_flow": true,
|
|
6
|
-
"dependencies": ["persistent_notification", "http"],
|
|
7
|
-
"iot_class": "cloud_polling",
|
|
8
|
-
"documentation": "https://gitlab.com/figorr/meteocat",
|
|
9
|
-
"loggers": ["meteocatpy"],
|
|
10
|
-
"requirements": ["meteocatpy==0.0.8", "packaging>=20.3", "wrapt>=1.14.0"],
|
|
11
|
-
"version": "0.1.
|
|
12
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"domain": "meteocat",
|
|
3
|
+
"name": "Meteocat",
|
|
4
|
+
"codeowners": ["@figorr"],
|
|
5
|
+
"config_flow": true,
|
|
6
|
+
"dependencies": ["persistent_notification", "http"],
|
|
7
|
+
"iot_class": "cloud_polling",
|
|
8
|
+
"documentation": "https://gitlab.com/figorr/meteocat",
|
|
9
|
+
"loggers": ["meteocatpy"],
|
|
10
|
+
"requirements": ["meteocatpy==0.0.8", "packaging>=20.3", "wrapt>=1.14.0"],
|
|
11
|
+
"version": "0.1.26"
|
|
12
|
+
}
|
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
from homeassistant.config_entries import ConfigEntry, OptionsFlow
|
|
5
|
-
from homeassistant.exceptions import HomeAssistantError
|
|
6
|
-
from homeassistant.helpers import config_validation as cv
|
|
7
|
-
import voluptuous as vol
|
|
8
|
-
|
|
9
|
-
from .const import (
|
|
10
|
-
CONF_API_KEY
|
|
11
|
-
)
|
|
12
|
-
from meteocatpy.town import MeteocatTown
|
|
13
|
-
from meteocatpy.exceptions import (
|
|
14
|
-
BadRequestError,
|
|
15
|
-
ForbiddenError,
|
|
16
|
-
TooManyRequestsError,
|
|
17
|
-
InternalServerError,
|
|
18
|
-
UnknownAPIError,
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
_LOGGER = logging.getLogger(__name__)
|
|
22
|
-
|
|
23
|
-
class MeteocatOptionsFlowHandler(OptionsFlow):
|
|
24
|
-
"""Manejo del flujo de opciones para Meteocat."""
|
|
25
|
-
|
|
26
|
-
def __init__(self, config_entry: ConfigEntry):
|
|
27
|
-
"""Inicializa el flujo de opciones."""
|
|
28
|
-
self.config_entry = config_entry
|
|
29
|
-
self.api_key: str | None = None
|
|
30
|
-
|
|
31
|
-
async def async_step_init(self, user_input: dict | None = None):
|
|
32
|
-
"""Paso inicial del flujo de opciones."""
|
|
33
|
-
return await self.async_step_update_api_key()
|
|
34
|
-
|
|
35
|
-
async def async_step_update_api_key(self, user_input: dict | None = None):
|
|
36
|
-
"""Permite al usuario actualizar la API Key."""
|
|
37
|
-
errors = {}
|
|
38
|
-
|
|
39
|
-
if user_input is not None:
|
|
40
|
-
self.api_key = user_input[CONF_API_KEY]
|
|
41
|
-
|
|
42
|
-
# Validar la nueva API Key utilizando MeteocatTown
|
|
43
|
-
town_client = MeteocatTown(self.api_key)
|
|
44
|
-
|
|
45
|
-
try:
|
|
46
|
-
await town_client.get_municipis() # Verificar que la API Key sea válida
|
|
47
|
-
except (
|
|
48
|
-
BadRequestError,
|
|
49
|
-
ForbiddenError,
|
|
50
|
-
TooManyRequestsError,
|
|
51
|
-
InternalServerError,
|
|
52
|
-
UnknownAPIError,
|
|
53
|
-
) as ex:
|
|
54
|
-
_LOGGER.error("Error al validar la nueva API Key: %s", ex)
|
|
55
|
-
errors["base"] = "cannot_connect"
|
|
56
|
-
except Exception as ex:
|
|
57
|
-
_LOGGER.error("Error inesperado al validar la nueva API Key: %s", ex)
|
|
58
|
-
errors["base"] = "unknown"
|
|
59
|
-
|
|
60
|
-
if not errors:
|
|
61
|
-
# Actualizar la configuración de la entrada con la nueva API Key
|
|
62
|
-
self.hass.config_entries.async_update_entry(
|
|
63
|
-
self.config_entry,
|
|
64
|
-
data={**self.config_entry.data, CONF_API_KEY: self.api_key},
|
|
65
|
-
)
|
|
66
|
-
return self.async_create_entry(title="", data={})
|
|
67
|
-
|
|
68
|
-
schema = vol.Schema({vol.Required(CONF_API_KEY): str})
|
|
69
|
-
return self.async_show_form(
|
|
70
|
-
step_id="update_api_key", data_schema=schema, errors=errors
|
|
71
|
-
)
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from homeassistant.config_entries import ConfigEntry, OptionsFlow
|
|
5
|
+
from homeassistant.exceptions import HomeAssistantError
|
|
6
|
+
from homeassistant.helpers import config_validation as cv
|
|
7
|
+
import voluptuous as vol
|
|
8
|
+
|
|
9
|
+
from .const import (
|
|
10
|
+
CONF_API_KEY
|
|
11
|
+
)
|
|
12
|
+
from meteocatpy.town import MeteocatTown
|
|
13
|
+
from meteocatpy.exceptions import (
|
|
14
|
+
BadRequestError,
|
|
15
|
+
ForbiddenError,
|
|
16
|
+
TooManyRequestsError,
|
|
17
|
+
InternalServerError,
|
|
18
|
+
UnknownAPIError,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
_LOGGER = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
class MeteocatOptionsFlowHandler(OptionsFlow):
|
|
24
|
+
"""Manejo del flujo de opciones para Meteocat."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, config_entry: ConfigEntry):
|
|
27
|
+
"""Inicializa el flujo de opciones."""
|
|
28
|
+
self.config_entry = config_entry
|
|
29
|
+
self.api_key: str | None = None
|
|
30
|
+
|
|
31
|
+
async def async_step_init(self, user_input: dict | None = None):
|
|
32
|
+
"""Paso inicial del flujo de opciones."""
|
|
33
|
+
return await self.async_step_update_api_key()
|
|
34
|
+
|
|
35
|
+
async def async_step_update_api_key(self, user_input: dict | None = None):
|
|
36
|
+
"""Permite al usuario actualizar la API Key."""
|
|
37
|
+
errors = {}
|
|
38
|
+
|
|
39
|
+
if user_input is not None:
|
|
40
|
+
self.api_key = user_input[CONF_API_KEY]
|
|
41
|
+
|
|
42
|
+
# Validar la nueva API Key utilizando MeteocatTown
|
|
43
|
+
town_client = MeteocatTown(self.api_key)
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
await town_client.get_municipis() # Verificar que la API Key sea válida
|
|
47
|
+
except (
|
|
48
|
+
BadRequestError,
|
|
49
|
+
ForbiddenError,
|
|
50
|
+
TooManyRequestsError,
|
|
51
|
+
InternalServerError,
|
|
52
|
+
UnknownAPIError,
|
|
53
|
+
) as ex:
|
|
54
|
+
_LOGGER.error("Error al validar la nueva API Key: %s", ex)
|
|
55
|
+
errors["base"] = "cannot_connect"
|
|
56
|
+
except Exception as ex:
|
|
57
|
+
_LOGGER.error("Error inesperado al validar la nueva API Key: %s", ex)
|
|
58
|
+
errors["base"] = "unknown"
|
|
59
|
+
|
|
60
|
+
if not errors:
|
|
61
|
+
# Actualizar la configuración de la entrada con la nueva API Key
|
|
62
|
+
self.hass.config_entries.async_update_entry(
|
|
63
|
+
self.config_entry,
|
|
64
|
+
data={**self.config_entry.data, CONF_API_KEY: self.api_key},
|
|
65
|
+
)
|
|
66
|
+
return self.async_create_entry(title="", data={})
|
|
67
|
+
|
|
68
|
+
schema = vol.Schema({vol.Required(CONF_API_KEY): str})
|
|
69
|
+
return self.async_show_form(
|
|
70
|
+
step_id="update_api_key", data_schema=schema, errors=errors
|
|
71
|
+
)
|