meteocat 3.1.0 → 4.0.0
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/ISSUE_TEMPLATE/bug_report.md +45 -45
- package/.github/ISSUE_TEMPLATE/config.yml +8 -8
- package/.github/ISSUE_TEMPLATE/improvement.md +39 -39
- package/.github/ISSUE_TEMPLATE/new_function.md +41 -41
- package/.github/labels.yml +63 -63
- package/.github/workflows/autocloser.yaml +27 -27
- package/.github/workflows/close-on-label.yml +48 -48
- package/.github/workflows/force-sync-labels.yml +18 -18
- package/.github/workflows/hassfest.yaml +13 -13
- package/.github/workflows/publish-zip.yml +67 -67
- package/.github/workflows/release.yml +41 -41
- package/.github/workflows/stale.yml +63 -63
- package/.github/workflows/sync-gitlab.yml +107 -107
- package/.github/workflows/sync-labels.yml +21 -21
- package/.github/workflows/validate.yaml +16 -16
- package/.pre-commit-config.yaml +37 -37
- package/.releaserc +37 -37
- package/AUTHORS.md +13 -13
- package/CHANGELOG.md +954 -898
- package/README.md +207 -204
- package/conftest.py +11 -11
- package/custom_components/meteocat/__init__.py +298 -293
- package/custom_components/meteocat/condition.py +63 -59
- package/custom_components/meteocat/config_flow.py +613 -435
- package/custom_components/meteocat/const.py +132 -120
- package/custom_components/meteocat/coordinator.py +1040 -205
- package/custom_components/meteocat/helpers.py +58 -63
- package/custom_components/meteocat/manifest.json +25 -24
- package/custom_components/meteocat/options_flow.py +287 -277
- package/custom_components/meteocat/sensor.py +366 -4
- package/custom_components/meteocat/strings.json +1058 -867
- package/custom_components/meteocat/translations/ca.json +1058 -867
- package/custom_components/meteocat/translations/en.json +1058 -867
- package/custom_components/meteocat/translations/es.json +1058 -867
- package/custom_components/meteocat/version.py +1 -1
- package/custom_components/meteocat/weather.py +218 -218
- package/filetree.py +48 -48
- package/filetree.txt +79 -70
- package/hacs.json +8 -8
- package/images/daily_forecast_2_alerts.png +0 -0
- package/images/daily_forecast_no_alerts.png +0 -0
- package/images/diagnostic_sensors.png +0 -0
- package/images/dynamic_sensors.png +0 -0
- package/images/options.png +0 -0
- package/images/regenerate_assets.png +0 -0
- package/images/setup_options.png +0 -0
- package/images/system_options.png +0 -0
- package/info.md +11 -11
- package/package.json +22 -22
- package/poetry.lock +3222 -3222
- package/pyproject.toml +68 -68
- 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 +1 @@
|
|
|
1
|
-
__version__ = "
|
|
1
|
+
__version__ = "4.0.0"
|
|
@@ -1,218 +1,218 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
from datetime import datetime
|
|
5
|
-
from typing import Optional
|
|
6
|
-
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
|
7
|
-
from homeassistant.components.weather import (
|
|
8
|
-
WeatherEntity,
|
|
9
|
-
WeatherEntityFeature,
|
|
10
|
-
Forecast,
|
|
11
|
-
)
|
|
12
|
-
from homeassistant.core import callback
|
|
13
|
-
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
14
|
-
from homeassistant.helpers.device_registry import DeviceInfo
|
|
15
|
-
from homeassistant.const import (
|
|
16
|
-
DEGREE,
|
|
17
|
-
PERCENTAGE,
|
|
18
|
-
UnitOfPrecipitationDepth,
|
|
19
|
-
UnitOfPressure,
|
|
20
|
-
UnitOfSpeed,
|
|
21
|
-
UnitOfTemperature,
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
from .const import (
|
|
25
|
-
DOMAIN,
|
|
26
|
-
ATTRIBUTION,
|
|
27
|
-
WIND_SPEED_CODE,
|
|
28
|
-
WIND_DIRECTION_CODE,
|
|
29
|
-
TEMPERATURE_CODE,
|
|
30
|
-
HUMIDITY_CODE,
|
|
31
|
-
PRESSURE_CODE,
|
|
32
|
-
PRECIPITATION_CODE,
|
|
33
|
-
WIND_GUST_CODE,
|
|
34
|
-
)
|
|
35
|
-
from .coordinator import (
|
|
36
|
-
HourlyForecastCoordinator,
|
|
37
|
-
DailyForecastCoordinator,
|
|
38
|
-
MeteocatSensorCoordinator,
|
|
39
|
-
MeteocatUviFileCoordinator,
|
|
40
|
-
MeteocatConditionCoordinator,
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
_LOGGER = logging.getLogger(__name__)
|
|
44
|
-
|
|
45
|
-
@callback
|
|
46
|
-
async def async_setup_entry(hass, entry, async_add_entities: AddEntitiesCallback) -> None:
|
|
47
|
-
"""Set up Meteocat weather entity from a config entry."""
|
|
48
|
-
entry_data = hass.data[DOMAIN][entry.entry_id]
|
|
49
|
-
|
|
50
|
-
hourly_forecast_coordinator = entry_data.get("hourly_forecast_coordinator")
|
|
51
|
-
daily_forecast_coordinator = entry_data.get("daily_forecast_coordinator")
|
|
52
|
-
sensor_coordinator = entry_data.get("sensor_coordinator")
|
|
53
|
-
uvi_file_coordinator = entry_data.get("uvi_file_coordinator")
|
|
54
|
-
condition_coordinator = entry_data.get("condition_coordinator")
|
|
55
|
-
|
|
56
|
-
async_add_entities([
|
|
57
|
-
MeteocatWeatherEntity(
|
|
58
|
-
hourly_forecast_coordinator,
|
|
59
|
-
daily_forecast_coordinator,
|
|
60
|
-
sensor_coordinator,
|
|
61
|
-
uvi_file_coordinator,
|
|
62
|
-
condition_coordinator,
|
|
63
|
-
entry_data
|
|
64
|
-
)
|
|
65
|
-
])
|
|
66
|
-
|
|
67
|
-
class MeteocatWeatherEntity(CoordinatorEntity, WeatherEntity):
|
|
68
|
-
"""Representation of a Meteocat Weather Entity."""
|
|
69
|
-
|
|
70
|
-
_attr_attribution = ATTRIBUTION
|
|
71
|
-
_attr_has_entity_name = True
|
|
72
|
-
_attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS
|
|
73
|
-
_attr_native_precipitation_probability_unit = PERCENTAGE
|
|
74
|
-
_attr_native_pressure_unit = UnitOfPressure.HPA
|
|
75
|
-
_attr_native_temperature_unit = UnitOfTemperature.CELSIUS
|
|
76
|
-
_attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR
|
|
77
|
-
_attr_native_wind_bearing_unit = DEGREE
|
|
78
|
-
_attr_supported_features = (
|
|
79
|
-
WeatherEntityFeature.FORECAST_HOURLY | WeatherEntityFeature.FORECAST_DAILY
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
def __init__(
|
|
83
|
-
self,
|
|
84
|
-
hourly_forecast_coordinator: HourlyForecastCoordinator,
|
|
85
|
-
daily_forecast_coordinator: DailyForecastCoordinator,
|
|
86
|
-
sensor_coordinator: MeteocatSensorCoordinator,
|
|
87
|
-
uvi_file_coordinator: MeteocatUviFileCoordinator,
|
|
88
|
-
condition_coordinator: MeteocatConditionCoordinator,
|
|
89
|
-
entry_data: dict,
|
|
90
|
-
) -> None:
|
|
91
|
-
"""Initialize the weather entity."""
|
|
92
|
-
super().__init__(daily_forecast_coordinator)
|
|
93
|
-
self._hourly_forecast_coordinator = hourly_forecast_coordinator
|
|
94
|
-
self._daily_forecast_coordinator = daily_forecast_coordinator
|
|
95
|
-
self._sensor_coordinator = sensor_coordinator
|
|
96
|
-
self._uvi_file_coordinator = uvi_file_coordinator
|
|
97
|
-
self._condition_coordinator = condition_coordinator
|
|
98
|
-
self._town_name = entry_data["town_name"]
|
|
99
|
-
self._town_id = entry_data["town_id"]
|
|
100
|
-
self._station_id = entry_data["station_id"]
|
|
101
|
-
|
|
102
|
-
@property
|
|
103
|
-
def name(self) -> str:
|
|
104
|
-
"""Return the name of the entity."""
|
|
105
|
-
return f"Weather {self._town_name}"
|
|
106
|
-
|
|
107
|
-
@property
|
|
108
|
-
def unique_id(self) -> str:
|
|
109
|
-
"""Return the unique ID of the entity."""
|
|
110
|
-
return f"weather.{DOMAIN}_{self._town_id}"
|
|
111
|
-
|
|
112
|
-
@property
|
|
113
|
-
def condition(self) -> Optional[str]:
|
|
114
|
-
"""Return the current weather condition."""
|
|
115
|
-
condition_data = self._condition_coordinator.data or {}
|
|
116
|
-
return condition_data.get("condition")
|
|
117
|
-
|
|
118
|
-
def _get_latest_sensor_value(self, code: str) -> Optional[float]:
|
|
119
|
-
"""Helper method to retrieve the latest sensor value."""
|
|
120
|
-
sensor_code = code
|
|
121
|
-
if not sensor_code:
|
|
122
|
-
return None
|
|
123
|
-
|
|
124
|
-
stations = self._sensor_coordinator.data or []
|
|
125
|
-
for station in stations:
|
|
126
|
-
variables = station.get("variables", [])
|
|
127
|
-
variable_data = next(
|
|
128
|
-
(var for var in variables if var.get("codi") == sensor_code),
|
|
129
|
-
None,
|
|
130
|
-
)
|
|
131
|
-
if variable_data:
|
|
132
|
-
lectures = variable_data.get("lectures", [])
|
|
133
|
-
if lectures:
|
|
134
|
-
return lectures[-1].get("valor")
|
|
135
|
-
return None
|
|
136
|
-
|
|
137
|
-
@property
|
|
138
|
-
def native_temperature(self) -> Optional[float]:
|
|
139
|
-
return self._get_latest_sensor_value(TEMPERATURE_CODE)
|
|
140
|
-
|
|
141
|
-
@property
|
|
142
|
-
def humidity(self) -> Optional[float]:
|
|
143
|
-
return self._get_latest_sensor_value(HUMIDITY_CODE)
|
|
144
|
-
|
|
145
|
-
@property
|
|
146
|
-
def native_pressure(self) -> Optional[float]:
|
|
147
|
-
return self._get_latest_sensor_value(PRESSURE_CODE)
|
|
148
|
-
|
|
149
|
-
@property
|
|
150
|
-
def native_wind_speed(self) -> Optional[float]:
|
|
151
|
-
return self._get_latest_sensor_value(WIND_SPEED_CODE)
|
|
152
|
-
|
|
153
|
-
@property
|
|
154
|
-
def native_wind_gust_speed(self) -> Optional[float]:
|
|
155
|
-
return self._get_latest_sensor_value(WIND_GUST_CODE)
|
|
156
|
-
|
|
157
|
-
@property
|
|
158
|
-
def wind_bearing(self) -> Optional[float]:
|
|
159
|
-
return self._get_latest_sensor_value(WIND_DIRECTION_CODE)
|
|
160
|
-
|
|
161
|
-
@property
|
|
162
|
-
def uv_index(self) -> Optional[float]:
|
|
163
|
-
"""Return the UV index."""
|
|
164
|
-
uvi_data = self._uvi_file_coordinator.data or {}
|
|
165
|
-
return uvi_data.get("uvi")
|
|
166
|
-
|
|
167
|
-
async def async_forecast_daily(self) -> list[Forecast] | None:
|
|
168
|
-
"""Return the daily forecast."""
|
|
169
|
-
await self._daily_forecast_coordinator.async_request_refresh()
|
|
170
|
-
daily_forecasts = self._daily_forecast_coordinator.get_all_daily_forecasts()
|
|
171
|
-
if not daily_forecasts:
|
|
172
|
-
return None
|
|
173
|
-
|
|
174
|
-
return [
|
|
175
|
-
Forecast(
|
|
176
|
-
datetime=forecast["date"],
|
|
177
|
-
temperature=forecast["temperature_max"],
|
|
178
|
-
templow=forecast["temperature_min"],
|
|
179
|
-
precipitation_probability=forecast["precipitation"],
|
|
180
|
-
condition=forecast["condition"],
|
|
181
|
-
)
|
|
182
|
-
for forecast in daily_forecasts
|
|
183
|
-
]
|
|
184
|
-
|
|
185
|
-
async def async_forecast_hourly(self) -> list[Forecast] | None:
|
|
186
|
-
"""Return the hourly forecast."""
|
|
187
|
-
await self._hourly_forecast_coordinator.async_request_refresh()
|
|
188
|
-
hourly_forecasts = self._hourly_forecast_coordinator.get_all_hourly_forecasts()
|
|
189
|
-
if not hourly_forecasts:
|
|
190
|
-
return None
|
|
191
|
-
|
|
192
|
-
return [
|
|
193
|
-
Forecast(
|
|
194
|
-
datetime=forecast["datetime"],
|
|
195
|
-
temperature=forecast["temperature"],
|
|
196
|
-
precipitation=forecast["precipitation"],
|
|
197
|
-
condition=forecast["condition"],
|
|
198
|
-
wind_speed=forecast["wind_speed"],
|
|
199
|
-
wind_bearing=forecast["wind_bearing"],
|
|
200
|
-
humidity=forecast["humidity"],
|
|
201
|
-
)
|
|
202
|
-
for forecast in hourly_forecasts
|
|
203
|
-
]
|
|
204
|
-
|
|
205
|
-
async def async_update(self) -> None:
|
|
206
|
-
"""Update the weather entity."""
|
|
207
|
-
await self._hourly_forecast_coordinator.async_request_refresh()
|
|
208
|
-
await self._daily_forecast_coordinator.async_request_refresh()
|
|
209
|
-
|
|
210
|
-
@property
|
|
211
|
-
def device_info(self) -> DeviceInfo:
|
|
212
|
-
"""Return the device info."""
|
|
213
|
-
return DeviceInfo(
|
|
214
|
-
identifiers={(DOMAIN, self._town_id)},
|
|
215
|
-
name=f"Meteocat {self._station_id}",
|
|
216
|
-
manufacturer="Meteocat",
|
|
217
|
-
model="Meteocat API",
|
|
218
|
-
)
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
|
7
|
+
from homeassistant.components.weather import (
|
|
8
|
+
WeatherEntity,
|
|
9
|
+
WeatherEntityFeature,
|
|
10
|
+
Forecast,
|
|
11
|
+
)
|
|
12
|
+
from homeassistant.core import callback
|
|
13
|
+
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
14
|
+
from homeassistant.helpers.device_registry import DeviceInfo
|
|
15
|
+
from homeassistant.const import (
|
|
16
|
+
DEGREE,
|
|
17
|
+
PERCENTAGE,
|
|
18
|
+
UnitOfPrecipitationDepth,
|
|
19
|
+
UnitOfPressure,
|
|
20
|
+
UnitOfSpeed,
|
|
21
|
+
UnitOfTemperature,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
from .const import (
|
|
25
|
+
DOMAIN,
|
|
26
|
+
ATTRIBUTION,
|
|
27
|
+
WIND_SPEED_CODE,
|
|
28
|
+
WIND_DIRECTION_CODE,
|
|
29
|
+
TEMPERATURE_CODE,
|
|
30
|
+
HUMIDITY_CODE,
|
|
31
|
+
PRESSURE_CODE,
|
|
32
|
+
PRECIPITATION_CODE,
|
|
33
|
+
WIND_GUST_CODE,
|
|
34
|
+
)
|
|
35
|
+
from .coordinator import (
|
|
36
|
+
HourlyForecastCoordinator,
|
|
37
|
+
DailyForecastCoordinator,
|
|
38
|
+
MeteocatSensorCoordinator,
|
|
39
|
+
MeteocatUviFileCoordinator,
|
|
40
|
+
MeteocatConditionCoordinator,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
_LOGGER = logging.getLogger(__name__)
|
|
44
|
+
|
|
45
|
+
@callback
|
|
46
|
+
async def async_setup_entry(hass, entry, async_add_entities: AddEntitiesCallback) -> None:
|
|
47
|
+
"""Set up Meteocat weather entity from a config entry."""
|
|
48
|
+
entry_data = hass.data[DOMAIN][entry.entry_id]
|
|
49
|
+
|
|
50
|
+
hourly_forecast_coordinator = entry_data.get("hourly_forecast_coordinator")
|
|
51
|
+
daily_forecast_coordinator = entry_data.get("daily_forecast_coordinator")
|
|
52
|
+
sensor_coordinator = entry_data.get("sensor_coordinator")
|
|
53
|
+
uvi_file_coordinator = entry_data.get("uvi_file_coordinator")
|
|
54
|
+
condition_coordinator = entry_data.get("condition_coordinator")
|
|
55
|
+
|
|
56
|
+
async_add_entities([
|
|
57
|
+
MeteocatWeatherEntity(
|
|
58
|
+
hourly_forecast_coordinator,
|
|
59
|
+
daily_forecast_coordinator,
|
|
60
|
+
sensor_coordinator,
|
|
61
|
+
uvi_file_coordinator,
|
|
62
|
+
condition_coordinator,
|
|
63
|
+
entry_data
|
|
64
|
+
)
|
|
65
|
+
])
|
|
66
|
+
|
|
67
|
+
class MeteocatWeatherEntity(CoordinatorEntity, WeatherEntity):
|
|
68
|
+
"""Representation of a Meteocat Weather Entity."""
|
|
69
|
+
|
|
70
|
+
_attr_attribution = ATTRIBUTION
|
|
71
|
+
_attr_has_entity_name = True
|
|
72
|
+
_attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS
|
|
73
|
+
_attr_native_precipitation_probability_unit = PERCENTAGE
|
|
74
|
+
_attr_native_pressure_unit = UnitOfPressure.HPA
|
|
75
|
+
_attr_native_temperature_unit = UnitOfTemperature.CELSIUS
|
|
76
|
+
_attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR
|
|
77
|
+
_attr_native_wind_bearing_unit = DEGREE
|
|
78
|
+
_attr_supported_features = (
|
|
79
|
+
WeatherEntityFeature.FORECAST_HOURLY | WeatherEntityFeature.FORECAST_DAILY
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def __init__(
|
|
83
|
+
self,
|
|
84
|
+
hourly_forecast_coordinator: HourlyForecastCoordinator,
|
|
85
|
+
daily_forecast_coordinator: DailyForecastCoordinator,
|
|
86
|
+
sensor_coordinator: MeteocatSensorCoordinator,
|
|
87
|
+
uvi_file_coordinator: MeteocatUviFileCoordinator,
|
|
88
|
+
condition_coordinator: MeteocatConditionCoordinator,
|
|
89
|
+
entry_data: dict,
|
|
90
|
+
) -> None:
|
|
91
|
+
"""Initialize the weather entity."""
|
|
92
|
+
super().__init__(daily_forecast_coordinator)
|
|
93
|
+
self._hourly_forecast_coordinator = hourly_forecast_coordinator
|
|
94
|
+
self._daily_forecast_coordinator = daily_forecast_coordinator
|
|
95
|
+
self._sensor_coordinator = sensor_coordinator
|
|
96
|
+
self._uvi_file_coordinator = uvi_file_coordinator
|
|
97
|
+
self._condition_coordinator = condition_coordinator
|
|
98
|
+
self._town_name = entry_data["town_name"]
|
|
99
|
+
self._town_id = entry_data["town_id"]
|
|
100
|
+
self._station_id = entry_data["station_id"]
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def name(self) -> str:
|
|
104
|
+
"""Return the name of the entity."""
|
|
105
|
+
return f"Weather {self._town_name}"
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def unique_id(self) -> str:
|
|
109
|
+
"""Return the unique ID of the entity."""
|
|
110
|
+
return f"weather.{DOMAIN}_{self._town_id}"
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def condition(self) -> Optional[str]:
|
|
114
|
+
"""Return the current weather condition."""
|
|
115
|
+
condition_data = self._condition_coordinator.data or {}
|
|
116
|
+
return condition_data.get("condition")
|
|
117
|
+
|
|
118
|
+
def _get_latest_sensor_value(self, code: str) -> Optional[float]:
|
|
119
|
+
"""Helper method to retrieve the latest sensor value."""
|
|
120
|
+
sensor_code = code
|
|
121
|
+
if not sensor_code:
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
stations = self._sensor_coordinator.data or []
|
|
125
|
+
for station in stations:
|
|
126
|
+
variables = station.get("variables", [])
|
|
127
|
+
variable_data = next(
|
|
128
|
+
(var for var in variables if var.get("codi") == sensor_code),
|
|
129
|
+
None,
|
|
130
|
+
)
|
|
131
|
+
if variable_data:
|
|
132
|
+
lectures = variable_data.get("lectures", [])
|
|
133
|
+
if lectures:
|
|
134
|
+
return lectures[-1].get("valor")
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def native_temperature(self) -> Optional[float]:
|
|
139
|
+
return self._get_latest_sensor_value(TEMPERATURE_CODE)
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def humidity(self) -> Optional[float]:
|
|
143
|
+
return self._get_latest_sensor_value(HUMIDITY_CODE)
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def native_pressure(self) -> Optional[float]:
|
|
147
|
+
return self._get_latest_sensor_value(PRESSURE_CODE)
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def native_wind_speed(self) -> Optional[float]:
|
|
151
|
+
return self._get_latest_sensor_value(WIND_SPEED_CODE)
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def native_wind_gust_speed(self) -> Optional[float]:
|
|
155
|
+
return self._get_latest_sensor_value(WIND_GUST_CODE)
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def wind_bearing(self) -> Optional[float]:
|
|
159
|
+
return self._get_latest_sensor_value(WIND_DIRECTION_CODE)
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def uv_index(self) -> Optional[float]:
|
|
163
|
+
"""Return the UV index."""
|
|
164
|
+
uvi_data = self._uvi_file_coordinator.data or {}
|
|
165
|
+
return uvi_data.get("uvi")
|
|
166
|
+
|
|
167
|
+
async def async_forecast_daily(self) -> list[Forecast] | None:
|
|
168
|
+
"""Return the daily forecast."""
|
|
169
|
+
await self._daily_forecast_coordinator.async_request_refresh()
|
|
170
|
+
daily_forecasts = self._daily_forecast_coordinator.get_all_daily_forecasts()
|
|
171
|
+
if not daily_forecasts:
|
|
172
|
+
return None
|
|
173
|
+
|
|
174
|
+
return [
|
|
175
|
+
Forecast(
|
|
176
|
+
datetime=forecast["date"],
|
|
177
|
+
temperature=forecast["temperature_max"],
|
|
178
|
+
templow=forecast["temperature_min"],
|
|
179
|
+
precipitation_probability=forecast["precipitation"],
|
|
180
|
+
condition=forecast["condition"],
|
|
181
|
+
)
|
|
182
|
+
for forecast in daily_forecasts
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
async def async_forecast_hourly(self) -> list[Forecast] | None:
|
|
186
|
+
"""Return the hourly forecast."""
|
|
187
|
+
await self._hourly_forecast_coordinator.async_request_refresh()
|
|
188
|
+
hourly_forecasts = self._hourly_forecast_coordinator.get_all_hourly_forecasts()
|
|
189
|
+
if not hourly_forecasts:
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
return [
|
|
193
|
+
Forecast(
|
|
194
|
+
datetime=forecast["datetime"],
|
|
195
|
+
temperature=forecast["temperature"],
|
|
196
|
+
precipitation=forecast["precipitation"],
|
|
197
|
+
condition=forecast["condition"],
|
|
198
|
+
wind_speed=forecast["wind_speed"],
|
|
199
|
+
wind_bearing=forecast["wind_bearing"],
|
|
200
|
+
humidity=forecast["humidity"],
|
|
201
|
+
)
|
|
202
|
+
for forecast in hourly_forecasts
|
|
203
|
+
]
|
|
204
|
+
|
|
205
|
+
async def async_update(self) -> None:
|
|
206
|
+
"""Update the weather entity."""
|
|
207
|
+
await self._hourly_forecast_coordinator.async_request_refresh()
|
|
208
|
+
await self._daily_forecast_coordinator.async_request_refresh()
|
|
209
|
+
|
|
210
|
+
@property
|
|
211
|
+
def device_info(self) -> DeviceInfo:
|
|
212
|
+
"""Return the device info."""
|
|
213
|
+
return DeviceInfo(
|
|
214
|
+
identifiers={(DOMAIN, self._town_id)},
|
|
215
|
+
name=f"Meteocat {self._station_id}",
|
|
216
|
+
manufacturer="Meteocat",
|
|
217
|
+
model="Meteocat API",
|
|
218
|
+
)
|
package/filetree.py
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import subprocess
|
|
2
|
-
import os
|
|
3
|
-
|
|
4
|
-
def generate_file_tree():
|
|
5
|
-
# Ruta donde se generará el archivo
|
|
6
|
-
output_file = 'filetree.txt'
|
|
7
|
-
|
|
8
|
-
# Ejecutar el comando git ls-files para obtener los archivos no ignorados
|
|
9
|
-
git_command = ['git', 'ls-files']
|
|
10
|
-
try:
|
|
11
|
-
# Captura de la salida del comando git
|
|
12
|
-
git_files = subprocess.check_output(git_command).decode('utf-8').splitlines()
|
|
13
|
-
|
|
14
|
-
# Crear una estructura de árbol
|
|
15
|
-
tree = {}
|
|
16
|
-
|
|
17
|
-
# Construir el árbol de directorios
|
|
18
|
-
for file in git_files:
|
|
19
|
-
parts = file.split('/') # Separar la ruta del archivo por '/'
|
|
20
|
-
current = tree
|
|
21
|
-
for part in parts[:-1]: # Recorremos todos los directorios
|
|
22
|
-
current = current.setdefault(part, {})
|
|
23
|
-
current[parts[-1]] = None # El archivo final se marca como 'None'
|
|
24
|
-
|
|
25
|
-
# Función recursiva para imprimir el árbol con la indentación correcta
|
|
26
|
-
def print_tree(directory, indent=""):
|
|
27
|
-
for name, subdirectory in directory.items():
|
|
28
|
-
if subdirectory is None:
|
|
29
|
-
# Si es un archivo, lo imprimimos
|
|
30
|
-
f.write(f"{indent}├── {name}\n")
|
|
31
|
-
else:
|
|
32
|
-
# Si es un directorio, lo imprimimos y recursivamente llamamos a print_tree
|
|
33
|
-
f.write(f"{indent}└── {name}/\n")
|
|
34
|
-
print_tree(subdirectory, indent + " ")
|
|
35
|
-
|
|
36
|
-
# Crear el archivo y escribir la estructura del árbol con codificación UTF-8
|
|
37
|
-
with open(output_file, 'w', encoding='utf-8') as f:
|
|
38
|
-
print_tree(tree)
|
|
39
|
-
|
|
40
|
-
print(f"Árbol de directorios generado en: {os.path.abspath(output_file)}")
|
|
41
|
-
|
|
42
|
-
except subprocess.CalledProcessError as e:
|
|
43
|
-
print(f"Error al ejecutar el comando: {e}")
|
|
44
|
-
except Exception as e:
|
|
45
|
-
print(f"Ocurrió un error inesperado: {e}")
|
|
46
|
-
|
|
47
|
-
if __name__ == "__main__":
|
|
48
|
-
generate_file_tree()
|
|
1
|
+
import subprocess
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
def generate_file_tree():
|
|
5
|
+
# Ruta donde se generará el archivo
|
|
6
|
+
output_file = 'filetree.txt'
|
|
7
|
+
|
|
8
|
+
# Ejecutar el comando git ls-files para obtener los archivos no ignorados
|
|
9
|
+
git_command = ['git', 'ls-files']
|
|
10
|
+
try:
|
|
11
|
+
# Captura de la salida del comando git
|
|
12
|
+
git_files = subprocess.check_output(git_command).decode('utf-8').splitlines()
|
|
13
|
+
|
|
14
|
+
# Crear una estructura de árbol
|
|
15
|
+
tree = {}
|
|
16
|
+
|
|
17
|
+
# Construir el árbol de directorios
|
|
18
|
+
for file in git_files:
|
|
19
|
+
parts = file.split('/') # Separar la ruta del archivo por '/'
|
|
20
|
+
current = tree
|
|
21
|
+
for part in parts[:-1]: # Recorremos todos los directorios
|
|
22
|
+
current = current.setdefault(part, {})
|
|
23
|
+
current[parts[-1]] = None # El archivo final se marca como 'None'
|
|
24
|
+
|
|
25
|
+
# Función recursiva para imprimir el árbol con la indentación correcta
|
|
26
|
+
def print_tree(directory, indent=""):
|
|
27
|
+
for name, subdirectory in directory.items():
|
|
28
|
+
if subdirectory is None:
|
|
29
|
+
# Si es un archivo, lo imprimimos
|
|
30
|
+
f.write(f"{indent}├── {name}\n")
|
|
31
|
+
else:
|
|
32
|
+
# Si es un directorio, lo imprimimos y recursivamente llamamos a print_tree
|
|
33
|
+
f.write(f"{indent}└── {name}/\n")
|
|
34
|
+
print_tree(subdirectory, indent + " ")
|
|
35
|
+
|
|
36
|
+
# Crear el archivo y escribir la estructura del árbol con codificación UTF-8
|
|
37
|
+
with open(output_file, 'w', encoding='utf-8') as f:
|
|
38
|
+
print_tree(tree)
|
|
39
|
+
|
|
40
|
+
print(f"Árbol de directorios generado en: {os.path.abspath(output_file)}")
|
|
41
|
+
|
|
42
|
+
except subprocess.CalledProcessError as e:
|
|
43
|
+
print(f"Error al ejecutar el comando: {e}")
|
|
44
|
+
except Exception as e:
|
|
45
|
+
print(f"Ocurrió un error inesperado: {e}")
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
generate_file_tree()
|