meteocat 0.1.22 → 0.1.25

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.
Files changed (35) hide show
  1. package/.github/workflows/release.yml +33 -33
  2. package/.pre-commit-config.yaml +37 -37
  3. package/.releaserc +23 -23
  4. package/.releaserc.toml +14 -14
  5. package/AUTHORS.md +12 -12
  6. package/CHANGELOG.md +401 -372
  7. package/README.md +40 -40
  8. package/custom_components/meteocat/__init__.py +107 -107
  9. package/custom_components/meteocat/condition.py +28 -28
  10. package/custom_components/meteocat/config_flow.py +192 -192
  11. package/custom_components/meteocat/const.py +55 -52
  12. package/custom_components/meteocat/coordinator.py +195 -171
  13. package/custom_components/meteocat/entity.py +98 -91
  14. package/custom_components/meteocat/helpers.py +42 -42
  15. package/custom_components/meteocat/manifest.json +12 -12
  16. package/custom_components/meteocat/options_flow.py +71 -71
  17. package/custom_components/meteocat/sensor.py +303 -185
  18. package/custom_components/meteocat/strings.json +25 -25
  19. package/custom_components/meteocat/translations/ca.json +25 -25
  20. package/custom_components/meteocat/translations/en.json +25 -25
  21. package/custom_components/meteocat/translations/es.json +25 -25
  22. package/custom_components/meteocat/version.py +2 -2
  23. package/filetree.py +48 -48
  24. package/filetree.txt +46 -46
  25. package/hacs.json +5 -5
  26. package/package.json +22 -22
  27. package/poetry.lock +3216 -3216
  28. package/pyproject.toml +64 -64
  29. package/releaserc.json +17 -17
  30. package/requirements.test.txt +3 -3
  31. package/setup.cfg +64 -64
  32. package/setup.py +10 -10
  33. package/tests/bandit.yaml +17 -17
  34. package/tests/conftest.py +19 -19
  35. package/tests/test_init.py +9 -9
@@ -1,171 +1,195 @@
1
- from __future__ import annotations
2
-
3
- import logging
4
- from datetime import timedelta
5
- from typing import Dict
6
-
7
- from homeassistant.core import HomeAssistant
8
- from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
9
- from homeassistant.exceptions import ConfigEntryNotReady
10
-
11
- from meteocatpy.data import MeteocatStationData
12
- from meteocatpy.forecast import MeteocatForecast
13
- from meteocatpy.exceptions import (
14
- BadRequestError,
15
- ForbiddenError,
16
- TooManyRequestsError,
17
- InternalServerError,
18
- UnknownAPIError,
19
- )
20
-
21
- from .const import DOMAIN
22
-
23
- _LOGGER = logging.getLogger(__name__)
24
-
25
- # Valores predeterminados para los intervalos de actualización
26
- DEFAULT_SENSOR_UPDATE_INTERVAL = timedelta(minutes=90)
27
- DEFAULT_ENTITY_UPDATE_INTERVAL = timedelta(hours=12)
28
-
29
-
30
- class MeteocatSensorCoordinator(DataUpdateCoordinator):
31
- """Coordinator para manejar la actualización de datos de los sensores."""
32
-
33
- def __init__(
34
- self,
35
- hass: HomeAssistant,
36
- entry_data: dict,
37
- update_interval: timedelta = DEFAULT_SENSOR_UPDATE_INTERVAL,
38
- ):
39
- """
40
- Inicializa el coordinador de sensores de Meteocat.
41
-
42
- Args:
43
- hass (HomeAssistant): Instancia de Home Assistant.
44
- entry_data (dict): Datos de configuración obtenidos de core.config_entries.
45
- update_interval (timedelta): Intervalo de actualización.
46
- """
47
- self.api_key = entry_data["api_key"] # Usamos la API key de la configuración
48
- self.town_name = entry_data["town_name"] # Usamos el nombre del municipio
49
- self.town_id = entry_data["town_id"] # Usamos el ID del municipio
50
- self.station_name = entry_data["station_name"] # Usamos el nombre de la estación
51
- self.station_id = entry_data["station_id"] # Usamos el ID de la estación
52
- self.variable_name = entry_data["variable_name"] # Usamos el nombre de la variable
53
- self.variable_id = entry_data["variable_id"] # Usamos el ID de la variable
54
- self.meteocat_station_data = MeteocatStationData(self.api_key)
55
-
56
- super().__init__(
57
- hass,
58
- _LOGGER,
59
- name=f"{DOMAIN} Sensor Coordinator",
60
- update_interval=update_interval,
61
- )
62
-
63
- async def _async_update_data(self) -> Dict:
64
- """Actualiza los datos de los sensores desde la API de Meteocat."""
65
- try:
66
- data = await self.meteocat_station_data.get_station_data_with_variables(self.station_id)
67
- _LOGGER.debug("Datos de sensores actualizados exitosamente: %s", data)
68
- return data
69
- except ForbiddenError as err:
70
- _LOGGER.error(
71
- "Acceso denegado al obtener datos de sensores (Station ID: %s): %s",
72
- self.station_id,
73
- err,
74
- )
75
- raise ConfigEntryNotReady from err
76
- except TooManyRequestsError as err:
77
- _LOGGER.warning(
78
- "Límite de solicitudes alcanzado al obtener datos de sensores (Station ID: %s): %s",
79
- self.station_id,
80
- err,
81
- )
82
- raise ConfigEntryNotReady from err
83
- except (BadRequestError, InternalServerError, UnknownAPIError) as err:
84
- _LOGGER.error(
85
- "Error al obtener datos de sensores (Station ID: %s): %s",
86
- self.station_id,
87
- err,
88
- )
89
- raise
90
- except Exception as err:
91
- _LOGGER.exception(
92
- "Error inesperado al obtener datos de sensores (Station ID: %s): %s",
93
- self.station_id,
94
- err,
95
- )
96
- raise
97
-
98
-
99
- class MeteocatEntityCoordinator(DataUpdateCoordinator):
100
- """Coordinator para manejar la actualización de datos de las entidades de predicción."""
101
-
102
- def __init__(
103
- self,
104
- hass: HomeAssistant,
105
- entry_data: dict,
106
- update_interval: timedelta = DEFAULT_ENTITY_UPDATE_INTERVAL,
107
- ):
108
- """
109
- Inicializa el coordinador de datos para entidades de predicción.
110
-
111
- Args:
112
- hass (HomeAssistant): Instancia de Home Assistant.
113
- entry_data (dict): Datos de configuración obtenidos de core.config_entries.
114
- update_interval (timedelta): Intervalo de actualización.
115
- """
116
- self.api_key = entry_data["api_key"]
117
- self.town_name = entry_data["town_name"]
118
- self.town_id = entry_data["town_id"]
119
- self.station_name = entry_data["station_name"]
120
- self.station_id = entry_data["station_id"]
121
- self.variable_name = entry_data["variable_name"]
122
- self.variable_id = entry_data["variable_id"]
123
- self.meteocat_forecast = MeteocatForecast(self.api_key)
124
-
125
- super().__init__(
126
- hass,
127
- _LOGGER,
128
- name=f"{DOMAIN} Entity Coordinator",
129
- update_interval=update_interval,
130
- )
131
-
132
- async def _async_update_data(self) -> Dict:
133
- """Actualiza los datos de las entidades de predicción desde la API de Meteocat."""
134
- try:
135
- hourly_forecast = await self.meteocat_forecast.get_prediccion_horaria(self.town_id)
136
- daily_forecast = await self.meteocat_forecast.get_prediccion_diaria(self.town_id)
137
- _LOGGER.debug(
138
- "Datos de predicción actualizados exitosamente (Town ID: %s)", self.town_id
139
- )
140
- return {
141
- "hourly_forecast": hourly_forecast,
142
- "daily_forecast": daily_forecast,
143
- }
144
- except ForbiddenError as err:
145
- _LOGGER.error(
146
- "Acceso denegado al obtener datos de predicción (Town ID: %s): %s",
147
- self.town_id,
148
- err,
149
- )
150
- raise ConfigEntryNotReady from err
151
- except TooManyRequestsError as err:
152
- _LOGGER.warning(
153
- "Límite de solicitudes alcanzado al obtener datos de predicción (Town ID: %s): %s",
154
- self.town_id,
155
- err,
156
- )
157
- raise ConfigEntryNotReady from err
158
- except (BadRequestError, InternalServerError, UnknownAPIError) as err:
159
- _LOGGER.error(
160
- "Error al obtener datos de predicción (Town ID: %s): %s",
161
- self.town_id,
162
- err,
163
- )
164
- raise
165
- except Exception as err:
166
- _LOGGER.exception(
167
- "Error inesperado al obtener datos de predicción (Town ID: %s): %s",
168
- self.town_id,
169
- err,
170
- )
171
- raise
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import json
5
+ import logging
6
+ from datetime import timedelta
7
+ from typing import Dict
8
+
9
+ from homeassistant.core import HomeAssistant
10
+ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
11
+ from homeassistant.exceptions import ConfigEntryNotReady
12
+
13
+ from meteocatpy.data import MeteocatStationData
14
+ from meteocatpy.forecast import MeteocatForecast
15
+ from meteocatpy.exceptions import (
16
+ BadRequestError,
17
+ ForbiddenError,
18
+ TooManyRequestsError,
19
+ InternalServerError,
20
+ UnknownAPIError,
21
+ )
22
+
23
+ from .const import DOMAIN
24
+
25
+ _LOGGER = logging.getLogger(__name__)
26
+
27
+ # Valores predeterminados para los intervalos de actualización
28
+ DEFAULT_SENSOR_UPDATE_INTERVAL = timedelta(minutes=90)
29
+ DEFAULT_ENTITY_UPDATE_INTERVAL = timedelta(hours=12)
30
+
31
+ def save_json_to_file(data: dict, filename="station_data.json"):
32
+ """Guardar datos JSON en un archivo para análisis."""
33
+ try:
34
+ # Crear la carpeta 'files' si no existe
35
+ output_folder = os.path.join(os.path.dirname(__file__), "files")
36
+ os.makedirs(output_folder, exist_ok=True)
37
+
38
+ # Ruta completa del archivo
39
+ output_file = os.path.join(output_folder, filename)
40
+
41
+ # Guardar los datos en el archivo
42
+ with open(output_file, "w", encoding="utf-8") as f:
43
+ json.dump(data, f, ensure_ascii=False, indent=4)
44
+
45
+ _LOGGER.info(f"Archivo JSON guardado en: {output_file}")
46
+ except Exception as e:
47
+ _LOGGER.error(f"Error al guardar el archivo JSON: {e}")
48
+
49
+ class MeteocatSensorCoordinator(DataUpdateCoordinator):
50
+ """Coordinator para manejar la actualización de datos de los sensores."""
51
+
52
+ def __init__(
53
+ self,
54
+ hass: HomeAssistant,
55
+ entry_data: dict,
56
+ update_interval: timedelta = DEFAULT_SENSOR_UPDATE_INTERVAL,
57
+ ):
58
+ """
59
+ Inicializa el coordinador de sensores de Meteocat.
60
+
61
+ Args:
62
+ hass (HomeAssistant): Instancia de Home Assistant.
63
+ entry_data (dict): Datos de configuración obtenidos de core.config_entries.
64
+ update_interval (timedelta): Intervalo de actualización.
65
+ """
66
+ self.api_key = entry_data["api_key"] # Usamos la API key de la configuración
67
+ self.town_name = entry_data["town_name"] # Usamos el nombre del municipio
68
+ self.town_id = entry_data["town_id"] # Usamos el ID del municipio
69
+ self.station_name = entry_data["station_name"] # Usamos el nombre de la estación
70
+ self.station_id = entry_data["station_id"] # Usamos el ID de la estación
71
+ self.variable_name = entry_data["variable_name"] # Usamos el nombre de la variable
72
+ self.variable_id = entry_data["variable_id"] # Usamos el ID de la variable
73
+ self.meteocat_station_data = MeteocatStationData(self.api_key)
74
+
75
+ super().__init__(
76
+ hass,
77
+ _LOGGER,
78
+ name=f"{DOMAIN} Sensor Coordinator",
79
+ update_interval=update_interval,
80
+ )
81
+
82
+ async def _async_update_data(self) -> Dict:
83
+ """Actualiza los datos de los sensores desde la API de Meteocat."""
84
+ try:
85
+ # Obtener datos desde la API
86
+ data = await self.meteocat_station_data.get_station_data(self.station_id)
87
+ _LOGGER.debug("Datos de sensores actualizados exitosamente: %s", data)
88
+
89
+ # Guardar los datos en un archivo JSON
90
+ save_json_to_file(data)
91
+
92
+ return data
93
+ except ForbiddenError as err:
94
+ _LOGGER.error(
95
+ "Acceso denegado al obtener datos de sensores (Station ID: %s): %s",
96
+ self.station_id,
97
+ err,
98
+ )
99
+ raise ConfigEntryNotReady from err
100
+ except TooManyRequestsError as err:
101
+ _LOGGER.warning(
102
+ "Límite de solicitudes alcanzado al obtener datos de sensores (Station ID: %s): %s",
103
+ self.station_id,
104
+ err,
105
+ )
106
+ raise ConfigEntryNotReady from err
107
+ except (BadRequestError, InternalServerError, UnknownAPIError) as err:
108
+ _LOGGER.error(
109
+ "Error al obtener datos de sensores (Station ID: %s): %s",
110
+ self.station_id,
111
+ err,
112
+ )
113
+ raise
114
+ except Exception as err:
115
+ _LOGGER.exception(
116
+ "Error inesperado al obtener datos de sensores (Station ID: %s): %s",
117
+ self.station_id,
118
+ err,
119
+ )
120
+ raise
121
+
122
+
123
+ class MeteocatEntityCoordinator(DataUpdateCoordinator):
124
+ """Coordinator para manejar la actualización de datos de las entidades de predicción."""
125
+
126
+ def __init__(
127
+ self,
128
+ hass: HomeAssistant,
129
+ entry_data: dict,
130
+ update_interval: timedelta = DEFAULT_ENTITY_UPDATE_INTERVAL,
131
+ ):
132
+ """
133
+ Inicializa el coordinador de datos para entidades de predicción.
134
+
135
+ Args:
136
+ hass (HomeAssistant): Instancia de Home Assistant.
137
+ entry_data (dict): Datos de configuración obtenidos de core.config_entries.
138
+ update_interval (timedelta): Intervalo de actualización.
139
+ """
140
+ self.api_key = entry_data["api_key"]
141
+ self.town_name = entry_data["town_name"]
142
+ self.town_id = entry_data["town_id"]
143
+ self.station_name = entry_data["station_name"]
144
+ self.station_id = entry_data["station_id"]
145
+ self.variable_name = entry_data["variable_name"]
146
+ self.variable_id = entry_data["variable_id"]
147
+ self.meteocat_forecast = MeteocatForecast(self.api_key)
148
+
149
+ super().__init__(
150
+ hass,
151
+ _LOGGER,
152
+ name=f"{DOMAIN} Entity Coordinator",
153
+ update_interval=update_interval,
154
+ )
155
+
156
+ async def _async_update_data(self) -> Dict:
157
+ """Actualiza los datos de las entidades de predicción desde la API de Meteocat."""
158
+ try:
159
+ hourly_forecast = await self.meteocat_forecast.get_prediccion_horaria(self.town_id)
160
+ daily_forecast = await self.meteocat_forecast.get_prediccion_diaria(self.town_id)
161
+ _LOGGER.debug(
162
+ "Datos de predicción actualizados exitosamente (Town ID: %s)", self.town_id
163
+ )
164
+ return {
165
+ "hourly_forecast": hourly_forecast,
166
+ "daily_forecast": daily_forecast,
167
+ }
168
+ except ForbiddenError as err:
169
+ _LOGGER.error(
170
+ "Acceso denegado al obtener datos de predicción (Town ID: %s): %s",
171
+ self.town_id,
172
+ err,
173
+ )
174
+ raise ConfigEntryNotReady from err
175
+ except TooManyRequestsError as err:
176
+ _LOGGER.warning(
177
+ "Límite de solicitudes alcanzado al obtener datos de predicción (Town ID: %s): %s",
178
+ self.town_id,
179
+ err,
180
+ )
181
+ raise ConfigEntryNotReady from err
182
+ except (BadRequestError, InternalServerError, UnknownAPIError) as err:
183
+ _LOGGER.error(
184
+ "Error al obtener datos de predicción (Town ID: %s): %s",
185
+ self.town_id,
186
+ err,
187
+ )
188
+ raise
189
+ except Exception as err:
190
+ _LOGGER.exception(
191
+ "Error inesperado al obtener datos de predicción (Town ID: %s): %s",
192
+ self.town_id,
193
+ err,
194
+ )
195
+ raise
@@ -1,91 +1,98 @@
1
- from __future__ import annotations
2
-
3
- import asyncio
4
- import logging
5
- from homeassistant.components.weather import WeatherEntity
6
- from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT
7
-
8
- from .const import (
9
- DOMAIN,
10
- CONF_API_KEY,
11
- TOWN_ID,
12
- TEMPERATURE,
13
- HUMIDITY,
14
- WIND_SPEED,
15
- WIND_DIRECTION,
16
- )
17
- from .condition import get_condition_from_statcel
18
- from .coordinator import MeteocatEntityCoordinator
19
-
20
-
21
- _LOGGER = logging.getLogger(__name__)
22
-
23
- async def async_setup_entry(hass, config_entry, async_add_entities):
24
- """Configura el componente weather basado en una entrada de configuración."""
25
- api_key = config_entry.data[CONF_API_KEY]
26
- town_id = config_entry.data[TOWN_ID]
27
-
28
- # Crear el coordinador
29
- coordinator = MeteocatEntityCoordinator(hass, api_key, town_id)
30
- # Asegurarse de que el coordinador esté actualizado antes de agregar la entidad
31
- await coordinator.async_refresh()
32
-
33
- async_add_entities([MeteocatWeatherEntity(coordinator)], True)
34
-
35
- class MeteocatWeatherEntity(WeatherEntity):
36
- """Entidad de clima para la integración Meteocat."""
37
-
38
- def __init__(self, coordinator: MeteocatEntityCoordinator):
39
- """Inicializa la entidad MeteocatWeather."""
40
- self._coordinator = coordinator
41
- self._attr_temperature_unit = TEMP_CELSIUS
42
- self._data = {}
43
-
44
- async def async_update(self):
45
- """Actualiza los datos meteorológicos."""
46
- try:
47
- # Usamos el coordinador para obtener los datos actualizados
48
- if self._coordinator.data:
49
- hourly_forecast = self._coordinator.data["hourly_forecast"]
50
- current_forecast = hourly_forecast["variables"]
51
- codi_estatcel = current_forecast.get("estatCel", {}).get("valor")
52
- is_night = current_forecast.get("is_night", False)
53
- self._data = {
54
- "temperature": current_forecast.get(TEMPERATURE, {}).get("valor"),
55
- "humidity": current_forecast.get(HUMIDITY, {}).get("valor"),
56
- "wind_speed": current_forecast.get(WIND_SPEED, {}).get("valor"),
57
- "wind_bearing": current_forecast.get(WIND_DIRECTION, {}).get("valor"),
58
- "condition": get_condition_from_statcel(codi_estatcel, is_night)["condition"],
59
- }
60
- except Exception as err:
61
- _LOGGER.error("Error al actualizar la predicción de Meteocat: %s", err)
62
-
63
- @property
64
- def name(self):
65
- """Retorna el nombre de la entidad."""
66
- return f"Clima {self._coordinator._town_id}"
67
-
68
- @property
69
- def temperature(self):
70
- """Retorna la temperatura actual."""
71
- return self._data.get("temperature")
72
-
73
- @property
74
- def humidity(self):
75
- """Retorna la humedad relativa actual."""
76
- return self._data.get("humidity")
77
-
78
- @property
79
- def wind_speed(self):
80
- """Retorna la velocidad del viento."""
81
- return self._data.get("wind_speed")
82
-
83
- @property
84
- def wind_bearing(self):
85
- """Retorna la dirección del viento."""
86
- return self._data.get("wind_bearing")
87
-
88
- @property
89
- def condition(self):
90
- """Retorna la condición climática."""
91
- return self._data.get("condition")
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import logging
5
+ from homeassistant.components.weather import WeatherEntity
6
+ from homeassistant.const import (
7
+ DEGREE,
8
+ PERCENTAGE,
9
+ UnitOfPressure,
10
+ UnitOfSpeed,
11
+ UnitOfTemperature,
12
+ UnitOfVolumetricFlux,
13
+ )
14
+
15
+ from .const import (
16
+ DOMAIN,
17
+ CONF_API_KEY,
18
+ TOWN_ID,
19
+ TEMPERATURE,
20
+ HUMIDITY,
21
+ WIND_SPEED,
22
+ WIND_DIRECTION,
23
+ )
24
+ from .condition import get_condition_from_statcel
25
+ from .coordinator import MeteocatEntityCoordinator
26
+
27
+
28
+ _LOGGER = logging.getLogger(__name__)
29
+
30
+ async def async_setup_entry(hass, config_entry, async_add_entities):
31
+ """Configura el componente weather basado en una entrada de configuración."""
32
+ api_key = config_entry.data[CONF_API_KEY]
33
+ town_id = config_entry.data[TOWN_ID]
34
+
35
+ # Crear el coordinador
36
+ coordinator = MeteocatEntityCoordinator(hass, api_key, town_id)
37
+ # Asegurarse de que el coordinador esté actualizado antes de agregar la entidad
38
+ await coordinator.async_refresh()
39
+
40
+ async_add_entities([MeteocatWeatherEntity(coordinator)], True)
41
+
42
+ class MeteocatWeatherEntity(WeatherEntity):
43
+ """Entidad de clima para la integración Meteocat."""
44
+
45
+ def __init__(self, coordinator: MeteocatEntityCoordinator):
46
+ """Inicializa la entidad MeteocatWeather."""
47
+ self._coordinator = coordinator
48
+ self._attr_temperature_unit = UnitOfTemperature.CELSIUS
49
+ self._data = {}
50
+
51
+ async def async_update(self):
52
+ """Actualiza los datos meteorológicos."""
53
+ try:
54
+ # Usamos el coordinador para obtener los datos actualizados
55
+ if self._coordinator.data:
56
+ hourly_forecast = self._coordinator.data["hourly_forecast"]
57
+ current_forecast = hourly_forecast["variables"]
58
+ codi_estatcel = current_forecast.get("estatCel", {}).get("valor")
59
+ is_night = current_forecast.get("is_night", False)
60
+ self._data = {
61
+ "temperature": current_forecast.get(TEMPERATURE, {}).get("valor"),
62
+ "humidity": current_forecast.get(HUMIDITY, {}).get("valor"),
63
+ "wind_speed": current_forecast.get(WIND_SPEED, {}).get("valor"),
64
+ "wind_bearing": current_forecast.get(WIND_DIRECTION, {}).get("valor"),
65
+ "condition": get_condition_from_statcel(codi_estatcel, is_night)["condition"],
66
+ }
67
+ except Exception as err:
68
+ _LOGGER.error("Error al actualizar la predicción de Meteocat: %s", err)
69
+
70
+ @property
71
+ def name(self):
72
+ """Retorna el nombre de la entidad."""
73
+ return f"Clima {self._coordinator._town_id}"
74
+
75
+ @property
76
+ def temperature(self):
77
+ """Retorna la temperatura actual."""
78
+ return self._data.get("temperature")
79
+
80
+ @property
81
+ def humidity(self):
82
+ """Retorna la humedad relativa actual."""
83
+ return self._data.get("humidity")
84
+
85
+ @property
86
+ def wind_speed(self):
87
+ """Retorna la velocidad del viento."""
88
+ return self._data.get("wind_speed")
89
+
90
+ @property
91
+ def wind_bearing(self):
92
+ """Retorna la dirección del viento."""
93
+ return self._data.get("wind_bearing")
94
+
95
+ @property
96
+ def condition(self):
97
+ """Retorna la condición climática."""
98
+ return self._data.get("condition")
@@ -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