From b942074d992f8fbb2d51f4d6fb0317256c7743e6 Mon Sep 17 00:00:00 2001 From: Jeef Date: Wed, 27 Mar 2024 10:11:31 -0600 Subject: [PATCH 01/38] continue --- .../components/weatherflow_cloud/__init__.py | 2 +- .../weatherflow_cloud/coordinator.py | 5 +- .../components/weatherflow_cloud/entity.py | 38 ++ .../components/weatherflow_cloud/icons.json | 44 +++ .../components/weatherflow_cloud/sensor.py | 335 ++++++++++++++++++ .../components/weatherflow_cloud/strings.json | 68 ++++ .../weatherflow_cloud/test_sensor.py | 51 +++ 7 files changed, 540 insertions(+), 3 deletions(-) create mode 100644 homeassistant/components/weatherflow_cloud/entity.py create mode 100644 homeassistant/components/weatherflow_cloud/icons.json create mode 100644 homeassistant/components/weatherflow_cloud/sensor.py create mode 100644 tests/components/weatherflow_cloud/test_sensor.py diff --git a/homeassistant/components/weatherflow_cloud/__init__.py b/homeassistant/components/weatherflow_cloud/__init__.py index a40386100e7ff5..8dc26f9b9c6c77 100644 --- a/homeassistant/components/weatherflow_cloud/__init__.py +++ b/homeassistant/components/weatherflow_cloud/__init__.py @@ -9,7 +9,7 @@ from .const import DOMAIN from .coordinator import WeatherFlowCloudDataUpdateCoordinator -PLATFORMS: list[Platform] = [Platform.WEATHER] +PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.WEATHER] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/weatherflow_cloud/coordinator.py b/homeassistant/components/weatherflow_cloud/coordinator.py index 78b4f3be2234c2..b62307bee38ef7 100644 --- a/homeassistant/components/weatherflow_cloud/coordinator.py +++ b/homeassistant/components/weatherflow_cloud/coordinator.py @@ -21,12 +21,11 @@ class WeatherFlowCloudDataUpdateCoordinator( def __init__(self, hass: HomeAssistant, api_token: str) -> None: """Initialize global WeatherFlow forecast data updater.""" self.weather_api = WeatherFlowRestAPI(api_token=api_token) - super().__init__( hass, LOGGER, name=DOMAIN, - update_interval=timedelta(minutes=15), + update_interval=timedelta(seconds=5), ) async def _async_update_data(self) -> dict[int, WeatherFlowDataREST]: @@ -38,3 +37,5 @@ async def _async_update_data(self) -> dict[int, WeatherFlowDataREST]: if err.status == 401: raise ConfigEntryAuthFailed(err) from err raise UpdateFailed(f"Update failed: {err}") from err + except AttributeError as err: + raise UpdateFailed(f"Empty or Bad Response - Update failed: {err}") from err diff --git a/homeassistant/components/weatherflow_cloud/entity.py b/homeassistant/components/weatherflow_cloud/entity.py new file mode 100644 index 00000000000000..e5ddb94c7215e9 --- /dev/null +++ b/homeassistant/components/weatherflow_cloud/entity.py @@ -0,0 +1,38 @@ +"""Base entity class for WeatherFlow Cloud integration.""" + +from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo +from homeassistant.helpers.entity import EntityDescription +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import ATTR_ATTRIBUTION, DOMAIN, MANUFACTURER +from .coordinator import WeatherFlowCloudDataUpdateCoordinator + + +class WeatherFlowCloudEntity(CoordinatorEntity[WeatherFlowCloudDataUpdateCoordinator]): + """Base entity class to use for sensors.""" + + _attr_attribution = ATTR_ATTRIBUTION + _attr_has_entity_name = True + + def __init__( + self, + coordinator: WeatherFlowCloudDataUpdateCoordinator, + description: EntityDescription, + station_id: int, + ) -> None: + """Class initializer.""" + super().__init__(coordinator=coordinator) + self.entity_description = description + self.station_id = station_id + + station_name = self.coordinator.data[station_id].station.name + + self._attr_unique_id = f"{station_name}_cloud_{description.key}" + + self._attr_device_info = DeviceInfo( + name=self.coordinator.data[self.station_id].station.name, + entry_type=DeviceEntryType.SERVICE, + identifiers={(DOMAIN, f"{station_id}")}, + manufacturer=MANUFACTURER, + configuration_url=f"https://tempestwx.com/station/{station_id}/grid", + ) diff --git a/homeassistant/components/weatherflow_cloud/icons.json b/homeassistant/components/weatherflow_cloud/icons.json new file mode 100644 index 00000000000000..8fbd3c12c40c1f --- /dev/null +++ b/homeassistant/components/weatherflow_cloud/icons.json @@ -0,0 +1,44 @@ +{ + "entity": { + "sensor": { + "air_temperature": { + "default": "mdi:thermometer" + }, + "air_density": { + "default": "mdi:format-line-weight" + }, + "feels_like": { + "default": "mdi:thermometer" + }, + "heat_index": { + "default": "mdi:sun-thermometer" + }, + "wet_bulb_temperature": { + "default": "mdi:thermometer-water" + }, + "wet_bulb_globe_temperature": { + "default": "mdi:thermometer-water" + }, + + "lightning_strike_count": { + "default": "mdi:lightning-bolt" + }, + "lightning_strike_count_last_1hr": { + "default": "mdi:lightning-bolt" + }, + "lightning_strike_count_last_3hr": { + "default": "mdi:lightning-bolt" + }, + "lightning_strike_last_distance": { + "default": "mdi:lightning-bolt" + }, + "lightning_strike_last_epoch": { + "default": "mdi:lightning-bolt" + }, + + "wind_chill": { + "default": "mdi:snowflake-thermometer" + } + } + } +} diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py new file mode 100644 index 00000000000000..7f2e9969193ba0 --- /dev/null +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -0,0 +1,335 @@ +"""Sensors for cloud based weatherflow.""" + +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +from datetime import UTC, date, datetime +from decimal import Decimal +from typing import Any + +from weatherflow4py.models.rest.unified import WeatherFlowData + +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + DEGREE, + UnitOfLength, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import StateType + +from .const import DOMAIN +from .coordinator import WeatherFlowCloudDataUpdateCoordinator +from .entity import WeatherFlowCloudEntity + + +@dataclass(frozen=True, kw_only=True) +class WeatherFlowCloudSensorEntityDescription( + SensorEntityDescription, +): + """Describes a WF Sensor.""" + + value_fn: Callable[[WeatherFlowData], int | str | datetime | None] + icon_fn: Callable[[WeatherFlowData], str] | None = None + extra_state_attributes_fn: Callable[[WeatherFlowData], dict[str, Any]] | None = None + + +def wind_direction_icon_fn(degree: int) -> str: + """Return a wind icon based on the degrees.""" + degree = degree % 360 # Normalize degrees + direction_ranges = { + range(23): "mdi:arrow-up-thin", + range(23, 68): "mdi:arrow-top-right-thin", + range(68, 113): "mdi:arrow-right-thin", + range(113, 158): "mdi:arrow-bottom-right-thin", + range(158, 203): "mdi:arrow-down-thin", + range(203, 248): "mdi:arrow-bottom-left-thin", + range(248, 293): "mdi:arrow-left-thin", + range(293, 338): "mdi:arrow-top-left-thin", + range(338, 361): "mdi:arrow-up-thin", + } + + for degree_range, icon in direction_ranges.items(): + if degree in degree_range: + return icon + + return "mdi:compass-outline" + + +WF_SENSORS: tuple[WeatherFlowCloudSensorEntityDescription, ...] = ( + WeatherFlowCloudSensorEntityDescription( + key="air_density", + translation_key="air_density", + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=5, + value_fn=lambda data: data.observation.obs[0].air_density, + native_unit_of_measurement="kg/m³", + ), + # Temp Sensors + WeatherFlowCloudSensorEntityDescription( + key="air_temperature", + translation_key="air_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=1, + value_fn=lambda data: data.observation.obs[0].air_temperature, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + ), + WeatherFlowCloudSensorEntityDescription( + key="dew_point", + translation_key="dew_point", + value_fn=lambda data: data.observation.obs[0].dew_point, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=1, + ), + WeatherFlowCloudSensorEntityDescription( + key="feels_like", + translation_key="feels_like", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=1, + value_fn=lambda data: data.observation.obs[0].feels_like, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + ), + WeatherFlowCloudSensorEntityDescription( + key="heat_index", + translation_key="heat_index", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=1, + value_fn=lambda data: data.observation.obs[0].heat_index, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + ), + WeatherFlowCloudSensorEntityDescription( + key="wind_chill", + translation_key="wind_chill", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=1, + value_fn=lambda data: data.observation.obs[0].wind_chill, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + ), + WeatherFlowCloudSensorEntityDescription( + key="wet_bulb_temperature", + translation_key="wet_bulb_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=1, + value_fn=lambda data: data.observation.obs[0].wet_bulb_temperature, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + ), + WeatherFlowCloudSensorEntityDescription( + key="wet_bulb_globe_temperature", + translation_key="wet_bulb_globe_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=1, + value_fn=lambda data: data.observation.obs[0].wet_bulb_globe_temperature, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + extra_state_attributes_fn=lambda data: { + "flag": data.observation.obs[0].wet_bulb_globe_temperature_flag.name, + "category": data.observation.obs[0].wet_bulb_globe_temperature_category, + }, + ), + # Pressure Sensors + WeatherFlowCloudSensorEntityDescription( + key="barometric_pressure", + translation_key="barometric_pressure", + value_fn=lambda data: data.observation.obs[0].barometric_pressure, + native_unit_of_measurement=UnitOfPressure.MBAR, + device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=3, + ), + WeatherFlowCloudSensorEntityDescription( + key="sea_level_pressure", + translation_key="sea_level_pressure", + value_fn=lambda data: data.observation.obs[0].sea_level_pressure, + native_unit_of_measurement=UnitOfPressure.MBAR, + device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=3, + ), + WeatherFlowCloudSensorEntityDescription( + key="station_pressure", + translation_key="station_pressure", + value_fn=lambda data: data.observation.obs[0].station_pressure, + native_unit_of_measurement=UnitOfPressure.MBAR, + device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=3, + ), + # Wind Sensors + WeatherFlowCloudSensorEntityDescription( + key="wind_direction", + translation_key="wind_direction", + native_unit_of_measurement=DEGREE, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=0, + value_fn=lambda data: data.observation.obs[0].wind_direction, + extra_state_attributes_fn=lambda data: { + "cardinal": str(data.observation.obs[0].wind_cardinal_direction), + }, + icon_fn=lambda data: wind_direction_icon_fn( + data.observation.obs[0].wind_direction + ), + ), + WeatherFlowCloudSensorEntityDescription( + key="wind_direction_cardinal", + translation_key="wind_direction_cardinal", + device_class=SensorDeviceClass.ENUM, + options=[ + "N", + "NNE", + "NE", + "ENE", + "E", + "ESE", + "SE", + "SSE", + "S", + "SSW", + "SW", + "WSW", + "W", + "WNW", + "NW", + "NNW", + ], + value_fn=lambda data: str(data.observation.obs[0].wind_cardinal_direction), + icon_fn=lambda data: wind_direction_icon_fn( # The wind direction icon function is more precise so lets use it again here + data.observation.obs[0].wind_direction + ), + ), + WeatherFlowCloudSensorEntityDescription( + key="wind_avg", + translation_key="wind_avg", + device_class=SensorDeviceClass.WIND_SPEED, + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=2, + value_fn=lambda data: data.observation.obs[0].wind_avg, + ), + WeatherFlowCloudSensorEntityDescription( + key="wind_gust", + translation_key="wind_gust", + device_class=SensorDeviceClass.WIND_SPEED, + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=2, + value_fn=lambda data: data.observation.obs[0].wind_gust, + ), + WeatherFlowCloudSensorEntityDescription( + key="wind_lull", + translation_key="wind_lull", + device_class=SensorDeviceClass.WIND_SPEED, + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=2, + value_fn=lambda data: data.observation.obs[0].wind_lull, + ), + # Lightning Sensors + WeatherFlowCloudSensorEntityDescription( + key="lightning_strike_count", + translation_key="lightning_strike_count", + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda data: data.observation.obs[0].lightning_strike_count, + ), + WeatherFlowCloudSensorEntityDescription( + key="lightning_strike_count_last_1hr", + translation_key="lightning_strike_count_last_1hr", + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda data: data.observation.obs[0].lightning_strike_count_last_1hr, + ), + WeatherFlowCloudSensorEntityDescription( + key="lightning_strike_count_last_3hr", + translation_key="lightning_strike_count_last_3hr", + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda data: data.observation.obs[0].lightning_strike_count_last_3hr, + ), + WeatherFlowCloudSensorEntityDescription( + key="lightning_strike_last_distance", + translation_key="lightning_strike_last_distance", + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.DISTANCE, + native_unit_of_measurement=UnitOfLength.KILOMETERS, + value_fn=lambda data: data.observation.obs[0].lightning_strike_last_distance, + ), + WeatherFlowCloudSensorEntityDescription( + key="lightning_strike_last_epoch", + translation_key="lightning_strike_last_epoch", + device_class=SensorDeviceClass.TIMESTAMP, + value_fn=lambda data: datetime.fromtimestamp( + data.observation.obs[0].lightning_strike_last_epoch, tz=UTC + ), + ), +) + + +# +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up WeatherFlow sensors based on a config entry.""" + + coordinator: WeatherFlowCloudDataUpdateCoordinator = hass.data[DOMAIN][ + entry.entry_id + ] + + stations = coordinator.data.keys() + + for sensor_description in WF_SENSORS: + for station_id in stations: + async_add_entities( + [ + WeatherFlowCloudSensor( + coordinator=coordinator, + description=sensor_description, + station_id=station_id, + ) + ], + update_before_add=True, + ) + + +class WeatherFlowCloudSensor(WeatherFlowCloudEntity, SensorEntity): + """Implementation of a WeatherFlow sensor.""" + + entity_description: WeatherFlowCloudSensorEntityDescription + + @property + def native_value(self) -> StateType | date | datetime | Decimal | None: + """Return the state of the sensor.""" + return self.entity_description.value_fn(self.coordinator.data[self.station_id]) + + @property + def extra_state_attributes(self) -> dict[str, str] | None: + """Return the state attributes.""" + if self.entity_description.extra_state_attributes_fn: + return self.entity_description.extra_state_attributes_fn( + self.coordinator.data[self.station_id] + ) + return None + + @property + def icon(self) -> str | None: + """Return the icon to use in the frontend, if any.""" + if self.entity_description.icon_fn: + return self.entity_description.icon_fn( + self.coordinator.data[self.station_id] + ) + return super().icon diff --git a/homeassistant/components/weatherflow_cloud/strings.json b/homeassistant/components/weatherflow_cloud/strings.json index 782b0dcf960068..173ef11410206d 100644 --- a/homeassistant/components/weatherflow_cloud/strings.json +++ b/homeassistant/components/weatherflow_cloud/strings.json @@ -23,5 +23,73 @@ "already_configured": "[%key:common::config_flow::abort::already_configured_service%]", "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" } + }, + "entity": { + "sensor": { + "air_temperature": { + "name": "[%key:component::sensor::entity_component::temperature::name%]" + }, + "air_density": { + "name": "Air density" + }, + "barometric_pressure": { + "name": "Pressure barometric" + }, + "sea_level_pressure": { + "name": "Pressure sea level" + }, + "station_pressure": { + "name": "Pressure station" + }, + "dew_point": { + "name": "Dew point" + }, + "lightning_strike_count": { + "name": "Lightning count" + }, + "lightning_strike_count_last_1hr": { + "name": "Lightning count last 1 hr" + }, + "lightning_strike_count_last_3hr": { + "name": "Lightning count last 3 hr" + }, + "lightning_strike_last_distance": { + "name": "Lightning last distance" + }, + "lightning_strike_last_epoch": { + "name": "Lightning last" + }, + + "wind_avg": { + "name": "Wind speed" + }, + "wind_chill": { + "name": "Wind chill" + }, + "wind_direction": { + "name": "Wind direction" + }, + "wind_direction_cardinal": { + "name": "Wind direction (cardinal)" + }, + "wind_gust": { + "name": "Wind gust" + }, + "wind_lull": { + "name": "Wind lull" + }, + "feels_like": { + "name": "Feels like" + }, + "heat_index": { + "name": "Heat index" + }, + "wet_bulb_temperature": { + "name": "Wet bulb temperature" + }, + "wet_bulb_globe_temperature": { + "name": "Wet bulb globe temperature" + } + } } } diff --git a/tests/components/weatherflow_cloud/test_sensor.py b/tests/components/weatherflow_cloud/test_sensor.py new file mode 100644 index 00000000000000..176c868cb472f3 --- /dev/null +++ b/tests/components/weatherflow_cloud/test_sensor.py @@ -0,0 +1,51 @@ +"""Testing for the icons.""" + +import pytest + +from homeassistant.components.weatherflow_cloud.sensor import wind_direction_icon_fn + + +@pytest.mark.parametrize( + "wind_direction, expected_icon", # noqa: PT006 + [ + (0, "mdi:arrow-up-thin"), + (1, "mdi:arrow-up-thin"), # Just after North transition + (5, "mdi:arrow-up-thin"), + (22, "mdi:arrow-up-thin"), # Just before North-East transition + (23, "mdi:arrow-top-right-thin"), + (24, "mdi:arrow-top-right-thin"), # Just after North-East transition + (67, "mdi:arrow-top-right-thin"), # Just before East transition + (68, "mdi:arrow-right-thin"), + (69, "mdi:arrow-right-thin"), # Just after East transition + (45, "mdi:arrow-top-right-thin"), + (90, "mdi:arrow-right-thin"), + (112, "mdi:arrow-right-thin"), # Just before South-East transition + (113, "mdi:arrow-bottom-right-thin"), + (114, "mdi:arrow-bottom-right-thin"), # Just after South-East transition + (157, "mdi:arrow-bottom-right-thin"), # Just before South transition + (158, "mdi:arrow-down-thin"), + (159, "mdi:arrow-down-thin"), # Just after South transition + (202, "mdi:arrow-down-thin"), # Just before South-West transition + (203, "mdi:arrow-bottom-left-thin"), + (204, "mdi:arrow-bottom-left-thin"), # Just after South-West transition + (247, "mdi:arrow-bottom-left-thin"), # Just before West transition + (248, "mdi:arrow-left-thin"), + (249, "mdi:arrow-left-thin"), # Just after West transition + (292, "mdi:arrow-left-thin"), # Just before North-West transition + (293, "mdi:arrow-top-left-thin"), + (294, "mdi:arrow-top-left-thin"), # Just after North-West transition + (337, "mdi:arrow-top-left-thin"), # Just before North transition + (338, "mdi:arrow-up-thin"), + (339, "mdi:arrow-up-thin"), # Just after North transition + (360, "mdi:arrow-up-thin"), # Edge case: wind direction is exactly 360 degrees + (135, "mdi:arrow-bottom-right-thin"), + (180, "mdi:arrow-down-thin"), + (225, "mdi:arrow-bottom-left-thin"), + (270, "mdi:arrow-left-thin"), + (359, "mdi:arrow-up-thin"), + ], +) +def test_wind_direction_icon_fn_and_cardinal(wind_direction, expected_icon): + """Test the cardinal directions are correct.""" + direction_icon = wind_direction_icon_fn(wind_direction) + assert direction_icon == expected_icon From f28a6dea0e7752bee2c5032aaef7c6b7ad8ba77c Mon Sep 17 00:00:00 2001 From: Jeef Date: Wed, 27 Mar 2024 10:14:09 -0600 Subject: [PATCH 02/38] Rebase dev --- homeassistant/components/weatherflow_cloud/sensor.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index 7f2e9969193ba0..132389bb4dc729 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -8,7 +8,7 @@ from decimal import Decimal from typing import Any -from weatherflow4py.models.rest.unified import WeatherFlowData +from weatherflow4py.models.rest.unified import WeatherFlowDataREST from homeassistant.components.sensor import ( SensorDeviceClass, @@ -39,9 +39,11 @@ class WeatherFlowCloudSensorEntityDescription( ): """Describes a WF Sensor.""" - value_fn: Callable[[WeatherFlowData], int | str | datetime | None] - icon_fn: Callable[[WeatherFlowData], str] | None = None - extra_state_attributes_fn: Callable[[WeatherFlowData], dict[str, Any]] | None = None + value_fn: Callable[[WeatherFlowDataREST], int | str | datetime | None] + icon_fn: Callable[[WeatherFlowDataREST], str] | None = None + extra_state_attributes_fn: ( + Callable[[WeatherFlowDataREST], dict[str, Any]] | None + ) = None def wind_direction_icon_fn(degree: int) -> str: From a0e0be2474f0552e4041edb99ed47a3bc2d1974e Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Mon, 20 May 2024 11:03:56 -0600 Subject: [PATCH 03/38] signle function to generate attr_entity info --- .../components/weatherflow_cloud/entity.py | 19 ++++++++++++------- .../components/weatherflow_cloud/weather.py | 10 +++------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/entity.py b/homeassistant/components/weatherflow_cloud/entity.py index e5ddb94c7215e9..d9298a1decf4fe 100644 --- a/homeassistant/components/weatherflow_cloud/entity.py +++ b/homeassistant/components/weatherflow_cloud/entity.py @@ -8,6 +8,17 @@ from .coordinator import WeatherFlowCloudDataUpdateCoordinator +def get_station_device_info(station_name: str, station_id: int) -> DeviceInfo: + """Generate attr_device_info from station name/id..""" + return DeviceInfo( + name=station_name, + entry_type=DeviceEntryType.SERVICE, + identifiers={(DOMAIN, f"{station_id}")}, + manufacturer=MANUFACTURER, + configuration_url=f"https://tempestwx.com/station/{station_id}/grid", + ) + + class WeatherFlowCloudEntity(CoordinatorEntity[WeatherFlowCloudDataUpdateCoordinator]): """Base entity class to use for sensors.""" @@ -29,10 +40,4 @@ def __init__( self._attr_unique_id = f"{station_name}_cloud_{description.key}" - self._attr_device_info = DeviceInfo( - name=self.coordinator.data[self.station_id].station.name, - entry_type=DeviceEntryType.SERVICE, - identifiers={(DOMAIN, f"{station_id}")}, - manufacturer=MANUFACTURER, - configuration_url=f"https://tempestwx.com/station/{station_id}/grid", - ) + self._attr_device_info = get_station_device_info(station_name, station_id) diff --git a/homeassistant/components/weatherflow_cloud/weather.py b/homeassistant/components/weatherflow_cloud/weather.py index 424a4df4c8ea0c..c7182c0c90c0c3 100644 --- a/homeassistant/components/weatherflow_cloud/weather.py +++ b/homeassistant/components/weatherflow_cloud/weather.py @@ -17,11 +17,11 @@ UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ATTR_ATTRIBUTION, DOMAIN, MANUFACTURER, STATE_MAP from .coordinator import WeatherFlowCloudDataUpdateCoordinator +from .entity import get_station_device_info async def async_setup_entry( @@ -70,12 +70,8 @@ def __init__( self.station_id = station_id self._attr_unique_id = f"weatherflow_forecast_{station_id}" - self._attr_device_info = DeviceInfo( - name=self.local_data.station.name, - entry_type=DeviceEntryType.SERVICE, - identifiers={(DOMAIN, f"{station_id}")}, - manufacturer=MANUFACTURER, - configuration_url=f"https://tempestwx.com/station/{station_id}/grid", + self._attr_device_info = get_station_device_info( + self.local_data.station.name, station_id ) @property From 1ed702d5d5a7d4715207ae9629a0f10eaf05e585 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Mon, 20 May 2024 11:15:02 -0600 Subject: [PATCH 04/38] rewrite icon determination as an if block --- .../components/weatherflow_cloud/sensor.py | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index 132389bb4dc729..945f8483a9af49 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -49,23 +49,25 @@ class WeatherFlowCloudSensorEntityDescription( def wind_direction_icon_fn(degree: int) -> str: """Return a wind icon based on the degrees.""" degree = degree % 360 # Normalize degrees - direction_ranges = { - range(23): "mdi:arrow-up-thin", - range(23, 68): "mdi:arrow-top-right-thin", - range(68, 113): "mdi:arrow-right-thin", - range(113, 158): "mdi:arrow-bottom-right-thin", - range(158, 203): "mdi:arrow-down-thin", - range(203, 248): "mdi:arrow-bottom-left-thin", - range(248, 293): "mdi:arrow-left-thin", - range(293, 338): "mdi:arrow-top-left-thin", - range(338, 361): "mdi:arrow-up-thin", - } - for degree_range, icon in direction_ranges.items(): - if degree in degree_range: - return icon + if degree < 23: + return "mdi:arrow-up-thin" + if degree < 68: + return "mdi:arrow-top-right-thin" + if degree < 113: + return "mdi:arrow-right-thin" + if degree < 158: + return "mdi:arrow-bottom-right-thin" + if degree < 203: + return "mdi:arrow-down-thin" + if degree < 248: + return "mdi:arrow-bottom-left-thin" + if degree < 293: + return "mdi:arrow-left-thin" + if degree < 338: + return "mdi:arrow-top-left-thin" - return "mdi:compass-outline" + return "mdi:arrow-up-thin" WF_SENSORS: tuple[WeatherFlowCloudSensorEntityDescription, ...] = ( From a3f5790f2b06ecbe86fce57569fe867f05a6c4c5 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Mon, 20 May 2024 13:52:12 -0600 Subject: [PATCH 05/38] handling PR --- homeassistant/components/weatherflow_cloud/sensor.py | 10 ++++------ tests/components/weatherflow_cloud/test_sensor.py | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index 945f8483a9af49..b98c907837afb4 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -46,8 +46,8 @@ class WeatherFlowCloudSensorEntityDescription( ) = None -def wind_direction_icon_fn(degree: int) -> str: - """Return a wind icon based on the degrees.""" +def wind_angle_icon_fn(degree: int) -> str: + """Return a wind icon based on the current direction (in degrees) the wind is blowing.""" degree = degree % 360 # Normalize degrees if degree < 23: @@ -186,9 +186,7 @@ def wind_direction_icon_fn(degree: int) -> str: extra_state_attributes_fn=lambda data: { "cardinal": str(data.observation.obs[0].wind_cardinal_direction), }, - icon_fn=lambda data: wind_direction_icon_fn( - data.observation.obs[0].wind_direction - ), + icon_fn=lambda data: wind_angle_icon_fn(data.observation.obs[0].wind_direction), ), WeatherFlowCloudSensorEntityDescription( key="wind_direction_cardinal", @@ -213,7 +211,7 @@ def wind_direction_icon_fn(degree: int) -> str: "NNW", ], value_fn=lambda data: str(data.observation.obs[0].wind_cardinal_direction), - icon_fn=lambda data: wind_direction_icon_fn( # The wind direction icon function is more precise so lets use it again here + icon_fn=lambda data: wind_angle_icon_fn( # The wind direction icon function is more precise so lets use it again here data.observation.obs[0].wind_direction ), ), diff --git a/tests/components/weatherflow_cloud/test_sensor.py b/tests/components/weatherflow_cloud/test_sensor.py index 176c868cb472f3..a933f52670b27a 100644 --- a/tests/components/weatherflow_cloud/test_sensor.py +++ b/tests/components/weatherflow_cloud/test_sensor.py @@ -2,7 +2,7 @@ import pytest -from homeassistant.components.weatherflow_cloud.sensor import wind_direction_icon_fn +from homeassistant.components.weatherflow_cloud.sensor import wind_angle_icon_fn @pytest.mark.parametrize( @@ -47,5 +47,5 @@ ) def test_wind_direction_icon_fn_and_cardinal(wind_direction, expected_icon): """Test the cardinal directions are correct.""" - direction_icon = wind_direction_icon_fn(wind_direction) + direction_icon = wind_angle_icon_fn(wind_direction) assert direction_icon == expected_icon From d30b6645eb0f9847b9e4e1786f4ea183205d9e17 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Mon, 10 Jun 2024 19:25:47 -0600 Subject: [PATCH 06/38] Removing wind sensors for now - separate future PR --- .../weatherflow_cloud/coordinator.py | 2 +- .../components/weatherflow_cloud/sensor.py | 99 +------------------ .../weatherflow_cloud/test_sensor.py | 51 ---------- 3 files changed, 2 insertions(+), 150 deletions(-) delete mode 100644 tests/components/weatherflow_cloud/test_sensor.py diff --git a/homeassistant/components/weatherflow_cloud/coordinator.py b/homeassistant/components/weatherflow_cloud/coordinator.py index b62307bee38ef7..b1d7f26b246326 100644 --- a/homeassistant/components/weatherflow_cloud/coordinator.py +++ b/homeassistant/components/weatherflow_cloud/coordinator.py @@ -25,7 +25,7 @@ def __init__(self, hass: HomeAssistant, api_token: str) -> None: hass, LOGGER, name=DOMAIN, - update_interval=timedelta(seconds=5), + update_interval=timedelta(seconds=60), ) async def _async_update_data(self) -> dict[int, WeatherFlowDataREST]: diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index b98c907837afb4..6a5702aa5aa365 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -17,13 +17,7 @@ SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - DEGREE, - UnitOfLength, - UnitOfPressure, - UnitOfSpeed, - UnitOfTemperature, -) +from homeassistant.const import UnitOfLength, UnitOfPressure, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType @@ -46,30 +40,6 @@ class WeatherFlowCloudSensorEntityDescription( ) = None -def wind_angle_icon_fn(degree: int) -> str: - """Return a wind icon based on the current direction (in degrees) the wind is blowing.""" - degree = degree % 360 # Normalize degrees - - if degree < 23: - return "mdi:arrow-up-thin" - if degree < 68: - return "mdi:arrow-top-right-thin" - if degree < 113: - return "mdi:arrow-right-thin" - if degree < 158: - return "mdi:arrow-bottom-right-thin" - if degree < 203: - return "mdi:arrow-down-thin" - if degree < 248: - return "mdi:arrow-bottom-left-thin" - if degree < 293: - return "mdi:arrow-left-thin" - if degree < 338: - return "mdi:arrow-top-left-thin" - - return "mdi:arrow-up-thin" - - WF_SENSORS: tuple[WeatherFlowCloudSensorEntityDescription, ...] = ( WeatherFlowCloudSensorEntityDescription( key="air_density", @@ -175,73 +145,6 @@ def wind_angle_icon_fn(degree: int) -> str: state_class=SensorStateClass.MEASUREMENT, suggested_display_precision=3, ), - # Wind Sensors - WeatherFlowCloudSensorEntityDescription( - key="wind_direction", - translation_key="wind_direction", - native_unit_of_measurement=DEGREE, - state_class=SensorStateClass.MEASUREMENT, - suggested_display_precision=0, - value_fn=lambda data: data.observation.obs[0].wind_direction, - extra_state_attributes_fn=lambda data: { - "cardinal": str(data.observation.obs[0].wind_cardinal_direction), - }, - icon_fn=lambda data: wind_angle_icon_fn(data.observation.obs[0].wind_direction), - ), - WeatherFlowCloudSensorEntityDescription( - key="wind_direction_cardinal", - translation_key="wind_direction_cardinal", - device_class=SensorDeviceClass.ENUM, - options=[ - "N", - "NNE", - "NE", - "ENE", - "E", - "ESE", - "SE", - "SSE", - "S", - "SSW", - "SW", - "WSW", - "W", - "WNW", - "NW", - "NNW", - ], - value_fn=lambda data: str(data.observation.obs[0].wind_cardinal_direction), - icon_fn=lambda data: wind_angle_icon_fn( # The wind direction icon function is more precise so lets use it again here - data.observation.obs[0].wind_direction - ), - ), - WeatherFlowCloudSensorEntityDescription( - key="wind_avg", - translation_key="wind_avg", - device_class=SensorDeviceClass.WIND_SPEED, - native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, - state_class=SensorStateClass.MEASUREMENT, - suggested_display_precision=2, - value_fn=lambda data: data.observation.obs[0].wind_avg, - ), - WeatherFlowCloudSensorEntityDescription( - key="wind_gust", - translation_key="wind_gust", - device_class=SensorDeviceClass.WIND_SPEED, - native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, - state_class=SensorStateClass.MEASUREMENT, - suggested_display_precision=2, - value_fn=lambda data: data.observation.obs[0].wind_gust, - ), - WeatherFlowCloudSensorEntityDescription( - key="wind_lull", - translation_key="wind_lull", - device_class=SensorDeviceClass.WIND_SPEED, - native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, - state_class=SensorStateClass.MEASUREMENT, - suggested_display_precision=2, - value_fn=lambda data: data.observation.obs[0].wind_lull, - ), # Lightning Sensors WeatherFlowCloudSensorEntityDescription( key="lightning_strike_count", diff --git a/tests/components/weatherflow_cloud/test_sensor.py b/tests/components/weatherflow_cloud/test_sensor.py deleted file mode 100644 index a933f52670b27a..00000000000000 --- a/tests/components/weatherflow_cloud/test_sensor.py +++ /dev/null @@ -1,51 +0,0 @@ -"""Testing for the icons.""" - -import pytest - -from homeassistant.components.weatherflow_cloud.sensor import wind_angle_icon_fn - - -@pytest.mark.parametrize( - "wind_direction, expected_icon", # noqa: PT006 - [ - (0, "mdi:arrow-up-thin"), - (1, "mdi:arrow-up-thin"), # Just after North transition - (5, "mdi:arrow-up-thin"), - (22, "mdi:arrow-up-thin"), # Just before North-East transition - (23, "mdi:arrow-top-right-thin"), - (24, "mdi:arrow-top-right-thin"), # Just after North-East transition - (67, "mdi:arrow-top-right-thin"), # Just before East transition - (68, "mdi:arrow-right-thin"), - (69, "mdi:arrow-right-thin"), # Just after East transition - (45, "mdi:arrow-top-right-thin"), - (90, "mdi:arrow-right-thin"), - (112, "mdi:arrow-right-thin"), # Just before South-East transition - (113, "mdi:arrow-bottom-right-thin"), - (114, "mdi:arrow-bottom-right-thin"), # Just after South-East transition - (157, "mdi:arrow-bottom-right-thin"), # Just before South transition - (158, "mdi:arrow-down-thin"), - (159, "mdi:arrow-down-thin"), # Just after South transition - (202, "mdi:arrow-down-thin"), # Just before South-West transition - (203, "mdi:arrow-bottom-left-thin"), - (204, "mdi:arrow-bottom-left-thin"), # Just after South-West transition - (247, "mdi:arrow-bottom-left-thin"), # Just before West transition - (248, "mdi:arrow-left-thin"), - (249, "mdi:arrow-left-thin"), # Just after West transition - (292, "mdi:arrow-left-thin"), # Just before North-West transition - (293, "mdi:arrow-top-left-thin"), - (294, "mdi:arrow-top-left-thin"), # Just after North-West transition - (337, "mdi:arrow-top-left-thin"), # Just before North transition - (338, "mdi:arrow-up-thin"), - (339, "mdi:arrow-up-thin"), # Just after North transition - (360, "mdi:arrow-up-thin"), # Edge case: wind direction is exactly 360 degrees - (135, "mdi:arrow-bottom-right-thin"), - (180, "mdi:arrow-down-thin"), - (225, "mdi:arrow-bottom-left-thin"), - (270, "mdi:arrow-left-thin"), - (359, "mdi:arrow-up-thin"), - ], -) -def test_wind_direction_icon_fn_and_cardinal(wind_direction, expected_icon): - """Test the cardinal directions are correct.""" - direction_icon = wind_angle_icon_fn(wind_direction) - assert direction_icon == expected_icon From d2c8c4105faaf3d50b8530654cbaa48bcd39dbcb Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Tue, 18 Jun 2024 14:55:06 -0600 Subject: [PATCH 07/38] ruff --- homeassistant/components/weatherflow_cloud/weather.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/weatherflow_cloud/weather.py b/homeassistant/components/weatherflow_cloud/weather.py index c7182c0c90c0c3..01160e6fc9e1ef 100644 --- a/homeassistant/components/weatherflow_cloud/weather.py +++ b/homeassistant/components/weatherflow_cloud/weather.py @@ -19,7 +19,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import ATTR_ATTRIBUTION, DOMAIN, MANUFACTURER, STATE_MAP +from .const import ATTR_ATTRIBUTION, DOMAIN, STATE_MAP from .coordinator import WeatherFlowCloudDataUpdateCoordinator from .entity import get_station_device_info From e4ca8bd97909f9a2ae29007679666eba38cb5b68 Mon Sep 17 00:00:00 2001 From: Jeef Date: Thu, 20 Jun 2024 14:58:31 -0600 Subject: [PATCH 08/38] Update coordinator.py Thought i already did this --- homeassistant/components/weatherflow_cloud/coordinator.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/coordinator.py b/homeassistant/components/weatherflow_cloud/coordinator.py index b1d7f26b246326..8b8a916262fb70 100644 --- a/homeassistant/components/weatherflow_cloud/coordinator.py +++ b/homeassistant/components/weatherflow_cloud/coordinator.py @@ -37,5 +37,3 @@ async def _async_update_data(self) -> dict[int, WeatherFlowDataREST]: if err.status == 401: raise ConfigEntryAuthFailed(err) from err raise UpdateFailed(f"Update failed: {err}") from err - except AttributeError as err: - raise UpdateFailed(f"Empty or Bad Response - Update failed: {err}") from err From d55a83ba079985d9f21faef198f0e5816b5e4376 Mon Sep 17 00:00:00 2001 From: Jeef Date: Thu, 20 Jun 2024 15:00:10 -0600 Subject: [PATCH 09/38] Update sensor.py --- homeassistant/components/weatherflow_cloud/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index 6a5702aa5aa365..cd8f3eaee8bc50 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -31,7 +31,7 @@ class WeatherFlowCloudSensorEntityDescription( SensorEntityDescription, ): - """Describes a WF Sensor.""" + """Describes a weatherflow sensor.""" value_fn: Callable[[WeatherFlowDataREST], int | str | datetime | None] icon_fn: Callable[[WeatherFlowDataREST], str] | None = None From 225272402899aaf18bfcaca145d9c96241ce0e1e Mon Sep 17 00:00:00 2001 From: Jeef Date: Thu, 20 Jun 2024 15:00:59 -0600 Subject: [PATCH 10/38] Update icons.json --- homeassistant/components/weatherflow_cloud/icons.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/icons.json b/homeassistant/components/weatherflow_cloud/icons.json index 8fbd3c12c40c1f..19e6ac56821146 100644 --- a/homeassistant/components/weatherflow_cloud/icons.json +++ b/homeassistant/components/weatherflow_cloud/icons.json @@ -19,7 +19,6 @@ "wet_bulb_globe_temperature": { "default": "mdi:thermometer-water" }, - "lightning_strike_count": { "default": "mdi:lightning-bolt" }, @@ -35,7 +34,6 @@ "lightning_strike_last_epoch": { "default": "mdi:lightning-bolt" }, - "wind_chill": { "default": "mdi:snowflake-thermometer" } From a0f354b4780f9e34b5b7bab087485bd2eb59cd69 Mon Sep 17 00:00:00 2001 From: Jeef Date: Thu, 20 Jun 2024 15:01:27 -0600 Subject: [PATCH 11/38] Update sensor.py --- homeassistant/components/weatherflow_cloud/sensor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index cd8f3eaee8bc50..4f8e45a995d0bb 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -183,7 +183,6 @@ class WeatherFlowCloudSensorEntityDescription( ) -# async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, From f3b7a085634a2d79ca77b503c089ed6d89ae30c0 Mon Sep 17 00:00:00 2001 From: Jeef Date: Fri, 5 Jul 2024 09:15:54 -0600 Subject: [PATCH 12/38] Update homeassistant/components/weatherflow_cloud/entity.py Co-authored-by: Joost Lekkerkerker --- homeassistant/components/weatherflow_cloud/entity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/weatherflow_cloud/entity.py b/homeassistant/components/weatherflow_cloud/entity.py index d9298a1decf4fe..c09b1741176133 100644 --- a/homeassistant/components/weatherflow_cloud/entity.py +++ b/homeassistant/components/weatherflow_cloud/entity.py @@ -13,7 +13,7 @@ def get_station_device_info(station_name: str, station_id: int) -> DeviceInfo: return DeviceInfo( name=station_name, entry_type=DeviceEntryType.SERVICE, - identifiers={(DOMAIN, f"{station_id}")}, + identifiers={(DOMAIN, str(station_id))}, manufacturer=MANUFACTURER, configuration_url=f"https://tempestwx.com/station/{station_id}/grid", ) From 64a9e2d9ae8bc228a72b1404f43fe8ee34b26ea9 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Fri, 5 Jul 2024 10:04:11 -0600 Subject: [PATCH 13/38] working on a unified entity --- .../components/weatherflow_cloud/entity.py | 24 +++++++++----- .../components/weatherflow_cloud/sensor.py | 18 ++++++++--- .../components/weatherflow_cloud/weather.py | 31 ++++++++++++------- 3 files changed, 50 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/entity.py b/homeassistant/components/weatherflow_cloud/entity.py index c09b1741176133..80252d675ac83d 100644 --- a/homeassistant/components/weatherflow_cloud/entity.py +++ b/homeassistant/components/weatherflow_cloud/entity.py @@ -26,10 +26,11 @@ class WeatherFlowCloudEntity(CoordinatorEntity[WeatherFlowCloudDataUpdateCoordin _attr_has_entity_name = True def __init__( - self, - coordinator: WeatherFlowCloudDataUpdateCoordinator, - description: EntityDescription, - station_id: int, + self, + coordinator: WeatherFlowCloudDataUpdateCoordinator, + description: EntityDescription, + station_id: int, + is_sensor: bool = True ) -> None: """Class initializer.""" super().__init__(coordinator=coordinator) @@ -38,6 +39,15 @@ def __init__( station_name = self.coordinator.data[station_id].station.name - self._attr_unique_id = f"{station_name}_cloud_{description.key}" - - self._attr_device_info = get_station_device_info(station_name, station_id) + if is_sensor: + self._attr_unique_id = f"{station_name}_{description.key}" + else: + self._attr_unique_id = f"weatherflow_forecast_{station_id}" + + self._attr_device_info = DeviceInfo( + name=station_name, + entry_type=DeviceEntryType.SERVICE, + identifiers={(DOMAIN, str(station_id))}, + manufacturer=MANUFACTURER, + configuration_url=f"https://tempestwx.com/station/{station_id}/grid", + ) \ No newline at end of file diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index 4f8e45a995d0bb..d596d70ebe81c5 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -36,7 +36,7 @@ class WeatherFlowCloudSensorEntityDescription( value_fn: Callable[[WeatherFlowDataREST], int | str | datetime | None] icon_fn: Callable[[WeatherFlowDataREST], str] | None = None extra_state_attributes_fn: ( - Callable[[WeatherFlowDataREST], dict[str, Any]] | None + Callable[[WeatherFlowDataREST], dict[str, Any]] | None ) = None @@ -184,9 +184,9 @@ class WeatherFlowCloudSensorEntityDescription( async def async_setup_entry( - hass: HomeAssistant, - entry: ConfigEntry, - async_add_entities: AddEntitiesCallback, + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, ) -> None: """Set up WeatherFlow sensors based on a config entry.""" @@ -206,7 +206,6 @@ async def async_setup_entry( station_id=station_id, ) ], - update_before_add=True, ) @@ -215,6 +214,15 @@ class WeatherFlowCloudSensor(WeatherFlowCloudEntity, SensorEntity): entity_description: WeatherFlowCloudSensorEntityDescription + def __init__(self, coordinator, description, station_id, station_name, unique_id): + """Initialize the sensor.""" + # Initialize the Entity Class + super().__init__(coordinator=coordinator, + description=description, + station_id=station_id, + is_sensor=True + ) + @property def native_value(self) -> StateType | date | datetime | Decimal | None: """Return the state of the sensor.""" diff --git a/homeassistant/components/weatherflow_cloud/weather.py b/homeassistant/components/weatherflow_cloud/weather.py index 01160e6fc9e1ef..ec7113067b6951 100644 --- a/homeassistant/components/weatherflow_cloud/weather.py +++ b/homeassistant/components/weatherflow_cloud/weather.py @@ -17,17 +17,21 @@ UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import ATTR_ATTRIBUTION, DOMAIN, STATE_MAP +from .const import ATTR_ATTRIBUTION, DOMAIN, MANUFACTURER, STATE_MAP from .coordinator import WeatherFlowCloudDataUpdateCoordinator -from .entity import get_station_device_info +<<<<<<< Updated upstream +======= +from .entity import get_station_device_info, WeatherFlowCloudEntity +>>>>>>> Stashed changes async def async_setup_entry( - hass: HomeAssistant, - config_entry: ConfigEntry, - async_add_entities: AddEntitiesCallback, + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, ) -> None: """Add a weather entity from a config_entry.""" coordinator: WeatherFlowCloudDataUpdateCoordinator = hass.data[DOMAIN][ @@ -43,6 +47,7 @@ async def async_setup_entry( class WeatherFlowWeather( + WeatherFlowCloudEntity, SingleCoordinatorWeatherEntity[WeatherFlowCloudDataUpdateCoordinator] ): """Implementation of a WeatherFlow weather condition.""" @@ -55,14 +60,14 @@ class WeatherFlowWeather( _attr_native_pressure_unit = UnitOfPressure.MBAR _attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND _attr_supported_features = ( - WeatherEntityFeature.FORECAST_DAILY | WeatherEntityFeature.FORECAST_HOURLY + WeatherEntityFeature.FORECAST_DAILY | WeatherEntityFeature.FORECAST_HOURLY ) _attr_name = None def __init__( - self, - coordinator: WeatherFlowCloudDataUpdateCoordinator, - station_id: int, + self, + coordinator: WeatherFlowCloudDataUpdateCoordinator, + station_id: int, ) -> None: """Initialise the platform with a data instance and station.""" super().__init__(coordinator) @@ -70,8 +75,12 @@ def __init__( self.station_id = station_id self._attr_unique_id = f"weatherflow_forecast_{station_id}" - self._attr_device_info = get_station_device_info( - self.local_data.station.name, station_id + self._attr_device_info = DeviceInfo( + name=self.local_data.station.name, + entry_type=DeviceEntryType.SERVICE, + identifiers={(DOMAIN, f"{station_id}")}, + manufacturer=MANUFACTURER, + configuration_url=f"https://tempestwx.com/station/{station_id}/grid", ) @property From 72cd6db0b06073bc60954a9f9d1b82b131b8fa97 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Fri, 5 Jul 2024 10:04:20 -0600 Subject: [PATCH 14/38] working on a unified entity --- .../components/weatherflow_cloud/entity.py | 12 ++++++------ .../components/weatherflow_cloud/sensor.py | 19 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/entity.py b/homeassistant/components/weatherflow_cloud/entity.py index 80252d675ac83d..3c1fd7da782668 100644 --- a/homeassistant/components/weatherflow_cloud/entity.py +++ b/homeassistant/components/weatherflow_cloud/entity.py @@ -26,11 +26,11 @@ class WeatherFlowCloudEntity(CoordinatorEntity[WeatherFlowCloudDataUpdateCoordin _attr_has_entity_name = True def __init__( - self, - coordinator: WeatherFlowCloudDataUpdateCoordinator, - description: EntityDescription, - station_id: int, - is_sensor: bool = True + self, + coordinator: WeatherFlowCloudDataUpdateCoordinator, + description: EntityDescription, + station_id: int, + is_sensor: bool = True, ) -> None: """Class initializer.""" super().__init__(coordinator=coordinator) @@ -50,4 +50,4 @@ def __init__( identifiers={(DOMAIN, str(station_id))}, manufacturer=MANUFACTURER, configuration_url=f"https://tempestwx.com/station/{station_id}/grid", - ) \ No newline at end of file + ) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index d596d70ebe81c5..a501dfbcad0f7e 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -36,7 +36,7 @@ class WeatherFlowCloudSensorEntityDescription( value_fn: Callable[[WeatherFlowDataREST], int | str | datetime | None] icon_fn: Callable[[WeatherFlowDataREST], str] | None = None extra_state_attributes_fn: ( - Callable[[WeatherFlowDataREST], dict[str, Any]] | None + Callable[[WeatherFlowDataREST], dict[str, Any]] | None ) = None @@ -184,9 +184,9 @@ class WeatherFlowCloudSensorEntityDescription( async def async_setup_entry( - hass: HomeAssistant, - entry: ConfigEntry, - async_add_entities: AddEntitiesCallback, + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, ) -> None: """Set up WeatherFlow sensors based on a config entry.""" @@ -217,11 +217,12 @@ class WeatherFlowCloudSensor(WeatherFlowCloudEntity, SensorEntity): def __init__(self, coordinator, description, station_id, station_name, unique_id): """Initialize the sensor.""" # Initialize the Entity Class - super().__init__(coordinator=coordinator, - description=description, - station_id=station_id, - is_sensor=True - ) + super().__init__( + coordinator=coordinator, + description=description, + station_id=station_id, + is_sensor=True, + ) @property def native_value(self) -> StateType | date | datetime | Decimal | None: From 66f7ef4e2555ceb92c0ccea0fd74c8bdc11ec16e Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Fri, 5 Jul 2024 11:49:51 -0600 Subject: [PATCH 15/38] sensor refactor --- .../components/weatherflow_cloud/entity.py | 21 +--------- .../weatherflow_cloud/manifest.json | 1 + .../components/weatherflow_cloud/sensor.py | 38 ++++--------------- .../components/weatherflow_cloud/strings.json | 6 --- .../components/weatherflow_cloud/weather.py | 23 +++++------ 5 files changed, 20 insertions(+), 69 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/entity.py b/homeassistant/components/weatherflow_cloud/entity.py index 3c1fd7da782668..f2e0257b78c54d 100644 --- a/homeassistant/components/weatherflow_cloud/entity.py +++ b/homeassistant/components/weatherflow_cloud/entity.py @@ -1,24 +1,12 @@ """Base entity class for WeatherFlow Cloud integration.""" from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo -from homeassistant.helpers.entity import EntityDescription from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import ATTR_ATTRIBUTION, DOMAIN, MANUFACTURER from .coordinator import WeatherFlowCloudDataUpdateCoordinator -def get_station_device_info(station_name: str, station_id: int) -> DeviceInfo: - """Generate attr_device_info from station name/id..""" - return DeviceInfo( - name=station_name, - entry_type=DeviceEntryType.SERVICE, - identifiers={(DOMAIN, str(station_id))}, - manufacturer=MANUFACTURER, - configuration_url=f"https://tempestwx.com/station/{station_id}/grid", - ) - - class WeatherFlowCloudEntity(CoordinatorEntity[WeatherFlowCloudDataUpdateCoordinator]): """Base entity class to use for sensors.""" @@ -28,22 +16,15 @@ class WeatherFlowCloudEntity(CoordinatorEntity[WeatherFlowCloudDataUpdateCoordin def __init__( self, coordinator: WeatherFlowCloudDataUpdateCoordinator, - description: EntityDescription, station_id: int, - is_sensor: bool = True, ) -> None: """Class initializer.""" super().__init__(coordinator=coordinator) - self.entity_description = description + # self.entity_description = description self.station_id = station_id station_name = self.coordinator.data[station_id].station.name - if is_sensor: - self._attr_unique_id = f"{station_name}_{description.key}" - else: - self._attr_unique_id = f"weatherflow_forecast_{station_id}" - self._attr_device_info = DeviceInfo( name=station_name, entry_type=DeviceEntryType.SERVICE, diff --git a/homeassistant/components/weatherflow_cloud/manifest.json b/homeassistant/components/weatherflow_cloud/manifest.json index 93df04d833c6f0..62fbdfc2aa97c6 100644 --- a/homeassistant/components/weatherflow_cloud/manifest.json +++ b/homeassistant/components/weatherflow_cloud/manifest.json @@ -5,5 +5,6 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/weatherflow_cloud", "iot_class": "cloud_polling", + "loggers": ["homeassistant.components.weatherflow_cloud", "weatherflow4py"], "requirements": ["weatherflow4py==0.2.21"] } diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index a501dfbcad0f7e..3dcc08089917e0 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -6,7 +6,6 @@ from dataclasses import dataclass from datetime import UTC, date, datetime from decimal import Decimal -from typing import Any from weatherflow4py.models.rest.unified import WeatherFlowDataREST @@ -34,10 +33,6 @@ class WeatherFlowCloudSensorEntityDescription( """Describes a weatherflow sensor.""" value_fn: Callable[[WeatherFlowDataREST], int | str | datetime | None] - icon_fn: Callable[[WeatherFlowDataREST], str] | None = None - extra_state_attributes_fn: ( - Callable[[WeatherFlowDataREST], dict[str, Any]] | None - ) = None WF_SENSORS: tuple[WeatherFlowCloudSensorEntityDescription, ...] = ( @@ -112,10 +107,6 @@ class WeatherFlowCloudSensorEntityDescription( suggested_display_precision=1, value_fn=lambda data: data.observation.obs[0].wet_bulb_globe_temperature, native_unit_of_measurement=UnitOfTemperature.CELSIUS, - extra_state_attributes_fn=lambda data: { - "flag": data.observation.obs[0].wet_bulb_globe_temperature_flag.name, - "category": data.observation.obs[0].wet_bulb_globe_temperature_category, - }, ), # Pressure Sensors WeatherFlowCloudSensorEntityDescription( @@ -214,35 +205,22 @@ class WeatherFlowCloudSensor(WeatherFlowCloudEntity, SensorEntity): entity_description: WeatherFlowCloudSensorEntityDescription - def __init__(self, coordinator, description, station_id, station_name, unique_id): + def __init__(self, coordinator, description, station_id): """Initialize the sensor.""" # Initialize the Entity Class super().__init__( coordinator=coordinator, - description=description, station_id=station_id, - is_sensor=True, ) + self.entity_description = description + self._attr_unique_id = ( + f"{self.coordinator.data[station_id].station.name}_{description.key}" + ) + self._attr_has_entity_name = True + + # @property def native_value(self) -> StateType | date | datetime | Decimal | None: """Return the state of the sensor.""" return self.entity_description.value_fn(self.coordinator.data[self.station_id]) - - @property - def extra_state_attributes(self) -> dict[str, str] | None: - """Return the state attributes.""" - if self.entity_description.extra_state_attributes_fn: - return self.entity_description.extra_state_attributes_fn( - self.coordinator.data[self.station_id] - ) - return None - - @property - def icon(self) -> str | None: - """Return the icon to use in the frontend, if any.""" - if self.entity_description.icon_fn: - return self.entity_description.icon_fn( - self.coordinator.data[self.station_id] - ) - return super().icon diff --git a/homeassistant/components/weatherflow_cloud/strings.json b/homeassistant/components/weatherflow_cloud/strings.json index 173ef11410206d..cfb2cbe97fd64d 100644 --- a/homeassistant/components/weatherflow_cloud/strings.json +++ b/homeassistant/components/weatherflow_cloud/strings.json @@ -26,9 +26,6 @@ }, "entity": { "sensor": { - "air_temperature": { - "name": "[%key:component::sensor::entity_component::temperature::name%]" - }, "air_density": { "name": "Air density" }, @@ -60,9 +57,6 @@ "name": "Lightning last" }, - "wind_avg": { - "name": "Wind speed" - }, "wind_chill": { "name": "Wind chill" }, diff --git a/homeassistant/components/weatherflow_cloud/weather.py b/homeassistant/components/weatherflow_cloud/weather.py index ec7113067b6951..8e782fc47ddd20 100644 --- a/homeassistant/components/weatherflow_cloud/weather.py +++ b/homeassistant/components/weatherflow_cloud/weather.py @@ -22,16 +22,13 @@ from .const import ATTR_ATTRIBUTION, DOMAIN, MANUFACTURER, STATE_MAP from .coordinator import WeatherFlowCloudDataUpdateCoordinator -<<<<<<< Updated upstream -======= -from .entity import get_station_device_info, WeatherFlowCloudEntity ->>>>>>> Stashed changes +from .entity import WeatherFlowCloudEntity async def async_setup_entry( - hass: HomeAssistant, - config_entry: ConfigEntry, - async_add_entities: AddEntitiesCallback, + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, ) -> None: """Add a weather entity from a config_entry.""" coordinator: WeatherFlowCloudDataUpdateCoordinator = hass.data[DOMAIN][ @@ -48,7 +45,7 @@ async def async_setup_entry( class WeatherFlowWeather( WeatherFlowCloudEntity, - SingleCoordinatorWeatherEntity[WeatherFlowCloudDataUpdateCoordinator] + SingleCoordinatorWeatherEntity[WeatherFlowCloudDataUpdateCoordinator], ): """Implementation of a WeatherFlow weather condition.""" @@ -60,17 +57,17 @@ class WeatherFlowWeather( _attr_native_pressure_unit = UnitOfPressure.MBAR _attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND _attr_supported_features = ( - WeatherEntityFeature.FORECAST_DAILY | WeatherEntityFeature.FORECAST_HOURLY + WeatherEntityFeature.FORECAST_DAILY | WeatherEntityFeature.FORECAST_HOURLY ) _attr_name = None def __init__( - self, - coordinator: WeatherFlowCloudDataUpdateCoordinator, - station_id: int, + self, + coordinator: WeatherFlowCloudDataUpdateCoordinator, + station_id: int, ) -> None: """Initialise the platform with a data instance and station.""" - super().__init__(coordinator) + super().__init__(coordinator, station_id) self.station_id = station_id self._attr_unique_id = f"weatherflow_forecast_{station_id}" From 0c3d1b4ca50ba655cd1ded2e87d379442553bcb9 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Fri, 5 Jul 2024 12:59:42 -0600 Subject: [PATCH 16/38] addressing entity comment --- homeassistant/components/weatherflow_cloud/entity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/weatherflow_cloud/entity.py b/homeassistant/components/weatherflow_cloud/entity.py index f2e0257b78c54d..42580ba3c290d2 100644 --- a/homeassistant/components/weatherflow_cloud/entity.py +++ b/homeassistant/components/weatherflow_cloud/entity.py @@ -8,7 +8,7 @@ class WeatherFlowCloudEntity(CoordinatorEntity[WeatherFlowCloudDataUpdateCoordinator]): - """Base entity class to use for sensors.""" + """Base entity class to use for everything.""" _attr_attribution = ATTR_ATTRIBUTION _attr_has_entity_name = True From 70032cc276b0440e6e93cdf470a5081582ba64de Mon Sep 17 00:00:00 2001 From: Jeef Date: Fri, 5 Jul 2024 13:00:14 -0600 Subject: [PATCH 17/38] Update homeassistant/components/weatherflow_cloud/entity.py Co-authored-by: Joost Lekkerkerker --- homeassistant/components/weatherflow_cloud/entity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/weatherflow_cloud/entity.py b/homeassistant/components/weatherflow_cloud/entity.py index 42580ba3c290d2..aea9602ae06770 100644 --- a/homeassistant/components/weatherflow_cloud/entity.py +++ b/homeassistant/components/weatherflow_cloud/entity.py @@ -19,7 +19,7 @@ def __init__( station_id: int, ) -> None: """Class initializer.""" - super().__init__(coordinator=coordinator) + super().__init__(coordinator) # self.entity_description = description self.station_id = station_id From 441f2744a4672ff41c0a3faa8d5fe2e01a79af77 Mon Sep 17 00:00:00 2001 From: Jeef Date: Fri, 5 Jul 2024 13:07:35 -0600 Subject: [PATCH 18/38] Update homeassistant/components/weatherflow_cloud/sensor.py Co-authored-by: Joost Lekkerkerker --- homeassistant/components/weatherflow_cloud/sensor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index 3dcc08089917e0..d8367f98058670 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -192,9 +192,9 @@ async def async_setup_entry( async_add_entities( [ WeatherFlowCloudSensor( - coordinator=coordinator, - description=sensor_description, - station_id=station_id, + coordinator, + sensor_description, + station_id, ) ], ) From 48350d3431da3a88ae1ecbe6199804881f207e2b Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Fri, 5 Jul 2024 13:10:16 -0600 Subject: [PATCH 19/38] doc --- homeassistant/components/weatherflow_cloud/entity.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/weatherflow_cloud/entity.py b/homeassistant/components/weatherflow_cloud/entity.py index aea9602ae06770..3c33ebcbdc4ef5 100644 --- a/homeassistant/components/weatherflow_cloud/entity.py +++ b/homeassistant/components/weatherflow_cloud/entity.py @@ -1,5 +1,7 @@ """Base entity class for WeatherFlow Cloud integration.""" +from weatherflow4py.models.rest.unified import WeatherFlowDataREST + from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -23,7 +25,7 @@ def __init__( # self.entity_description = description self.station_id = station_id - station_name = self.coordinator.data[station_id].station.name + station_name = coordinator.data[station_id].station.name self._attr_device_info = DeviceInfo( name=station_name, @@ -32,3 +34,8 @@ def __init__( manufacturer=MANUFACTURER, configuration_url=f"https://tempestwx.com/station/{station_id}/grid", ) + + @property + def station(self) -> WeatherFlowDataREST: + """Individual Station data.""" + return self.coordinator.data[self.station_id] From 54f9b06dd9bad0771cad00f23b9e484f276e50a3 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Sun, 7 Jul 2024 07:53:09 -0600 Subject: [PATCH 20/38] pr comments again --- homeassistant/components/weatherflow_cloud/sensor.py | 9 --------- homeassistant/components/weatherflow_cloud/strings.json | 6 ++---- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index d8367f98058670..a22a39401f55fd 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -127,15 +127,6 @@ class WeatherFlowCloudSensorEntityDescription( state_class=SensorStateClass.MEASUREMENT, suggested_display_precision=3, ), - WeatherFlowCloudSensorEntityDescription( - key="station_pressure", - translation_key="station_pressure", - value_fn=lambda data: data.observation.obs[0].station_pressure, - native_unit_of_measurement=UnitOfPressure.MBAR, - device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE, - state_class=SensorStateClass.MEASUREMENT, - suggested_display_precision=3, - ), # Lightning Sensors WeatherFlowCloudSensorEntityDescription( key="lightning_strike_count", diff --git a/homeassistant/components/weatherflow_cloud/strings.json b/homeassistant/components/weatherflow_cloud/strings.json index cfb2cbe97fd64d..df561c8b7532d3 100644 --- a/homeassistant/components/weatherflow_cloud/strings.json +++ b/homeassistant/components/weatherflow_cloud/strings.json @@ -35,9 +35,7 @@ "sea_level_pressure": { "name": "Pressure sea level" }, - "station_pressure": { - "name": "Pressure station" - }, + "dew_point": { "name": "Dew point" }, @@ -54,7 +52,7 @@ "name": "Lightning last distance" }, "lightning_strike_last_epoch": { - "name": "Lightning last" + "name": "Lightning last strike" }, "wind_chill": { From 4d758da2cc63fdda664e679c76443417e8bc732a Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Sun, 7 Jul 2024 11:04:18 -0600 Subject: [PATCH 21/38] fixing PR --- .../components/weatherflow_cloud/entity.py | 5 +- .../weatherflow_cloud/manifest.json | 2 +- .../components/weatherflow_cloud/sensor.py | 51 ++++++++++--------- 3 files changed, 28 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/entity.py b/homeassistant/components/weatherflow_cloud/entity.py index 3c33ebcbdc4ef5..46077ab087007a 100644 --- a/homeassistant/components/weatherflow_cloud/entity.py +++ b/homeassistant/components/weatherflow_cloud/entity.py @@ -22,13 +22,10 @@ def __init__( ) -> None: """Class initializer.""" super().__init__(coordinator) - # self.entity_description = description self.station_id = station_id - station_name = coordinator.data[station_id].station.name - self._attr_device_info = DeviceInfo( - name=station_name, + name=self.station.station.name, entry_type=DeviceEntryType.SERVICE, identifiers={(DOMAIN, str(station_id))}, manufacturer=MANUFACTURER, diff --git a/homeassistant/components/weatherflow_cloud/manifest.json b/homeassistant/components/weatherflow_cloud/manifest.json index 62fbdfc2aa97c6..354b9642c06036 100644 --- a/homeassistant/components/weatherflow_cloud/manifest.json +++ b/homeassistant/components/weatherflow_cloud/manifest.json @@ -5,6 +5,6 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/weatherflow_cloud", "iot_class": "cloud_polling", - "loggers": ["homeassistant.components.weatherflow_cloud", "weatherflow4py"], + "loggers": ["weatherflow4py"], "requirements": ["weatherflow4py==0.2.21"] } diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index a22a39401f55fd..8f369e19cb3860 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -41,7 +41,7 @@ class WeatherFlowCloudSensorEntityDescription( translation_key="air_density", state_class=SensorStateClass.MEASUREMENT, suggested_display_precision=5, - value_fn=lambda data: data.observation.obs[0].air_density, + value_fn=lambda data: data.air_density, native_unit_of_measurement="kg/m³", ), # Temp Sensors @@ -51,13 +51,13 @@ class WeatherFlowCloudSensorEntityDescription( device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, suggested_display_precision=1, - value_fn=lambda data: data.observation.obs[0].air_temperature, + value_fn=lambda data: data.air_temperature, native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), WeatherFlowCloudSensorEntityDescription( key="dew_point", translation_key="dew_point", - value_fn=lambda data: data.observation.obs[0].dew_point, + value_fn=lambda data: data.dew_point, native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -69,7 +69,7 @@ class WeatherFlowCloudSensorEntityDescription( device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, suggested_display_precision=1, - value_fn=lambda data: data.observation.obs[0].feels_like, + value_fn=lambda data: data.feels_like, native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), WeatherFlowCloudSensorEntityDescription( @@ -78,7 +78,7 @@ class WeatherFlowCloudSensorEntityDescription( device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, suggested_display_precision=1, - value_fn=lambda data: data.observation.obs[0].heat_index, + value_fn=lambda data: data.heat_index, native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), WeatherFlowCloudSensorEntityDescription( @@ -87,7 +87,7 @@ class WeatherFlowCloudSensorEntityDescription( device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, suggested_display_precision=1, - value_fn=lambda data: data.observation.obs[0].wind_chill, + value_fn=lambda data: data.wind_chill, native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), WeatherFlowCloudSensorEntityDescription( @@ -96,7 +96,7 @@ class WeatherFlowCloudSensorEntityDescription( device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, suggested_display_precision=1, - value_fn=lambda data: data.observation.obs[0].wet_bulb_temperature, + value_fn=lambda data: data.wet_bulb_temperature, native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), WeatherFlowCloudSensorEntityDescription( @@ -105,14 +105,14 @@ class WeatherFlowCloudSensorEntityDescription( device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, suggested_display_precision=1, - value_fn=lambda data: data.observation.obs[0].wet_bulb_globe_temperature, + value_fn=lambda data: data.wet_bulb_globe_temperature, native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), # Pressure Sensors WeatherFlowCloudSensorEntityDescription( key="barometric_pressure", translation_key="barometric_pressure", - value_fn=lambda data: data.observation.obs[0].barometric_pressure, + value_fn=lambda data: data.barometric_pressure, native_unit_of_measurement=UnitOfPressure.MBAR, device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE, state_class=SensorStateClass.MEASUREMENT, @@ -121,7 +121,7 @@ class WeatherFlowCloudSensorEntityDescription( WeatherFlowCloudSensorEntityDescription( key="sea_level_pressure", translation_key="sea_level_pressure", - value_fn=lambda data: data.observation.obs[0].sea_level_pressure, + value_fn=lambda data: data.sea_level_pressure, native_unit_of_measurement=UnitOfPressure.MBAR, device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE, state_class=SensorStateClass.MEASUREMENT, @@ -132,19 +132,19 @@ class WeatherFlowCloudSensorEntityDescription( key="lightning_strike_count", translation_key="lightning_strike_count", state_class=SensorStateClass.MEASUREMENT, - value_fn=lambda data: data.observation.obs[0].lightning_strike_count, + value_fn=lambda data: data.lightning_strike_count, ), WeatherFlowCloudSensorEntityDescription( key="lightning_strike_count_last_1hr", translation_key="lightning_strike_count_last_1hr", state_class=SensorStateClass.MEASUREMENT, - value_fn=lambda data: data.observation.obs[0].lightning_strike_count_last_1hr, + value_fn=lambda data: data.lightning_strike_count_last_1hr, ), WeatherFlowCloudSensorEntityDescription( key="lightning_strike_count_last_3hr", translation_key="lightning_strike_count_last_3hr", state_class=SensorStateClass.MEASUREMENT, - value_fn=lambda data: data.observation.obs[0].lightning_strike_count_last_3hr, + value_fn=lambda data: data.lightning_strike_count_last_3hr, ), WeatherFlowCloudSensorEntityDescription( key="lightning_strike_last_distance", @@ -152,14 +152,14 @@ class WeatherFlowCloudSensorEntityDescription( state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.DISTANCE, native_unit_of_measurement=UnitOfLength.KILOMETERS, - value_fn=lambda data: data.observation.obs[0].lightning_strike_last_distance, + value_fn=lambda data: data.lightning_strike_last_distance, ), WeatherFlowCloudSensorEntityDescription( key="lightning_strike_last_epoch", translation_key="lightning_strike_last_epoch", device_class=SensorDeviceClass.TIMESTAMP, value_fn=lambda data: datetime.fromtimestamp( - data.observation.obs[0].lightning_strike_last_epoch, tz=UTC + data.lightning_strike_last_epoch, tz=UTC ), ), ) @@ -196,22 +196,23 @@ class WeatherFlowCloudSensor(WeatherFlowCloudEntity, SensorEntity): entity_description: WeatherFlowCloudSensorEntityDescription - def __init__(self, coordinator, description, station_id): + def __init__( + self, + coordinator: WeatherFlowCloudDataUpdateCoordinator, + description: WeatherFlowCloudSensorEntityDescription, + station_id: int, + ) -> None: """Initialize the sensor.""" # Initialize the Entity Class - super().__init__( - coordinator=coordinator, - station_id=station_id, - ) + super().__init__(coordinator, station_id) self.entity_description = description self._attr_unique_id = ( - f"{self.coordinator.data[station_id].station.name}_{description.key}" + f"{self.coordinator.data[station_id].station.station_id}_{description.key}" ) - self._attr_has_entity_name = True - - # @property def native_value(self) -> StateType | date | datetime | Decimal | None: """Return the state of the sensor.""" - return self.entity_description.value_fn(self.coordinator.data[self.station_id]) + return self.entity_description.value_fn( + self.coordinator.data[self.station_id].observation.obs[0] + ) From 80f78824af05911aadc43a079a8f54246b12106a Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Sun, 7 Jul 2024 11:05:03 -0600 Subject: [PATCH 22/38] fixing PR --- homeassistant/components/weatherflow_cloud/sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index 8f369e19cb3860..6a792fa964ea31 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -7,7 +7,7 @@ from datetime import UTC, date, datetime from decimal import Decimal -from weatherflow4py.models.rest.unified import WeatherFlowDataREST +from weatherflow4py.models.rest.observation import Observation from homeassistant.components.sensor import ( SensorDeviceClass, @@ -32,7 +32,7 @@ class WeatherFlowCloudSensorEntityDescription( ): """Describes a weatherflow sensor.""" - value_fn: Callable[[WeatherFlowDataREST], int | str | datetime | None] + value_fn: Callable[[Observation], int | str | datetime | None] WF_SENSORS: tuple[WeatherFlowCloudSensorEntityDescription, ...] = ( From fd8e137c9b79a9a6603b363c5d4673082cdfab75 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Wed, 10 Jul 2024 08:41:25 -0600 Subject: [PATCH 23/38] applying entity class in sensor --- homeassistant/components/weatherflow_cloud/sensor.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index 6a792fa964ea31..11ccf0caabdbd5 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -206,9 +206,7 @@ def __init__( # Initialize the Entity Class super().__init__(coordinator, station_id) self.entity_description = description - self._attr_unique_id = ( - f"{self.coordinator.data[station_id].station.station_id}_{description.key}" - ) + self._attr_unique_id = f"{station_id}_{description.key}" @property def native_value(self) -> StateType | date | datetime | Decimal | None: From ec4ccc90d8c9a21c30ba369152c9b3eb93b6ee32 Mon Sep 17 00:00:00 2001 From: Jeef Date: Wed, 10 Jul 2024 08:40:36 -0600 Subject: [PATCH 24/38] Update homeassistant/components/weatherflow_cloud/sensor.py Co-authored-by: Joost Lekkerkerker --- homeassistant/components/weatherflow_cloud/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index 11ccf0caabdbd5..5c0fa1415e9245 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -212,5 +212,5 @@ def __init__( def native_value(self) -> StateType | date | datetime | Decimal | None: """Return the state of the sensor.""" return self.entity_description.value_fn( - self.coordinator.data[self.station_id].observation.obs[0] + self.station.observation.obs[0] ) From 5d7cf8c47d95f9161e7565fedf4e681aadb018c8 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Wed, 10 Jul 2024 08:55:32 -0600 Subject: [PATCH 25/38] Cleaning up weather.py --- .../components/weatherflow_cloud/weather.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/weather.py b/homeassistant/components/weatherflow_cloud/weather.py index 8e782fc47ddd20..4cb1f0d3187261 100644 --- a/homeassistant/components/weatherflow_cloud/weather.py +++ b/homeassistant/components/weatherflow_cloud/weather.py @@ -17,10 +17,9 @@ UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import ATTR_ATTRIBUTION, DOMAIN, MANUFACTURER, STATE_MAP +from .const import DOMAIN, STATE_MAP from .coordinator import WeatherFlowCloudDataUpdateCoordinator from .entity import WeatherFlowCloudEntity @@ -49,9 +48,6 @@ class WeatherFlowWeather( ): """Implementation of a WeatherFlow weather condition.""" - _attr_attribution = ATTR_ATTRIBUTION - _attr_has_entity_name = True - _attr_native_temperature_unit = UnitOfTemperature.CELSIUS _attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS _attr_native_pressure_unit = UnitOfPressure.MBAR @@ -72,14 +68,6 @@ def __init__( self.station_id = station_id self._attr_unique_id = f"weatherflow_forecast_{station_id}" - self._attr_device_info = DeviceInfo( - name=self.local_data.station.name, - entry_type=DeviceEntryType.SERVICE, - identifiers={(DOMAIN, f"{station_id}")}, - manufacturer=MANUFACTURER, - configuration_url=f"https://tempestwx.com/station/{station_id}/grid", - ) - @property def local_data(self) -> WeatherFlowDataREST: """Return the local weather data object for this station.""" From 91f26737bbb75d73ea9cd4cbedda3356d647c181 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Wed, 10 Jul 2024 09:47:28 -0600 Subject: [PATCH 26/38] station id cleanup for weather class --- homeassistant/components/weatherflow_cloud/sensor.py | 4 +--- homeassistant/components/weatherflow_cloud/weather.py | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index 5c0fa1415e9245..12963ea011cf7f 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -211,6 +211,4 @@ def __init__( @property def native_value(self) -> StateType | date | datetime | Decimal | None: """Return the state of the sensor.""" - return self.entity_description.value_fn( - self.station.observation.obs[0] - ) + return self.entity_description.value_fn(self.station.observation.obs[0]) diff --git a/homeassistant/components/weatherflow_cloud/weather.py b/homeassistant/components/weatherflow_cloud/weather.py index 4cb1f0d3187261..c475f2974a97fa 100644 --- a/homeassistant/components/weatherflow_cloud/weather.py +++ b/homeassistant/components/weatherflow_cloud/weather.py @@ -64,8 +64,6 @@ def __init__( ) -> None: """Initialise the platform with a data instance and station.""" super().__init__(coordinator, station_id) - - self.station_id = station_id self._attr_unique_id = f"weatherflow_forecast_{station_id}" @property From 7abd78e05441e7e723d598538e47326d1c063107 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Wed, 10 Jul 2024 09:51:20 -0600 Subject: [PATCH 27/38] rewrite adding sensors the correct way --- .../components/weatherflow_cloud/sensor.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index 12963ea011cf7f..a667cfb1e1290b 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -178,17 +178,11 @@ async def async_setup_entry( stations = coordinator.data.keys() - for sensor_description in WF_SENSORS: - for station_id in stations: - async_add_entities( - [ - WeatherFlowCloudSensor( - coordinator, - sensor_description, - station_id, - ) - ], - ) + async_add_entities( + WeatherFlowCloudSensor(coordinator, sensor_description, station_id) + for station_id in stations + for sensor_description in WF_SENSORS + ) class WeatherFlowCloudSensor(WeatherFlowCloudEntity, SensorEntity): From 8c2f91142785a3cc0e2e4a01f46c55e1494063af Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Wed, 10 Jul 2024 14:29:18 -0600 Subject: [PATCH 28/38] Adding snapshot testing --- .../components/weatherflow_cloud/__init__.py | 12 + .../components/weatherflow_cloud/conftest.py | 57 + .../weatherflow_cloud/fixtures/forecast.json | 4783 +++++++++++++++++ .../fixtures/station_observation.json | 100 + .../weatherflow_cloud/fixtures/stations.json | 132 + .../snapshots/test_sensor.ambr | 806 +++ .../weatherflow_cloud/test_sensor.py | 30 + 7 files changed, 5920 insertions(+) create mode 100644 tests/components/weatherflow_cloud/fixtures/forecast.json create mode 100644 tests/components/weatherflow_cloud/fixtures/station_observation.json create mode 100644 tests/components/weatherflow_cloud/fixtures/stations.json create mode 100644 tests/components/weatherflow_cloud/snapshots/test_sensor.ambr create mode 100644 tests/components/weatherflow_cloud/test_sensor.py diff --git a/tests/components/weatherflow_cloud/__init__.py b/tests/components/weatherflow_cloud/__init__.py index c251e7868cc2a5..31004a27f64e0d 100644 --- a/tests/components/weatherflow_cloud/__init__.py +++ b/tests/components/weatherflow_cloud/__init__.py @@ -1 +1,13 @@ """Tests for the WeatherflowCloud integration.""" + +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry + + +async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None: + """Fixture for setting up the component.""" + config_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() diff --git a/tests/components/weatherflow_cloud/conftest.py b/tests/components/weatherflow_cloud/conftest.py index 222d487393a708..36b42bf24a83b5 100644 --- a/tests/components/weatherflow_cloud/conftest.py +++ b/tests/components/weatherflow_cloud/conftest.py @@ -5,6 +5,15 @@ from aiohttp import ClientResponseError import pytest +from weatherflow4py.models.rest.forecast import WeatherDataForecastREST +from weatherflow4py.models.rest.observation import ObservationStationREST +from weatherflow4py.models.rest.stations import StationsResponseREST +from weatherflow4py.models.rest.unified import WeatherFlowDataREST + +from homeassistant.components.weatherflow_cloud.const import DOMAIN +from homeassistant.const import CONF_API_TOKEN + +from tests.common import MockConfigEntry, load_fixture @pytest.fixture @@ -56,3 +65,51 @@ def mock_get_stations_401_error() -> Generator[AsyncMock]: side_effect=side_effects, ) as mock_get_stations: yield mock_get_stations + + +MOCK_API_TOKEN = "1234567890" + + +@pytest.fixture +async def mock_config_entry() -> MockConfigEntry: + """Fixture for MockConfigEntry.""" + return MockConfigEntry( + domain=DOMAIN, + data={CONF_API_TOKEN: MOCK_API_TOKEN}, + version=1, + ) + + +@pytest.fixture +def mock_api(): + """Fixture for Mock WeatherFlowRestAPI.""" + get_stations_response_data = StationsResponseREST.from_json( + load_fixture("stations.json", DOMAIN) + ) + get_forecast_response_data = WeatherDataForecastREST.from_json( + load_fixture("forecast.json", DOMAIN) + ) + get_observation_response_data = ObservationStationREST.from_json( + load_fixture("station_observation.json", DOMAIN) + ) + + data = { + 24432: WeatherFlowDataREST( + weather=get_forecast_response_data, + observation=get_observation_response_data, + station=get_stations_response_data.stations[0], + device_observations=None, + ) + } + + with patch( + "homeassistant.components.weatherflow_cloud.coordinator.WeatherFlowRestAPI", + autospec=True, + ) as mock_api_class: + # Create an instance of AsyncMock for the API + mock_api = AsyncMock() + mock_api.get_all_data.return_value = data + # Patch the class to return our mock_api instance + mock_api_class.return_value = mock_api + + yield mock_api diff --git a/tests/components/weatherflow_cloud/fixtures/forecast.json b/tests/components/weatherflow_cloud/fixtures/forecast.json new file mode 100644 index 00000000000000..62793983327b59 --- /dev/null +++ b/tests/components/weatherflow_cloud/fixtures/forecast.json @@ -0,0 +1,4783 @@ +{ + "current_conditions": { + "air_density": 1.0, + "air_temperature": 4.0, + "brightness": 59768, + "conditions": "Clear", + "delta_t": 6.0, + "dew_point": -13.0, + "feels_like": 3.0, + "icon": "clear-day", + "is_precip_local_day_rain_check": true, + "is_precip_local_yesterday_rain_check": true, + "lightning_strike_count_last_1hr": 0, + "lightning_strike_count_last_3hr": 0, + "lightning_strike_last_distance": 39, + "lightning_strike_last_distance_msg": "37 - 41 km", + "lightning_strike_last_epoch": 1698522523, + "precip_accum_local_day": 0, + "precip_accum_local_yesterday": 0, + "precip_minutes_local_day": 0, + "precip_minutes_local_yesterday": 0, + "pressure_trend": "rising", + "relative_humidity": 27, + "sea_level_pressure": 1022.1, + "solar_radiation": 498, + "station_pressure": 795.8, + "time": 1703785918, + "uv": 2, + "wet_bulb_globe_temperature": 2.0, + "wet_bulb_temperature": -1.0, + "wind_avg": 2.0, + "wind_direction": 40, + "wind_direction_cardinal": "NE", + "wind_gust": 4.0 + }, + "forecast": { + "daily": [ + { + "air_temp_high": 5.0, + "air_temp_low": -6.0, + "conditions": "Clear", + "day_num": 28, + "day_start_local": 1703746800, + "icon": "clear-day", + "month_num": 12, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "sunrise": 1703773057, + "sunset": 1703807070 + }, + { + "air_temp_high": 7.0, + "air_temp_low": -1.0, + "conditions": "Clear", + "day_num": 29, + "day_start_local": 1703833200, + "icon": "clear-day", + "month_num": 12, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "sunrise": 1703859473, + "sunset": 1703893513 + }, + { + "air_temp_high": 10.0, + "air_temp_low": -1.0, + "conditions": "Partly Cloudy", + "day_num": 30, + "day_start_local": 1703919600, + "icon": "partly-cloudy-day", + "month_num": 12, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "sunrise": 1703945887, + "sunset": 1703979957 + }, + { + "air_temp_high": 2.0, + "air_temp_low": -3.0, + "conditions": "Partly Cloudy", + "day_num": 31, + "day_start_local": 1704006000, + "icon": "partly-cloudy-day", + "month_num": 12, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "sunrise": 1704032299, + "sunset": 1704066403 + }, + { + "air_temp_high": 5.0, + "air_temp_low": -4.0, + "conditions": "Partly Cloudy", + "day_num": 1, + "day_start_local": 1704092400, + "icon": "partly-cloudy-day", + "month_num": 1, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "sunrise": 1704118709, + "sunset": 1704152851 + }, + { + "air_temp_high": 4.0, + "air_temp_low": -4.0, + "conditions": "Partly Cloudy", + "day_num": 2, + "day_start_local": 1704178800, + "icon": "partly-cloudy-day", + "month_num": 1, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "sunrise": 1704205116, + "sunset": 1704239300 + }, + { + "air_temp_high": 3.0, + "air_temp_low": -5.0, + "conditions": "Partly Cloudy", + "day_num": 3, + "day_start_local": 1704265200, + "icon": "partly-cloudy-day", + "month_num": 1, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "sunrise": 1704291522, + "sunset": 1704325751 + }, + { + "air_temp_high": 4.0, + "air_temp_low": -4.0, + "conditions": "Wintry Mix Possible", + "day_num": 4, + "day_start_local": 1704351600, + "icon": "possibly-sleet-day", + "month_num": 1, + "precip_icon": "chance-sleet", + "precip_probability": 20, + "precip_type": "sleet", + "sunrise": 1704377925, + "sunset": 1704412203 + }, + { + "air_temp_high": 1.0, + "air_temp_low": -5.0, + "conditions": "Partly Cloudy", + "day_num": 5, + "day_start_local": 1704438000, + "icon": "partly-cloudy-day", + "month_num": 1, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "sunrise": 1704464327, + "sunset": 1704498656 + }, + { + "air_temp_high": 4.0, + "air_temp_low": -5.0, + "conditions": "Partly Cloudy", + "day_num": 6, + "day_start_local": 1704524400, + "icon": "partly-cloudy-day", + "month_num": 1, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "sunrise": 1704550726, + "sunset": 1704585111 + } + ], + "hourly": [ + { + "air_temperature": 4.0, + "conditions": "Clear", + "feels_like": -1.0, + "icon": "clear-day", + "local_day": 28, + "local_hour": 11, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 50, + "sea_level_pressure": 1021.3, + "time": 1703786400, + "uv": 4.0, + "wind_avg": 8.0, + "wind_direction": 350, + "wind_direction_cardinal": "N", + "wind_gust": 12.0 + }, + { + "air_temperature": 4.0, + "conditions": "Clear", + "feels_like": 0.0, + "icon": "clear-day", + "local_day": 28, + "local_hour": 12, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 50, + "sea_level_pressure": 1020.5, + "time": 1703790000, + "uv": 5.0, + "wind_avg": 7.0, + "wind_direction": 350, + "wind_direction_cardinal": "N", + "wind_gust": 11.0 + }, + { + "air_temperature": 5.0, + "conditions": "Clear", + "feels_like": 0.0, + "icon": "clear-day", + "local_day": 28, + "local_hour": 13, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 49, + "sea_level_pressure": 1019.3, + "time": 1703793600, + "uv": 5.0, + "wind_avg": 7.0, + "wind_direction": 350, + "wind_direction_cardinal": "N", + "wind_gust": 11.0 + }, + { + "air_temperature": 5.0, + "conditions": "Clear", + "feels_like": 1.0, + "icon": "clear-day", + "local_day": 28, + "local_hour": 14, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 47, + "sea_level_pressure": 1018.9, + "time": 1703797200, + "uv": 4.0, + "wind_avg": 8.0, + "wind_direction": 350, + "wind_direction_cardinal": "N", + "wind_gust": 11.0 + }, + { + "air_temperature": 5.0, + "conditions": "Clear", + "feels_like": 1.0, + "icon": "clear-day", + "local_day": 28, + "local_hour": 15, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 46, + "sea_level_pressure": 1019.9, + "time": 1703800800, + "uv": 3.0, + "wind_avg": 8.0, + "wind_direction": 350, + "wind_direction_cardinal": "N", + "wind_gust": 11.0 + }, + { + "air_temperature": 4.0, + "conditions": "Clear", + "feels_like": -1.0, + "icon": "clear-day", + "local_day": 28, + "local_hour": 16, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 52, + "sea_level_pressure": 1021.9, + "time": 1703804400, + "uv": 1.0, + "wind_avg": 6.0, + "wind_direction": 340, + "wind_direction_cardinal": "NNW", + "wind_gust": 9.0 + }, + { + "air_temperature": 1.0, + "conditions": "Clear", + "feels_like": -4.0, + "icon": "clear-night", + "local_day": 28, + "local_hour": 17, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 64, + "sea_level_pressure": 1025.4, + "time": 1703808000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 330, + "wind_direction_cardinal": "NNW", + "wind_gust": 7.0 + }, + { + "air_temperature": 0.0, + "conditions": "Clear", + "feels_like": -5.0, + "icon": "clear-night", + "local_day": 28, + "local_hour": 18, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 69, + "sea_level_pressure": 1026.1, + "time": 1703811600, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 0.0, + "conditions": "Clear", + "feels_like": -4.0, + "icon": "clear-night", + "local_day": 28, + "local_hour": 19, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 65, + "sea_level_pressure": 1026.6, + "time": 1703815200, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 0.0, + "conditions": "Clear", + "feels_like": -4.0, + "icon": "clear-night", + "local_day": 28, + "local_hour": 20, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 66, + "sea_level_pressure": 1026.6, + "time": 1703818800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 1.0, + "conditions": "Clear", + "feels_like": -3.0, + "icon": "clear-night", + "local_day": 28, + "local_hour": 21, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 63, + "sea_level_pressure": 1026.7, + "time": 1703822400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 1.0, + "conditions": "Clear", + "feels_like": -3.0, + "icon": "clear-night", + "local_day": 28, + "local_hour": 22, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 61, + "sea_level_pressure": 1026.6, + "time": 1703826000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 1.0, + "conditions": "Clear", + "feels_like": -3.0, + "icon": "clear-night", + "local_day": 28, + "local_hour": 23, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 61, + "sea_level_pressure": 1026.7, + "time": 1703829600, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 1.0, + "conditions": "Clear", + "feels_like": -4.0, + "icon": "clear-night", + "local_day": 29, + "local_hour": 0, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 64, + "sea_level_pressure": 1026.2, + "time": 1703833200, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 1.0, + "conditions": "Clear", + "feels_like": -3.0, + "icon": "clear-night", + "local_day": 29, + "local_hour": 1, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 63, + "sea_level_pressure": 1025.9, + "time": 1703836800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 1.0, + "conditions": "Clear", + "feels_like": -3.0, + "icon": "clear-night", + "local_day": 29, + "local_hour": 2, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 62, + "sea_level_pressure": 1026.1, + "time": 1703840400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 1.0, + "conditions": "Clear", + "feels_like": -3.0, + "icon": "clear-night", + "local_day": 29, + "local_hour": 3, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 61, + "sea_level_pressure": 1026.0, + "time": 1703844000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 310, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 0.0, + "conditions": "Clear", + "feels_like": -4.0, + "icon": "clear-night", + "local_day": 29, + "local_hour": 4, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 66, + "sea_level_pressure": 1025.9, + "time": 1703847600, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 310, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 0.0, + "conditions": "Clear", + "feels_like": -4.0, + "icon": "clear-night", + "local_day": 29, + "local_hour": 5, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 67, + "sea_level_pressure": 1026.3, + "time": 1703851200, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 310, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 1.0, + "conditions": "Clear", + "feels_like": -4.0, + "icon": "clear-night", + "local_day": 29, + "local_hour": 6, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 64, + "sea_level_pressure": 1026.8, + "time": 1703854800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 310, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 2.0, + "conditions": "Clear", + "feels_like": -3.0, + "icon": "clear-night", + "local_day": 29, + "local_hour": 7, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 60, + "sea_level_pressure": 1027.3, + "time": 1703858400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 310, + "wind_direction_cardinal": "NW", + "wind_gust": 5.0 + }, + { + "air_temperature": 5.0, + "conditions": "Clear", + "feels_like": 2.0, + "icon": "clear-day", + "local_day": 29, + "local_hour": 8, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 48, + "sea_level_pressure": 1026.2, + "time": 1703862000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 310, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 6.0, + "conditions": "Clear", + "feels_like": 3.0, + "icon": "clear-day", + "local_day": 29, + "local_hour": 9, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 45, + "sea_level_pressure": 1023.4, + "time": 1703865600, + "uv": 2.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 5.0, + "conditions": "Clear", + "feels_like": 2.0, + "icon": "clear-day", + "local_day": 29, + "local_hour": 10, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 47, + "sea_level_pressure": 1021.9, + "time": 1703869200, + "uv": 3.0, + "wind_avg": 4.0, + "wind_direction": 330, + "wind_direction_cardinal": "NNW", + "wind_gust": 6.0 + }, + { + "air_temperature": 6.0, + "conditions": "Clear", + "feels_like": 3.0, + "icon": "clear-day", + "local_day": 29, + "local_hour": 11, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 44, + "sea_level_pressure": 1020.8, + "time": 1703872800, + "uv": 4.0, + "wind_avg": 4.0, + "wind_direction": 350, + "wind_direction_cardinal": "N", + "wind_gust": 6.0 + }, + { + "air_temperature": 7.0, + "conditions": "Clear", + "feels_like": 4.0, + "icon": "clear-day", + "local_day": 29, + "local_hour": 12, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 42, + "sea_level_pressure": 1019.3, + "time": 1703876400, + "uv": 5.0, + "wind_avg": 4.0, + "wind_direction": 360, + "wind_direction_cardinal": "N", + "wind_gust": 6.0 + }, + { + "air_temperature": 7.0, + "conditions": "Clear", + "feels_like": 5.0, + "icon": "clear-day", + "local_day": 29, + "local_hour": 13, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 40, + "sea_level_pressure": 1018.1, + "time": 1703880000, + "uv": 5.0, + "wind_avg": 4.0, + "wind_direction": 0, + "wind_direction_cardinal": "N", + "wind_gust": 6.0 + }, + { + "air_temperature": 7.0, + "conditions": "Clear", + "feels_like": 5.0, + "icon": "clear-day", + "local_day": 29, + "local_hour": 14, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 40, + "sea_level_pressure": 1017.8, + "time": 1703883600, + "uv": 4.0, + "wind_avg": 3.0, + "wind_direction": 10, + "wind_direction_cardinal": "N", + "wind_gust": 5.0 + }, + { + "air_temperature": 7.0, + "conditions": "Clear", + "feels_like": 5.0, + "icon": "clear-day", + "local_day": 29, + "local_hour": 15, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 41, + "sea_level_pressure": 1018.0, + "time": 1703887200, + "uv": 3.0, + "wind_avg": 3.0, + "wind_direction": 180, + "wind_direction_cardinal": "S", + "wind_gust": 4.0 + }, + { + "air_temperature": 5.0, + "conditions": "Clear", + "feels_like": 3.0, + "icon": "clear-day", + "local_day": 29, + "local_hour": 16, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 46, + "sea_level_pressure": 1018.8, + "time": 1703890800, + "uv": 1.0, + "wind_avg": 3.0, + "wind_direction": 180, + "wind_direction_cardinal": "S", + "wind_gust": 4.0 + }, + { + "air_temperature": 3.0, + "conditions": "Partly Cloudy", + "feels_like": 0.0, + "icon": "partly-cloudy-night", + "local_day": 29, + "local_hour": 17, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 55, + "sea_level_pressure": 1020.6, + "time": 1703894400, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 250, + "wind_direction_cardinal": "WSW", + "wind_gust": 4.0 + }, + { + "air_temperature": 1.0, + "conditions": "Clear", + "feels_like": -2.0, + "icon": "clear-night", + "local_day": 29, + "local_hour": 18, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 62, + "sea_level_pressure": 1020.7, + "time": 1703898000, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 4.0 + }, + { + "air_temperature": 0.0, + "conditions": "Clear", + "feels_like": -3.0, + "icon": "clear-night", + "local_day": 29, + "local_hour": 19, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 66, + "sea_level_pressure": 1020.7, + "time": 1703901600, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 3.0 + }, + { + "air_temperature": 0.0, + "conditions": "Clear", + "feels_like": -3.0, + "icon": "clear-night", + "local_day": 29, + "local_hour": 20, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 69, + "sea_level_pressure": 1020.8, + "time": 1703905200, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 3.0 + }, + { + "air_temperature": -1.0, + "conditions": "Clear", + "feels_like": -4.0, + "icon": "clear-night", + "local_day": 29, + "local_hour": 21, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 72, + "sea_level_pressure": 1020.3, + "time": 1703908800, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 3.0 + }, + { + "air_temperature": -1.0, + "conditions": "Clear", + "feels_like": -4.0, + "icon": "clear-night", + "local_day": 29, + "local_hour": 22, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 73, + "sea_level_pressure": 1019.9, + "time": 1703912400, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 4.0 + }, + { + "air_temperature": -1.0, + "conditions": "Clear", + "feels_like": -5.0, + "icon": "clear-night", + "local_day": 29, + "local_hour": 23, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 74, + "sea_level_pressure": 1019.4, + "time": 1703916000, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 4.0 + }, + { + "air_temperature": -1.0, + "conditions": "Clear", + "feels_like": -5.0, + "icon": "clear-night", + "local_day": 30, + "local_hour": 0, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 75, + "sea_level_pressure": 1019.0, + "time": 1703919600, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 4.0 + }, + { + "air_temperature": -1.0, + "conditions": "Clear", + "feels_like": -5.0, + "icon": "clear-night", + "local_day": 30, + "local_hour": 1, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 75, + "sea_level_pressure": 1018.5, + "time": 1703923200, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 4.0 + }, + { + "air_temperature": -1.0, + "conditions": "Clear", + "feels_like": -5.0, + "icon": "clear-night", + "local_day": 30, + "local_hour": 2, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 74, + "sea_level_pressure": 1018.1, + "time": 1703926800, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": -1.0, + "conditions": "Clear", + "feels_like": -5.0, + "icon": "clear-night", + "local_day": 30, + "local_hour": 3, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 74, + "sea_level_pressure": 1017.7, + "time": 1703930400, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 5.0 + }, + { + "air_temperature": -1.0, + "conditions": "Clear", + "feels_like": -5.0, + "icon": "clear-night", + "local_day": 30, + "local_hour": 4, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 74, + "sea_level_pressure": 1017.4, + "time": 1703934000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 5.0 + }, + { + "air_temperature": -1.0, + "conditions": "Clear", + "feels_like": -5.0, + "icon": "clear-night", + "local_day": 30, + "local_hour": 5, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 73, + "sea_level_pressure": 1017.0, + "time": 1703937600, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 5.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -5.0, + "icon": "partly-cloudy-night", + "local_day": 30, + "local_hour": 6, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 72, + "sea_level_pressure": 1016.8, + "time": 1703941200, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-night", + "local_day": 30, + "local_hour": 7, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 69, + "sea_level_pressure": 1016.5, + "time": 1703944800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-day", + "local_day": 30, + "local_hour": 8, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 63, + "sea_level_pressure": 1016.3, + "time": 1703948400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": 3.0, + "conditions": "Partly Cloudy", + "feels_like": 0.0, + "icon": "partly-cloudy-day", + "local_day": 30, + "local_hour": 9, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 55, + "sea_level_pressure": 1015.0, + "time": 1703952000, + "uv": 2.0, + "wind_avg": 4.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": 6.0, + "conditions": "Partly Cloudy", + "feels_like": 3.0, + "icon": "partly-cloudy-day", + "local_day": 30, + "local_hour": 10, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 45, + "sea_level_pressure": 1013.7, + "time": 1703955600, + "uv": 2.0, + "wind_avg": 4.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": 8.0, + "conditions": "Partly Cloudy", + "feels_like": 5.0, + "icon": "partly-cloudy-day", + "local_day": 30, + "local_hour": 11, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 39, + "sea_level_pressure": 1012.4, + "time": 1703959200, + "uv": 2.0, + "wind_avg": 4.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": 9.0, + "conditions": "Partly Cloudy", + "feels_like": 7.0, + "icon": "partly-cloudy-day", + "local_day": 30, + "local_hour": 12, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 36, + "sea_level_pressure": 1011.5, + "time": 1703962800, + "uv": 4.0, + "wind_avg": 4.0, + "wind_direction": 210, + "wind_direction_cardinal": "SSW", + "wind_gust": 5.0 + }, + { + "air_temperature": 10.0, + "conditions": "Partly Cloudy", + "feels_like": 8.0, + "icon": "partly-cloudy-day", + "local_day": 30, + "local_hour": 13, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 35, + "sea_level_pressure": 1010.7, + "time": 1703966400, + "uv": 4.0, + "wind_avg": 4.0, + "wind_direction": 210, + "wind_direction_cardinal": "SSW", + "wind_gust": 5.0 + }, + { + "air_temperature": 9.0, + "conditions": "Partly Cloudy", + "feels_like": 7.0, + "icon": "partly-cloudy-day", + "local_day": 30, + "local_hour": 14, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 35, + "sea_level_pressure": 1009.8, + "time": 1703970000, + "uv": 4.0, + "wind_avg": 4.0, + "wind_direction": 210, + "wind_direction_cardinal": "SSW", + "wind_gust": 5.0 + }, + { + "air_temperature": 8.0, + "conditions": "Partly Cloudy", + "feels_like": 6.0, + "icon": "partly-cloudy-day", + "local_day": 30, + "local_hour": 15, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 38, + "sea_level_pressure": 1010.7, + "time": 1703973600, + "uv": 2.0, + "wind_avg": 3.0, + "wind_direction": 230, + "wind_direction_cardinal": "SW", + "wind_gust": 4.0 + }, + { + "air_temperature": 6.0, + "conditions": "Partly Cloudy", + "feels_like": 4.0, + "icon": "partly-cloudy-day", + "local_day": 30, + "local_hour": 16, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 43, + "sea_level_pressure": 1011.6, + "time": 1703977200, + "uv": 2.0, + "wind_avg": 3.0, + "wind_direction": 230, + "wind_direction_cardinal": "SW", + "wind_gust": 4.0 + }, + { + "air_temperature": 5.0, + "conditions": "Partly Cloudy", + "feels_like": 3.0, + "icon": "partly-cloudy-night", + "local_day": 30, + "local_hour": 17, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 48, + "sea_level_pressure": 1012.5, + "time": 1703980800, + "uv": 2.0, + "wind_avg": 2.0, + "wind_direction": 230, + "wind_direction_cardinal": "SW", + "wind_gust": 3.0 + }, + { + "air_temperature": 4.0, + "conditions": "Partly Cloudy", + "feels_like": 1.0, + "icon": "partly-cloudy-night", + "local_day": 30, + "local_hour": 18, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 53, + "sea_level_pressure": 1013.1, + "time": 1703984400, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 3.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": 0.0, + "icon": "partly-cloudy-night", + "local_day": 30, + "local_hour": 19, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 56, + "sea_level_pressure": 1013.7, + "time": 1703988000, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 3.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -1.0, + "icon": "partly-cloudy-night", + "local_day": 30, + "local_hour": 20, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 60, + "sea_level_pressure": 1014.4, + "time": 1703991600, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 2.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -1.0, + "icon": "partly-cloudy-night", + "local_day": 30, + "local_hour": 21, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 63, + "sea_level_pressure": 1014.7, + "time": 1703995200, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 2.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -2.0, + "icon": "partly-cloudy-night", + "local_day": 30, + "local_hour": 22, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 66, + "sea_level_pressure": 1015.1, + "time": 1703998800, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 2.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -2.0, + "icon": "partly-cloudy-night", + "local_day": 30, + "local_hour": 23, + "precip": 0, + "precip_icon": "chance-snow", + "precip_probability": 0, + "precip_type": "snow", + "relative_humidity": 67, + "sea_level_pressure": 1015.5, + "time": 1704002400, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 2.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-night", + "local_day": 31, + "local_hour": 0, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 68, + "sea_level_pressure": 1015.3, + "time": 1704006000, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 2.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-night", + "local_day": 31, + "local_hour": 1, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 68, + "sea_level_pressure": 1015.0, + "time": 1704009600, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 3.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-night", + "local_day": 31, + "local_hour": 2, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 69, + "sea_level_pressure": 1014.7, + "time": 1704013200, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 3.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-night", + "local_day": 31, + "local_hour": 3, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 71, + "sea_level_pressure": 1015.1, + "time": 1704016800, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 3.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-night", + "local_day": 31, + "local_hour": 4, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 73, + "sea_level_pressure": 1015.5, + "time": 1704020400, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 3.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-night", + "local_day": 31, + "local_hour": 5, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 74, + "sea_level_pressure": 1015.9, + "time": 1704024000, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 3.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-night", + "local_day": 31, + "local_hour": 6, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 74, + "sea_level_pressure": 1016.7, + "time": 1704027600, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 3.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-night", + "local_day": 31, + "local_hour": 7, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 73, + "sea_level_pressure": 1017.4, + "time": 1704031200, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 3.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-day", + "local_day": 31, + "local_hour": 8, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 71, + "sea_level_pressure": 1018.2, + "time": 1704034800, + "uv": 0.0, + "wind_avg": 2.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 3.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-day", + "local_day": 31, + "local_hour": 9, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 68, + "sea_level_pressure": 1018.2, + "time": 1704038400, + "uv": 1.0, + "wind_avg": 2.0, + "wind_direction": 120, + "wind_direction_cardinal": "ESE", + "wind_gust": 4.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -2.0, + "icon": "partly-cloudy-day", + "local_day": 31, + "local_hour": 10, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 64, + "sea_level_pressure": 1018.2, + "time": 1704042000, + "uv": 1.0, + "wind_avg": 3.0, + "wind_direction": 120, + "wind_direction_cardinal": "ESE", + "wind_gust": 4.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -2.0, + "icon": "partly-cloudy-day", + "local_day": 31, + "local_hour": 11, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 61, + "sea_level_pressure": 1018.1, + "time": 1704045600, + "uv": 1.0, + "wind_avg": 3.0, + "wind_direction": 120, + "wind_direction_cardinal": "ESE", + "wind_gust": 5.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -1.0, + "icon": "partly-cloudy-day", + "local_day": 31, + "local_hour": 12, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 59, + "sea_level_pressure": 1017.6, + "time": 1704049200, + "uv": 3.0, + "wind_avg": 3.0, + "wind_direction": 100, + "wind_direction_cardinal": "E", + "wind_gust": 5.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -1.0, + "icon": "partly-cloudy-day", + "local_day": 31, + "local_hour": 13, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 58, + "sea_level_pressure": 1017.0, + "time": 1704052800, + "uv": 3.0, + "wind_avg": 3.0, + "wind_direction": 100, + "wind_direction_cardinal": "E", + "wind_gust": 5.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -1.0, + "icon": "partly-cloudy-day", + "local_day": 31, + "local_hour": 14, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 58, + "sea_level_pressure": 1016.4, + "time": 1704056400, + "uv": 3.0, + "wind_avg": 3.0, + "wind_direction": 100, + "wind_direction_cardinal": "E", + "wind_gust": 5.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -2.0, + "icon": "partly-cloudy-day", + "local_day": 31, + "local_hour": 15, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 60, + "sea_level_pressure": 1017.9, + "time": 1704060000, + "uv": 2.0, + "wind_avg": 3.0, + "wind_direction": 130, + "wind_direction_cardinal": "SE", + "wind_gust": 5.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -2.0, + "icon": "partly-cloudy-day", + "local_day": 31, + "local_hour": 16, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 63, + "sea_level_pressure": 1019.4, + "time": 1704063600, + "uv": 2.0, + "wind_avg": 3.0, + "wind_direction": 130, + "wind_direction_cardinal": "SE", + "wind_gust": 4.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-night", + "local_day": 31, + "local_hour": 17, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 67, + "sea_level_pressure": 1021.0, + "time": 1704067200, + "uv": 2.0, + "wind_avg": 3.0, + "wind_direction": 130, + "wind_direction_cardinal": "SE", + "wind_gust": 4.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-night", + "local_day": 31, + "local_hour": 18, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 72, + "sea_level_pressure": 1021.8, + "time": 1704070800, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 230, + "wind_direction_cardinal": "SW", + "wind_gust": 4.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 31, + "local_hour": 19, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 78, + "sea_level_pressure": 1022.7, + "time": 1704074400, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 230, + "wind_direction_cardinal": "SW", + "wind_gust": 4.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 31, + "local_hour": 20, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 82, + "sea_level_pressure": 1023.6, + "time": 1704078000, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 230, + "wind_direction_cardinal": "SW", + "wind_gust": 4.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -7.0, + "icon": "partly-cloudy-night", + "local_day": 31, + "local_hour": 21, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 85, + "sea_level_pressure": 1023.6, + "time": 1704081600, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 4.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -7.0, + "icon": "partly-cloudy-night", + "local_day": 31, + "local_hour": 22, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 86, + "sea_level_pressure": 1023.6, + "time": 1704085200, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 4.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -7.0, + "icon": "partly-cloudy-night", + "local_day": 31, + "local_hour": 23, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 87, + "sea_level_pressure": 1023.6, + "time": 1704088800, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 4.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 1, + "local_hour": 0, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 87, + "sea_level_pressure": 1024.0, + "time": 1704092400, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 250, + "wind_direction_cardinal": "WSW", + "wind_gust": 4.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 1, + "local_hour": 1, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 88, + "sea_level_pressure": 1024.5, + "time": 1704096000, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 250, + "wind_direction_cardinal": "WSW", + "wind_gust": 4.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 1, + "local_hour": 2, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 89, + "sea_level_pressure": 1024.9, + "time": 1704099600, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 250, + "wind_direction_cardinal": "WSW", + "wind_gust": 4.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 1, + "local_hour": 3, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 90, + "sea_level_pressure": 1024.8, + "time": 1704103200, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 4.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 1, + "local_hour": 4, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 91, + "sea_level_pressure": 1024.6, + "time": 1704106800, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 4.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 1, + "local_hour": 5, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 90, + "sea_level_pressure": 1024.5, + "time": 1704110400, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 4.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 1, + "local_hour": 6, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 89, + "sea_level_pressure": 1024.4, + "time": 1704114000, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 1, + "local_hour": 7, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 85, + "sea_level_pressure": 1024.4, + "time": 1704117600, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-day", + "local_day": 1, + "local_hour": 8, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 79, + "sea_level_pressure": 1024.4, + "time": 1704121200, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-day", + "local_day": 1, + "local_hour": 9, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 69, + "sea_level_pressure": 1022.7, + "time": 1704124800, + "uv": 1.0, + "wind_avg": 4.0, + "wind_direction": 230, + "wind_direction_cardinal": "SW", + "wind_gust": 5.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -2.0, + "icon": "partly-cloudy-day", + "local_day": 1, + "local_hour": 10, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 59, + "sea_level_pressure": 1021.1, + "time": 1704128400, + "uv": 1.0, + "wind_avg": 4.0, + "wind_direction": 230, + "wind_direction_cardinal": "SW", + "wind_gust": 6.0 + }, + { + "air_temperature": 4.0, + "conditions": "Partly Cloudy", + "feels_like": 0.0, + "icon": "partly-cloudy-day", + "local_day": 1, + "local_hour": 11, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 53, + "sea_level_pressure": 1019.5, + "time": 1704132000, + "uv": 1.0, + "wind_avg": 4.0, + "wind_direction": 230, + "wind_direction_cardinal": "SW", + "wind_gust": 6.0 + }, + { + "air_temperature": 4.0, + "conditions": "Partly Cloudy", + "feels_like": 1.0, + "icon": "partly-cloudy-day", + "local_day": 1, + "local_hour": 12, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 49, + "sea_level_pressure": 1018.5, + "time": 1704135600, + "uv": 4.0, + "wind_avg": 4.0, + "wind_direction": 170, + "wind_direction_cardinal": "S", + "wind_gust": 6.0 + }, + { + "air_temperature": 5.0, + "conditions": "Partly Cloudy", + "feels_like": 2.0, + "icon": "partly-cloudy-day", + "local_day": 1, + "local_hour": 13, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 47, + "sea_level_pressure": 1017.4, + "time": 1704139200, + "uv": 4.0, + "wind_avg": 4.0, + "wind_direction": 170, + "wind_direction_cardinal": "S", + "wind_gust": 6.0 + }, + { + "air_temperature": 5.0, + "conditions": "Partly Cloudy", + "feels_like": 2.0, + "icon": "partly-cloudy-day", + "local_day": 1, + "local_hour": 14, + "precip": 0, + "precip_icon": "chance-rain", + "precip_probability": 0, + "precip_type": "rain", + "relative_humidity": 48, + "sea_level_pressure": 1016.4, + "time": 1704142800, + "uv": 4.0, + "wind_avg": 4.0, + "wind_direction": 170, + "wind_direction_cardinal": "S", + "wind_gust": 6.0 + }, + { + "air_temperature": 4.0, + "conditions": "Partly Cloudy", + "feels_like": 1.0, + "icon": "partly-cloudy-day", + "local_day": 1, + "local_hour": 15, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 50, + "sea_level_pressure": 1017.7, + "time": 1704146400, + "uv": 3.0, + "wind_avg": 4.0, + "wind_direction": 190, + "wind_direction_cardinal": "S", + "wind_gust": 6.0 + }, + { + "air_temperature": 3.0, + "conditions": "Partly Cloudy", + "feels_like": 0.0, + "icon": "partly-cloudy-day", + "local_day": 1, + "local_hour": 16, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 55, + "sea_level_pressure": 1018.9, + "time": 1704150000, + "uv": 3.0, + "wind_avg": 4.0, + "wind_direction": 190, + "wind_direction_cardinal": "S", + "wind_gust": 5.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -2.0, + "icon": "partly-cloudy-night", + "local_day": 1, + "local_hour": 17, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 60, + "sea_level_pressure": 1020.2, + "time": 1704153600, + "uv": 3.0, + "wind_avg": 3.0, + "wind_direction": 190, + "wind_direction_cardinal": "S", + "wind_gust": 5.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-night", + "local_day": 1, + "local_hour": 18, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 66, + "sea_level_pressure": 1020.8, + "time": 1704157200, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 250, + "wind_direction_cardinal": "WSW", + "wind_gust": 5.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-night", + "local_day": 1, + "local_hour": 19, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 71, + "sea_level_pressure": 1021.4, + "time": 1704160800, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 250, + "wind_direction_cardinal": "WSW", + "wind_gust": 5.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -5.0, + "icon": "partly-cloudy-night", + "local_day": 1, + "local_hour": 20, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 75, + "sea_level_pressure": 1022.0, + "time": 1704164400, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 250, + "wind_direction_cardinal": "WSW", + "wind_gust": 5.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 1, + "local_hour": 21, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 78, + "sea_level_pressure": 1021.9, + "time": 1704168000, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 1, + "local_hour": 22, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 78, + "sea_level_pressure": 1021.7, + "time": 1704171600, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 1, + "local_hour": 23, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 78, + "sea_level_pressure": 1021.6, + "time": 1704175200, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 2, + "local_hour": 0, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 78, + "sea_level_pressure": 1020.8, + "time": 1704178800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 5.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 2, + "local_hour": 1, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 77, + "sea_level_pressure": 1020.1, + "time": 1704182400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 5.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 2, + "local_hour": 2, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 77, + "sea_level_pressure": 1019.3, + "time": 1704186000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 5.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 2, + "local_hour": 3, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 77, + "sea_level_pressure": 1019.0, + "time": 1704189600, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 310, + "wind_direction_cardinal": "NW", + "wind_gust": 5.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 2, + "local_hour": 4, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 78, + "sea_level_pressure": 1018.7, + "time": 1704193200, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 310, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 2, + "local_hour": 5, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 78, + "sea_level_pressure": 1018.4, + "time": 1704196800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 310, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 2, + "local_hour": 6, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 76, + "sea_level_pressure": 1018.5, + "time": 1704200400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 2, + "local_hour": 7, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 74, + "sea_level_pressure": 1018.7, + "time": 1704204000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -5.0, + "icon": "partly-cloudy-day", + "local_day": 2, + "local_hour": 8, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 71, + "sea_level_pressure": 1018.9, + "time": 1704207600, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-day", + "local_day": 2, + "local_hour": 9, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 67, + "sea_level_pressure": 1018.2, + "time": 1704211200, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 350, + "wind_direction_cardinal": "N", + "wind_gust": 6.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-day", + "local_day": 2, + "local_hour": 10, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 62, + "sea_level_pressure": 1017.5, + "time": 1704214800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 350, + "wind_direction_cardinal": "N", + "wind_gust": 6.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -2.0, + "icon": "partly-cloudy-day", + "local_day": 2, + "local_hour": 11, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 58, + "sea_level_pressure": 1016.8, + "time": 1704218400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 350, + "wind_direction_cardinal": "N", + "wind_gust": 6.0 + }, + { + "air_temperature": 3.0, + "conditions": "Partly Cloudy", + "feels_like": -1.0, + "icon": "partly-cloudy-day", + "local_day": 2, + "local_hour": 12, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 55, + "sea_level_pressure": 1015.7, + "time": 1704222000, + "uv": 3.0, + "wind_avg": 4.0, + "wind_direction": 10, + "wind_direction_cardinal": "N", + "wind_gust": 6.0 + }, + { + "air_temperature": 4.0, + "conditions": "Partly Cloudy", + "feels_like": 0.0, + "icon": "partly-cloudy-day", + "local_day": 2, + "local_hour": 13, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 53, + "sea_level_pressure": 1014.7, + "time": 1704225600, + "uv": 3.0, + "wind_avg": 4.0, + "wind_direction": 10, + "wind_direction_cardinal": "N", + "wind_gust": 7.0 + }, + { + "air_temperature": 4.0, + "conditions": "Partly Cloudy", + "feels_like": 0.0, + "icon": "partly-cloudy-day", + "local_day": 2, + "local_hour": 14, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 53, + "sea_level_pressure": 1013.6, + "time": 1704229200, + "uv": 3.0, + "wind_avg": 4.0, + "wind_direction": 10, + "wind_direction_cardinal": "N", + "wind_gust": 7.0 + }, + { + "air_temperature": 3.0, + "conditions": "Partly Cloudy", + "feels_like": -1.0, + "icon": "partly-cloudy-day", + "local_day": 2, + "local_hour": 15, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 56, + "sea_level_pressure": 1014.8, + "time": 1704232800, + "uv": 2.0, + "wind_avg": 4.0, + "wind_direction": 360, + "wind_direction_cardinal": "N", + "wind_gust": 6.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-day", + "local_day": 2, + "local_hour": 16, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 61, + "sea_level_pressure": 1016.1, + "time": 1704236400, + "uv": 2.0, + "wind_avg": 4.0, + "wind_direction": 360, + "wind_direction_cardinal": "N", + "wind_gust": 6.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-night", + "local_day": 2, + "local_hour": 17, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 67, + "sea_level_pressure": 1017.4, + "time": 1704240000, + "uv": 2.0, + "wind_avg": 4.0, + "wind_direction": 360, + "wind_direction_cardinal": "N", + "wind_gust": 6.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -5.0, + "icon": "partly-cloudy-night", + "local_day": 2, + "local_hour": 18, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 73, + "sea_level_pressure": 1017.7, + "time": 1704243600, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 310, + "wind_direction_cardinal": "NW", + "wind_gust": 6.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 2, + "local_hour": 19, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 78, + "sea_level_pressure": 1018.1, + "time": 1704247200, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 310, + "wind_direction_cardinal": "NW", + "wind_gust": 5.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -7.0, + "icon": "partly-cloudy-night", + "local_day": 2, + "local_hour": 20, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 82, + "sea_level_pressure": 1018.5, + "time": 1704250800, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 310, + "wind_direction_cardinal": "NW", + "wind_gust": 5.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 2, + "local_hour": 21, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 85, + "sea_level_pressure": 1018.4, + "time": 1704254400, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 5.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 2, + "local_hour": 22, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 88, + "sea_level_pressure": 1018.4, + "time": 1704258000, + "uv": 0.0, + "wind_avg": 3.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 5.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 2, + "local_hour": 23, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 90, + "sea_level_pressure": 1018.4, + "time": 1704261600, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 5.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 3, + "local_hour": 0, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 91, + "sea_level_pressure": 1018.4, + "time": 1704265200, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 3, + "local_hour": 1, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 91, + "sea_level_pressure": 1018.3, + "time": 1704268800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 3, + "local_hour": 2, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 93, + "sea_level_pressure": 1018.3, + "time": 1704272400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": -5.0, + "conditions": "Partly Cloudy", + "feels_like": -10.0, + "icon": "partly-cloudy-night", + "local_day": 3, + "local_hour": 3, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 95, + "sea_level_pressure": 1018.1, + "time": 1704276000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": -5.0, + "conditions": "Partly Cloudy", + "feels_like": -10.0, + "icon": "partly-cloudy-night", + "local_day": 3, + "local_hour": 4, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 97, + "sea_level_pressure": 1017.8, + "time": 1704279600, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": -5.0, + "conditions": "Partly Cloudy", + "feels_like": -10.0, + "icon": "partly-cloudy-night", + "local_day": 3, + "local_hour": 5, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 98, + "sea_level_pressure": 1017.6, + "time": 1704283200, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 5.0 + }, + { + "air_temperature": -5.0, + "conditions": "Partly Cloudy", + "feels_like": -10.0, + "icon": "partly-cloudy-night", + "local_day": 3, + "local_hour": 6, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 96, + "sea_level_pressure": 1017.7, + "time": 1704286800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 6.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 3, + "local_hour": 7, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 93, + "sea_level_pressure": 1017.8, + "time": 1704290400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 6.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-day", + "local_day": 3, + "local_hour": 8, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 86, + "sea_level_pressure": 1017.9, + "time": 1704294000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 6.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-day", + "local_day": 3, + "local_hour": 9, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 78, + "sea_level_pressure": 1016.1, + "time": 1704297600, + "uv": 1.0, + "wind_avg": 4.0, + "wind_direction": 210, + "wind_direction_cardinal": "SSW", + "wind_gust": 6.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-day", + "local_day": 3, + "local_hour": 10, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 68, + "sea_level_pressure": 1014.3, + "time": 1704301200, + "uv": 1.0, + "wind_avg": 4.0, + "wind_direction": 210, + "wind_direction_cardinal": "SSW", + "wind_gust": 6.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-day", + "local_day": 3, + "local_hour": 11, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 62, + "sea_level_pressure": 1012.5, + "time": 1704304800, + "uv": 1.0, + "wind_avg": 4.0, + "wind_direction": 210, + "wind_direction_cardinal": "SSW", + "wind_gust": 6.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -2.0, + "icon": "partly-cloudy-day", + "local_day": 3, + "local_hour": 12, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 57, + "sea_level_pressure": 1011.4, + "time": 1704308400, + "uv": 4.0, + "wind_avg": 4.0, + "wind_direction": 190, + "wind_direction_cardinal": "S", + "wind_gust": 6.0 + }, + { + "air_temperature": 3.0, + "conditions": "Partly Cloudy", + "feels_like": -1.0, + "icon": "partly-cloudy-day", + "local_day": 3, + "local_hour": 13, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 55, + "sea_level_pressure": 1010.3, + "time": 1704312000, + "uv": 4.0, + "wind_avg": 4.0, + "wind_direction": 190, + "wind_direction_cardinal": "S", + "wind_gust": 7.0 + }, + { + "air_temperature": 3.0, + "conditions": "Partly Cloudy", + "feels_like": -1.0, + "icon": "partly-cloudy-day", + "local_day": 3, + "local_hour": 14, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 55, + "sea_level_pressure": 1009.1, + "time": 1704315600, + "uv": 4.0, + "wind_avg": 4.0, + "wind_direction": 190, + "wind_direction_cardinal": "S", + "wind_gust": 7.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -2.0, + "icon": "partly-cloudy-day", + "local_day": 3, + "local_hour": 15, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 57, + "sea_level_pressure": 1010.4, + "time": 1704319200, + "uv": 3.0, + "wind_avg": 4.0, + "wind_direction": 190, + "wind_direction_cardinal": "S", + "wind_gust": 7.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-day", + "local_day": 3, + "local_hour": 16, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 63, + "sea_level_pressure": 1011.7, + "time": 1704322800, + "uv": 3.0, + "wind_avg": 4.0, + "wind_direction": 190, + "wind_direction_cardinal": "S", + "wind_gust": 6.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -5.0, + "icon": "partly-cloudy-night", + "local_day": 3, + "local_hour": 17, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 68, + "sea_level_pressure": 1012.9, + "time": 1704326400, + "uv": 3.0, + "wind_avg": 4.0, + "wind_direction": 190, + "wind_direction_cardinal": "S", + "wind_gust": 6.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 3, + "local_hour": 18, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 74, + "sea_level_pressure": 1013.2, + "time": 1704330000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 240, + "wind_direction_cardinal": "WSW", + "wind_gust": 6.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -7.0, + "icon": "partly-cloudy-night", + "local_day": 3, + "local_hour": 19, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 79, + "sea_level_pressure": 1013.5, + "time": 1704333600, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 240, + "wind_direction_cardinal": "WSW", + "wind_gust": 6.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 3, + "local_hour": 20, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 82, + "sea_level_pressure": 1013.8, + "time": 1704337200, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 240, + "wind_direction_cardinal": "WSW", + "wind_gust": 6.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 3, + "local_hour": 21, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 85, + "sea_level_pressure": 1014.0, + "time": 1704340800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 6.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 3, + "local_hour": 22, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 85, + "sea_level_pressure": 1014.1, + "time": 1704344400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 6.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 3, + "local_hour": 23, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 86, + "sea_level_pressure": 1014.3, + "time": 1704348000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 6.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 4, + "local_hour": 0, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 87, + "sea_level_pressure": 1014.6, + "time": 1704351600, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 6.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 4, + "local_hour": 1, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 87, + "sea_level_pressure": 1015.0, + "time": 1704355200, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 6.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 4, + "local_hour": 2, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 88, + "sea_level_pressure": 1015.3, + "time": 1704358800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 4, + "local_hour": 3, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 89, + "sea_level_pressure": 1015.7, + "time": 1704362400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 4, + "local_hour": 4, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 89, + "sea_level_pressure": 1016.0, + "time": 1704366000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 4, + "local_hour": 5, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 88, + "sea_level_pressure": 1016.4, + "time": 1704369600, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 4, + "local_hour": 6, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 86, + "sea_level_pressure": 1016.9, + "time": 1704373200, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 4, + "local_hour": 7, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 84, + "sea_level_pressure": 1017.4, + "time": 1704376800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -7.0, + "icon": "partly-cloudy-day", + "local_day": 4, + "local_hour": 8, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 78, + "sea_level_pressure": 1018.0, + "time": 1704380400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-day", + "local_day": 4, + "local_hour": 9, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 72, + "sea_level_pressure": 1016.3, + "time": 1704384000, + "uv": 1.0, + "wind_avg": 5.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-day", + "local_day": 4, + "local_hour": 10, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 63, + "sea_level_pressure": 1014.6, + "time": 1704387600, + "uv": 1.0, + "wind_avg": 5.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -2.0, + "icon": "partly-cloudy-day", + "local_day": 4, + "local_hour": 11, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 58, + "sea_level_pressure": 1013.0, + "time": 1704391200, + "uv": 1.0, + "wind_avg": 5.0, + "wind_direction": 280, + "wind_direction_cardinal": "W", + "wind_gust": 8.0 + }, + { + "air_temperature": 3.0, + "conditions": "Partly Cloudy", + "feels_like": -1.0, + "icon": "partly-cloudy-day", + "local_day": 4, + "local_hour": 12, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 54, + "sea_level_pressure": 1011.6, + "time": 1704394800, + "uv": 4.0, + "wind_avg": 5.0, + "wind_direction": 300, + "wind_direction_cardinal": "WNW", + "wind_gust": 8.0 + }, + { + "air_temperature": 4.0, + "conditions": "Partly Cloudy", + "feels_like": -1.0, + "icon": "partly-cloudy-day", + "local_day": 4, + "local_hour": 13, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 15, + "precip_type": "sleet", + "relative_humidity": 53, + "sea_level_pressure": 1010.2, + "time": 1704398400, + "uv": 4.0, + "wind_avg": 5.0, + "wind_direction": 300, + "wind_direction_cardinal": "WNW", + "wind_gust": 8.0 + }, + { + "air_temperature": 4.0, + "conditions": "Partly Cloudy", + "feels_like": 0.0, + "icon": "partly-cloudy-day", + "local_day": 4, + "local_hour": 14, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 15, + "precip_type": "sleet", + "relative_humidity": 53, + "sea_level_pressure": 1008.8, + "time": 1704402000, + "uv": 4.0, + "wind_avg": 5.0, + "wind_direction": 300, + "wind_direction_cardinal": "WNW", + "wind_gust": 8.0 + }, + { + "air_temperature": 3.0, + "conditions": "Partly Cloudy", + "feels_like": -1.0, + "icon": "partly-cloudy-day", + "local_day": 4, + "local_hour": 15, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 15, + "precip_type": "sleet", + "relative_humidity": 55, + "sea_level_pressure": 1009.6, + "time": 1704405600, + "uv": 3.0, + "wind_avg": 5.0, + "wind_direction": 330, + "wind_direction_cardinal": "NNW", + "wind_gust": 7.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-day", + "local_day": 4, + "local_hour": 16, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 15, + "precip_type": "sleet", + "relative_humidity": 60, + "sea_level_pressure": 1010.3, + "time": 1704409200, + "uv": 3.0, + "wind_avg": 5.0, + "wind_direction": 330, + "wind_direction_cardinal": "NNW", + "wind_gust": 7.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-night", + "local_day": 4, + "local_hour": 17, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 15, + "precip_type": "sleet", + "relative_humidity": 65, + "sea_level_pressure": 1011.0, + "time": 1704412800, + "uv": 3.0, + "wind_avg": 5.0, + "wind_direction": 330, + "wind_direction_cardinal": "NNW", + "wind_gust": 7.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -5.0, + "icon": "partly-cloudy-night", + "local_day": 4, + "local_hour": 18, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 15, + "precip_type": "sleet", + "relative_humidity": 70, + "sea_level_pressure": 1011.2, + "time": 1704416400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 7.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 4, + "local_hour": 19, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 15, + "precip_type": "sleet", + "relative_humidity": 75, + "sea_level_pressure": 1011.4, + "time": 1704420000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 7.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -7.0, + "icon": "partly-cloudy-night", + "local_day": 4, + "local_hour": 20, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 15, + "precip_type": "sleet", + "relative_humidity": 80, + "sea_level_pressure": 1011.6, + "time": 1704423600, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 320, + "wind_direction_cardinal": "NW", + "wind_gust": 7.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 4, + "local_hour": 21, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 15, + "precip_type": "sleet", + "relative_humidity": 84, + "sea_level_pressure": 1011.7, + "time": 1704427200, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 300, + "wind_direction_cardinal": "WNW", + "wind_gust": 7.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 4, + "local_hour": 22, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 15, + "precip_type": "sleet", + "relative_humidity": 86, + "sea_level_pressure": 1011.8, + "time": 1704430800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 300, + "wind_direction_cardinal": "WNW", + "wind_gust": 7.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 4, + "local_hour": 23, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 15, + "precip_type": "sleet", + "relative_humidity": 88, + "sea_level_pressure": 1011.9, + "time": 1704434400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 300, + "wind_direction_cardinal": "WNW", + "wind_gust": 6.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 5, + "local_hour": 0, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 10, + "precip_type": "sleet", + "relative_humidity": 90, + "sea_level_pressure": 1012.6, + "time": 1704438000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 6.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -10.0, + "icon": "partly-cloudy-night", + "local_day": 5, + "local_hour": 1, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 92, + "sea_level_pressure": 1013.3, + "time": 1704441600, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 6.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -10.0, + "icon": "partly-cloudy-night", + "local_day": 5, + "local_hour": 2, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 93, + "sea_level_pressure": 1014.0, + "time": 1704445200, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 6.0 + }, + { + "air_temperature": -5.0, + "conditions": "Partly Cloudy", + "feels_like": -10.0, + "icon": "partly-cloudy-night", + "local_day": 5, + "local_hour": 3, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 95, + "sea_level_pressure": 1014.7, + "time": 1704448800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 6.0 + }, + { + "air_temperature": -5.0, + "conditions": "Partly Cloudy", + "feels_like": -10.0, + "icon": "partly-cloudy-night", + "local_day": 5, + "local_hour": 4, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 96, + "sea_level_pressure": 1015.4, + "time": 1704452400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 6.0 + }, + { + "air_temperature": -5.0, + "conditions": "Partly Cloudy", + "feels_like": -10.0, + "icon": "partly-cloudy-night", + "local_day": 5, + "local_hour": 5, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 95, + "sea_level_pressure": 1016.1, + "time": 1704456000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 290, + "wind_direction_cardinal": "WNW", + "wind_gust": 6.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 5, + "local_hour": 6, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 91, + "sea_level_pressure": 1015.9, + "time": 1704459600, + "uv": 1.0, + "wind_avg": 4.0, + "wind_direction": 240, + "wind_direction_cardinal": "WSW", + "wind_gust": 7.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 5, + "local_hour": 7, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 85, + "sea_level_pressure": 1015.7, + "time": 1704463200, + "uv": 1.0, + "wind_avg": 4.0, + "wind_direction": 240, + "wind_direction_cardinal": "WSW", + "wind_gust": 7.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -7.0, + "icon": "partly-cloudy-day", + "local_day": 5, + "local_hour": 8, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 78, + "sea_level_pressure": 1015.4, + "time": 1704466800, + "uv": 1.0, + "wind_avg": 5.0, + "wind_direction": 240, + "wind_direction_cardinal": "WSW", + "wind_gust": 7.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-day", + "local_day": 5, + "local_hour": 9, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 71, + "sea_level_pressure": 1015.2, + "time": 1704470400, + "uv": 1.0, + "wind_avg": 5.0, + "wind_direction": 240, + "wind_direction_cardinal": "WSW", + "wind_gust": 7.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-day", + "local_day": 5, + "local_hour": 10, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 66, + "sea_level_pressure": 1015.0, + "time": 1704474000, + "uv": 1.0, + "wind_avg": 5.0, + "wind_direction": 240, + "wind_direction_cardinal": "WSW", + "wind_gust": 8.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-day", + "local_day": 5, + "local_hour": 11, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 63, + "sea_level_pressure": 1014.7, + "time": 1704477600, + "uv": 1.0, + "wind_avg": 5.0, + "wind_direction": 240, + "wind_direction_cardinal": "WSW", + "wind_gust": 8.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-day", + "local_day": 5, + "local_hour": 12, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 61, + "sea_level_pressure": 1015.0, + "time": 1704481200, + "uv": 3.0, + "wind_avg": 5.0, + "wind_direction": 190, + "wind_direction_cardinal": "S", + "wind_gust": 8.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-day", + "local_day": 5, + "local_hour": 13, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 61, + "sea_level_pressure": 1015.2, + "time": 1704484800, + "uv": 3.0, + "wind_avg": 5.0, + "wind_direction": 190, + "wind_direction_cardinal": "S", + "wind_gust": 7.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-day", + "local_day": 5, + "local_hour": 14, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 62, + "sea_level_pressure": 1015.5, + "time": 1704488400, + "uv": 3.0, + "wind_avg": 5.0, + "wind_direction": 190, + "wind_direction_cardinal": "S", + "wind_gust": 7.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-day", + "local_day": 5, + "local_hour": 15, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 65, + "sea_level_pressure": 1015.7, + "time": 1704492000, + "uv": 3.0, + "wind_avg": 5.0, + "wind_direction": 190, + "wind_direction_cardinal": "S", + "wind_gust": 7.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -5.0, + "icon": "partly-cloudy-day", + "local_day": 5, + "local_hour": 16, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 67, + "sea_level_pressure": 1015.9, + "time": 1704495600, + "uv": 3.0, + "wind_avg": 5.0, + "wind_direction": 190, + "wind_direction_cardinal": "S", + "wind_gust": 7.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -5.0, + "icon": "partly-cloudy-night", + "local_day": 5, + "local_hour": 17, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 70, + "sea_level_pressure": 1016.2, + "time": 1704499200, + "uv": 3.0, + "wind_avg": 5.0, + "wind_direction": 190, + "wind_direction_cardinal": "S", + "wind_gust": 7.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 5, + "local_hour": 18, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 73, + "sea_level_pressure": 1016.1, + "time": 1704502800, + "uv": 0.0, + "wind_avg": 5.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -7.0, + "icon": "partly-cloudy-night", + "local_day": 5, + "local_hour": 19, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 77, + "sea_level_pressure": 1016.0, + "time": 1704506400, + "uv": 0.0, + "wind_avg": 5.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 5, + "local_hour": 20, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 80, + "sea_level_pressure": 1015.8, + "time": 1704510000, + "uv": 0.0, + "wind_avg": 5.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -8.0, + "icon": "partly-cloudy-night", + "local_day": 5, + "local_hour": 21, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 85, + "sea_level_pressure": 1015.7, + "time": 1704513600, + "uv": 0.0, + "wind_avg": 5.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 5, + "local_hour": 22, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 89, + "sea_level_pressure": 1015.6, + "time": 1704517200, + "uv": 0.0, + "wind_avg": 5.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 6.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -10.0, + "icon": "partly-cloudy-night", + "local_day": 5, + "local_hour": 23, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 92, + "sea_level_pressure": 1015.5, + "time": 1704520800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 6.0 + }, + { + "air_temperature": -5.0, + "conditions": "Partly Cloudy", + "feels_like": -10.0, + "icon": "partly-cloudy-night", + "local_day": 6, + "local_hour": 0, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 95, + "sea_level_pressure": 1015.6, + "time": 1704524400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 6.0 + }, + { + "air_temperature": -5.0, + "conditions": "Partly Cloudy", + "feels_like": -11.0, + "icon": "partly-cloudy-night", + "local_day": 6, + "local_hour": 1, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 98, + "sea_level_pressure": 1015.7, + "time": 1704528000, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -5.0, + "conditions": "Partly Cloudy", + "feels_like": -11.0, + "icon": "partly-cloudy-night", + "local_day": 6, + "local_hour": 2, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 99, + "sea_level_pressure": 1015.7, + "time": 1704531600, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -5.0, + "conditions": "Partly Cloudy", + "feels_like": -11.0, + "icon": "partly-cloudy-night", + "local_day": 6, + "local_hour": 3, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 100, + "sea_level_pressure": 1015.8, + "time": 1704535200, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -5.0, + "conditions": "Partly Cloudy", + "feels_like": -11.0, + "icon": "partly-cloudy-night", + "local_day": 6, + "local_hour": 4, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 98, + "sea_level_pressure": 1015.9, + "time": 1704538800, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -4.0, + "conditions": "Partly Cloudy", + "feels_like": -10.0, + "icon": "partly-cloudy-night", + "local_day": 6, + "local_hour": 5, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 93, + "sea_level_pressure": 1016.0, + "time": 1704542400, + "uv": 0.0, + "wind_avg": 4.0, + "wind_direction": 270, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -3.0, + "conditions": "Partly Cloudy", + "feels_like": -9.0, + "icon": "partly-cloudy-night", + "local_day": 6, + "local_hour": 6, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 86, + "sea_level_pressure": 1015.8, + "time": 1704546000, + "uv": 1.0, + "wind_avg": 5.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -7.0, + "icon": "partly-cloudy-night", + "local_day": 6, + "local_hour": 7, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 78, + "sea_level_pressure": 1015.7, + "time": 1704549600, + "uv": 1.0, + "wind_avg": 5.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 7.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -5.0, + "icon": "partly-cloudy-day", + "local_day": 6, + "local_hour": 8, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 69, + "sea_level_pressure": 1015.6, + "time": 1704553200, + "uv": 1.0, + "wind_avg": 5.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 8.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-day", + "local_day": 6, + "local_hour": 9, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 61, + "sea_level_pressure": 1015.5, + "time": 1704556800, + "uv": 1.0, + "wind_avg": 5.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 8.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -2.0, + "icon": "partly-cloudy-day", + "local_day": 6, + "local_hour": 10, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 56, + "sea_level_pressure": 1015.4, + "time": 1704560400, + "uv": 1.0, + "wind_avg": 5.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 8.0 + }, + { + "air_temperature": 3.0, + "conditions": "Partly Cloudy", + "feels_like": -1.0, + "icon": "partly-cloudy-day", + "local_day": 6, + "local_hour": 11, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 53, + "sea_level_pressure": 1015.2, + "time": 1704564000, + "uv": 1.0, + "wind_avg": 5.0, + "wind_direction": 260, + "wind_direction_cardinal": "W", + "wind_gust": 8.0 + }, + { + "air_temperature": 4.0, + "conditions": "Partly Cloudy", + "feels_like": 0.0, + "icon": "partly-cloudy-day", + "local_day": 6, + "local_hour": 12, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 51, + "sea_level_pressure": 1015.1, + "time": 1704567600, + "uv": 3.0, + "wind_avg": 5.0, + "wind_direction": 230, + "wind_direction_cardinal": "SW", + "wind_gust": 8.0 + }, + { + "air_temperature": 4.0, + "conditions": "Partly Cloudy", + "feels_like": 0.0, + "icon": "partly-cloudy-day", + "local_day": 6, + "local_hour": 13, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 51, + "sea_level_pressure": 1015.0, + "time": 1704571200, + "uv": 3.0, + "wind_avg": 5.0, + "wind_direction": 230, + "wind_direction_cardinal": "SW", + "wind_gust": 8.0 + }, + { + "air_temperature": 4.0, + "conditions": "Partly Cloudy", + "feels_like": 0.0, + "icon": "partly-cloudy-day", + "local_day": 6, + "local_hour": 14, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 53, + "sea_level_pressure": 1014.8, + "time": 1704574800, + "uv": 3.0, + "wind_avg": 5.0, + "wind_direction": 230, + "wind_direction_cardinal": "SW", + "wind_gust": 7.0 + }, + { + "air_temperature": 3.0, + "conditions": "Partly Cloudy", + "feels_like": -1.0, + "icon": "partly-cloudy-day", + "local_day": 6, + "local_hour": 15, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 55, + "sea_level_pressure": 1014.7, + "time": 1704578400, + "uv": 3.0, + "wind_avg": 5.0, + "wind_direction": 230, + "wind_direction_cardinal": "SW", + "wind_gust": 7.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -2.0, + "icon": "partly-cloudy-day", + "local_day": 6, + "local_hour": 16, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 57, + "sea_level_pressure": 1014.5, + "time": 1704582000, + "uv": 3.0, + "wind_avg": 5.0, + "wind_direction": 230, + "wind_direction_cardinal": "SW", + "wind_gust": 7.0 + }, + { + "air_temperature": 2.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-night", + "local_day": 6, + "local_hour": 17, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 59, + "sea_level_pressure": 1014.4, + "time": 1704585600, + "uv": 3.0, + "wind_avg": 5.0, + "wind_direction": 230, + "wind_direction_cardinal": "SW", + "wind_gust": 7.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -3.0, + "icon": "partly-cloudy-night", + "local_day": 6, + "local_hour": 18, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 5, + "precip_type": "sleet", + "relative_humidity": 62, + "sea_level_pressure": 1013.9, + "time": 1704589200, + "uv": 0.0, + "wind_avg": 5.0, + "wind_direction": 250, + "wind_direction_cardinal": "WSW", + "wind_gust": 7.0 + }, + { + "air_temperature": 1.0, + "conditions": "Partly Cloudy", + "feels_like": -4.0, + "icon": "partly-cloudy-night", + "local_day": 6, + "local_hour": 19, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 65, + "sea_level_pressure": 1013.4, + "time": 1704592800, + "uv": 0.0, + "wind_avg": 5.0, + "wind_direction": 250, + "wind_direction_cardinal": "WSW", + "wind_gust": 7.0 + }, + { + "air_temperature": 0.0, + "conditions": "Partly Cloudy", + "feels_like": -5.0, + "icon": "partly-cloudy-night", + "local_day": 6, + "local_hour": 20, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 68, + "sea_level_pressure": 1012.9, + "time": 1704596400, + "uv": 0.0, + "wind_avg": 5.0, + "wind_direction": 250, + "wind_direction_cardinal": "WSW", + "wind_gust": 7.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 6, + "local_hour": 21, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 71, + "sea_level_pressure": 1012.4, + "time": 1704600000, + "uv": 0.0, + "wind_avg": 5.0, + "wind_direction": 250, + "wind_direction_cardinal": "WSW", + "wind_gust": 7.0 + }, + { + "air_temperature": -1.0, + "conditions": "Partly Cloudy", + "feels_like": -6.0, + "icon": "partly-cloudy-night", + "local_day": 6, + "local_hour": 22, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 73, + "sea_level_pressure": 1011.9, + "time": 1704603600, + "uv": 0.0, + "wind_avg": 5.0, + "wind_direction": 250, + "wind_direction_cardinal": "WSW", + "wind_gust": 7.0 + }, + { + "air_temperature": -2.0, + "conditions": "Partly Cloudy", + "feels_like": -7.0, + "icon": "partly-cloudy-night", + "local_day": 6, + "local_hour": 23, + "precip": 0, + "precip_icon": "chance-sleet", + "precip_probability": 0, + "precip_type": "sleet", + "relative_humidity": 75, + "sea_level_pressure": 1011.4, + "time": 1704607200, + "uv": 0.0, + "wind_avg": 5.0, + "wind_direction": 250, + "wind_direction_cardinal": "WSW", + "wind_gust": 7.0 + } + ] + }, + "latitude": 43.94962, + "location_name": "My Home Station", + "longitude": -102.86831, + "source_id_conditions": 5, + "status": { + "status_code": 0, + "status_message": "SUCCESS" + }, + "timezone": "America/Denver", + "timezone_offset_minutes": -420, + "units": { + "units_air_density": "kg/m3", + "units_brightness": "lux", + "units_distance": "km", + "units_other": "metric", + "units_precip": "mm", + "units_pressure": "mb", + "units_solar_radiation": "w/m2", + "units_temp": "c", + "units_wind": "mps" + } +} diff --git a/tests/components/weatherflow_cloud/fixtures/station_observation.json b/tests/components/weatherflow_cloud/fixtures/station_observation.json new file mode 100644 index 00000000000000..148b180df738cd --- /dev/null +++ b/tests/components/weatherflow_cloud/fixtures/station_observation.json @@ -0,0 +1,100 @@ +{ + "elevation": 2063.150146484375, + "is_public": true, + "latitude": 43.94962, + "longitude": -102.86831, + "obs": [ + { + "air_density": 0.96139, + "air_temperature": 10.5, + "barometric_pressure": 782.8, + "brightness": 757, + "delta_t": 8.4, + "dew_point": -10.4, + "feels_like": 10.5, + "heat_index": 10.5, + "lightning_strike_count": 0, + "lightning_strike_count_last_1hr": 0, + "lightning_strike_count_last_3hr": 0, + "lightning_strike_last_distance": 26, + "lightning_strike_last_epoch": 1707346875, + "precip": 0.0, + "precip_accum_last_1hr": 0.0, + "precip_accum_local_day": 0.0, + "precip_accum_local_day_final": 0.0, + "precip_accum_local_yesterday": 0.0, + "precip_accum_local_yesterday_final": 0.0, + "precip_analysis_type_yesterday": 0, + "precip_minutes_local_day": 0, + "precip_minutes_local_yesterday": 0, + "precip_minutes_local_yesterday_final": 0, + "pressure_trend": "steady", + "relative_humidity": 22, + "sea_level_pressure": 1006.2, + "solar_radiation": 6, + "station_pressure": 782.8, + "timestamp": 1708994629, + "uv": 0.03, + "wet_bulb_globe_temperature": 4.6, + "wet_bulb_temperature": 2.1, + "wind_avg": 1.4, + "wind_chill": 10.5, + "wind_direction": 203, + "wind_gust": 3.2, + "wind_lull": 0.3 + } + ], + "outdoor_keys": [ + "timestamp", + "air_temperature", + "barometric_pressure", + "station_pressure", + "pressure_trend", + "sea_level_pressure", + "relative_humidity", + "precip", + "precip_accum_last_1hr", + "precip_accum_local_day", + "precip_accum_local_day_final", + "precip_accum_local_yesterday_final", + "precip_minutes_local_day", + "precip_minutes_local_yesterday_final", + "wind_avg", + "wind_direction", + "wind_gust", + "wind_lull", + "solar_radiation", + "uv", + "brightness", + "lightning_strike_last_epoch", + "lightning_strike_last_distance", + "lightning_strike_count", + "lightning_strike_count_last_1hr", + "lightning_strike_count_last_3hr", + "feels_like", + "heat_index", + "wind_chill", + "dew_point", + "wet_bulb_temperature", + "wet_bulb_globe_temperature", + "delta_t", + "air_density" + ], + "public_name": "My Home Station", + "station_id": 24432, + "station_name": "My Home Station", + "station_units": { + "units_direction": "degrees", + "units_distance": "mi", + "units_other": "metric", + "units_precip": "in", + "units_pressure": "hpa", + "units_temp": "f", + "units_wind": "bft" + }, + "status": { + "status_code": 0, + "status_message": "SUCCESS" + }, + "timezone": "America/Denver" +} diff --git a/tests/components/weatherflow_cloud/fixtures/stations.json b/tests/components/weatherflow_cloud/fixtures/stations.json new file mode 100644 index 00000000000000..e0ca96bd240df3 --- /dev/null +++ b/tests/components/weatherflow_cloud/fixtures/stations.json @@ -0,0 +1,132 @@ +{ + "stations": [ + { + "created_epoch": 1658343273, + "devices": [ + { + "device_id": 7654321, + "device_meta": { + "agl": 1.8288, + "environment": "indoor", + "name": "HB-00068123", + "wifi_network_name": "" + }, + "device_type": "HB", + "firmware_revision": "177", + "hardware_revision": "1", + "location_id": 24432, + "serial_number": "HB-00068123" + }, + { + "device_id": 123456, + "device_meta": { + "agl": 1.8288, + "environment": "outdoor", + "name": "ST-11084623", + "wifi_network_name": "" + }, + "device_settings": { + "show_precip_final": true + }, + "device_type": "ST", + "firmware_revision": "172", + "hardware_revision": "1", + "location_id": 24432, + "serial_number": "ST-11084623" + } + ], + "is_local_mode": false, + "last_modified_epoch": 1658344464, + "latitude": 43.94962, + "location_id": 24432, + "longitude": -102.86831, + "name": "My Home Station", + "public_name": "My Home Station", + "station_id": 24432, + "station_items": [ + { + "device_id": 123456, + "item": "air_temperature_humidity", + "location_id": 24432, + "location_item_id": 657904, + "sort": 0, + "station_id": 24432, + "station_item_id": 657904 + }, + { + "device_id": 123456, + "item": "barometric_pressure", + "location_id": 24432, + "location_item_id": 657906, + "sort": 3, + "station_id": 24432, + "station_item_id": 657906 + }, + { + "device_id": 7654321, + "item": "diagnostics", + "location_id": 24432, + "location_item_id": 657912, + "station_id": 24432, + "station_item_id": 657912 + }, + { + "device_id": 123456, + "item": "diagnostics", + "location_id": 24432, + "location_item_id": 657913, + "sort": 6, + "station_id": 24432, + "station_item_id": 657913 + }, + { + "device_id": 123456, + "item": "light", + "location_id": 24432, + "location_item_id": 657908, + "sort": 2, + "station_id": 24432, + "station_item_id": 657908 + }, + { + "device_id": 123456, + "item": "lightning", + "location_id": 24432, + "location_item_id": 657905, + "sort": 4, + "station_id": 24432, + "station_item_id": 657905 + }, + { + "device_id": 123456, + "item": "rain", + "location_id": 24432, + "location_item_id": 657907, + "sort": 5, + "station_id": 24432, + "station_item_id": 657907 + }, + { + "device_id": 123456, + "item": "wind", + "location_id": 24432, + "location_item_id": 657909, + "sort": 1, + "station_id": 24432, + "station_item_id": 657909 + } + ], + "station_meta": { + "elevation": 2063.150146484375, + "share_with_wf": true, + "share_with_wu": true + }, + "timezone": "America/Denver", + "timezone_offset_minutes": -420 + } + ], + "status": { + "status_code": 0, + "status_message": "SUCCESS" + } +} diff --git a/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr b/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr new file mode 100644 index 00000000000000..f560c4a47735a7 --- /dev/null +++ b/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr @@ -0,0 +1,806 @@ +# serializer version: 1 +# name: test_all_entities[sensor.my_home_station_atmospheric_pressure-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_atmospheric_pressure', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 3, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Atmospheric pressure', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'barometric_pressure', + 'unique_id': '24432_barometric_pressure', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_atmospheric_pressure-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'atmospheric_pressure', + 'friendly_name': 'My Home Station Atmospheric pressure', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_atmospheric_pressure', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '782.8', + }) +# --- +# name: test_all_entities[sensor.my_home_station_atmospheric_pressure_2-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_atmospheric_pressure_2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 3, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Atmospheric pressure', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'sea_level_pressure', + 'unique_id': '24432_sea_level_pressure', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_atmospheric_pressure_2-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'atmospheric_pressure', + 'friendly_name': 'My Home Station Atmospheric pressure', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_atmospheric_pressure_2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1006.2', + }) +# --- +# name: test_all_entities[sensor.my_home_station_distance-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_distance', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Distance', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'lightning_strike_last_distance', + 'unique_id': '24432_lightning_strike_last_distance', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_distance-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'distance', + 'friendly_name': 'My Home Station Distance', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_distance', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '26', + }) +# --- +# name: test_all_entities[sensor.my_home_station_none-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_none', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 5, + }), + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': None, + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'air_density', + 'unique_id': '24432_air_density', + 'unit_of_measurement': 'kg/m³', + }) +# --- +# name: test_all_entities[sensor.my_home_station_none-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'friendly_name': 'My Home Station None', + 'state_class': , + 'unit_of_measurement': 'kg/m³', + }), + 'context': , + 'entity_id': 'sensor.my_home_station_none', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.96139', + }) +# --- +# name: test_all_entities[sensor.my_home_station_none_2-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_none_2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': None, + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'lightning_strike_count', + 'unique_id': '24432_lightning_strike_count', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.my_home_station_none_2-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'friendly_name': 'My Home Station None', + 'state_class': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_none_2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- +# name: test_all_entities[sensor.my_home_station_none_3-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_none_3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': None, + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'lightning_strike_count_last_1hr', + 'unique_id': '24432_lightning_strike_count_last_1hr', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.my_home_station_none_3-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'friendly_name': 'My Home Station None', + 'state_class': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_none_3', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- +# name: test_all_entities[sensor.my_home_station_none_4-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_none_4', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': None, + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'lightning_strike_count_last_3hr', + 'unique_id': '24432_lightning_strike_count_last_3hr', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.my_home_station_none_4-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'friendly_name': 'My Home Station None', + 'state_class': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_none_4', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- +# name: test_all_entities[sensor.my_home_station_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'air_temperature', + 'unique_id': '24432_air_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'temperature', + 'friendly_name': 'My Home Station Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10.5', + }) +# --- +# name: test_all_entities[sensor.my_home_station_temperature_2-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_temperature_2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'dew_point', + 'unique_id': '24432_dew_point', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_temperature_2-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'temperature', + 'friendly_name': 'My Home Station Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_temperature_2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '-10.4', + }) +# --- +# name: test_all_entities[sensor.my_home_station_temperature_3-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_temperature_3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'feels_like', + 'unique_id': '24432_feels_like', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_temperature_3-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'temperature', + 'friendly_name': 'My Home Station Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_temperature_3', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10.5', + }) +# --- +# name: test_all_entities[sensor.my_home_station_temperature_4-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_temperature_4', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'heat_index', + 'unique_id': '24432_heat_index', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_temperature_4-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'temperature', + 'friendly_name': 'My Home Station Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_temperature_4', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10.5', + }) +# --- +# name: test_all_entities[sensor.my_home_station_temperature_5-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_temperature_5', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'wind_chill', + 'unique_id': '24432_wind_chill', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_temperature_5-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'temperature', + 'friendly_name': 'My Home Station Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_temperature_5', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10.5', + }) +# --- +# name: test_all_entities[sensor.my_home_station_temperature_6-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_temperature_6', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'wet_bulb_temperature', + 'unique_id': '24432_wet_bulb_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_temperature_6-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'temperature', + 'friendly_name': 'My Home Station Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_temperature_6', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2.1', + }) +# --- +# name: test_all_entities[sensor.my_home_station_temperature_7-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_temperature_7', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'wet_bulb_globe_temperature', + 'unique_id': '24432_wet_bulb_globe_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_temperature_7-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'temperature', + 'friendly_name': 'My Home Station Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_temperature_7', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '4.6', + }) +# --- +# name: test_all_entities[sensor.my_home_station_timestamp-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_timestamp', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Timestamp', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'lightning_strike_last_epoch', + 'unique_id': '24432_lightning_strike_last_epoch', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.my_home_station_timestamp-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'timestamp', + 'friendly_name': 'My Home Station Timestamp', + }), + 'context': , + 'entity_id': 'sensor.my_home_station_timestamp', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2024-02-07T23:01:15+00:00', + }) +# --- diff --git a/tests/components/weatherflow_cloud/test_sensor.py b/tests/components/weatherflow_cloud/test_sensor.py new file mode 100644 index 00000000000000..9d041d820bf809 --- /dev/null +++ b/tests/components/weatherflow_cloud/test_sensor.py @@ -0,0 +1,30 @@ +"""Tests for the WeatherFlow Cloud sensor platform.""" + +from unittest.mock import AsyncMock, patch + +from syrupy import SnapshotAssertion + +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from . import setup_integration + +from tests.common import MockConfigEntry, snapshot_platform + + +async def test_all_entities( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + mock_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, + mock_api: AsyncMock, +) -> None: + """Test all entities.""" + with patch( + "homeassistant.components.weatherflow_cloud.PLATFORMS", [Platform.SENSOR] + ): + await setup_integration(hass, mock_config_entry) + await hass.async_block_till_done() + + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) From b1d6a1fc8e117396683e25669f7b60da446ea5d7 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Wed, 10 Jul 2024 15:58:12 -0600 Subject: [PATCH 29/38] snapshot update --- .../snapshots/test_sensor.ambr | 754 +++++++++++++++++- 1 file changed, 752 insertions(+), 2 deletions(-) diff --git a/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr b/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr index f560c4a47735a7..6db24054d408b5 100644 --- a/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr +++ b/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr @@ -1,4 +1,58 @@ # serializer version: 1 +# name: test_all_entities[sensor.my_home_station_air_density-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_air_density', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 5, + }), + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Air density', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'air_density', + 'unique_id': '24432_air_density', + 'unit_of_measurement': 'kg/m³', + }) +# --- +# name: test_all_entities[sensor.my_home_station_air_density-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'friendly_name': 'My Home Station Air density', + 'state_class': , + 'unit_of_measurement': 'kg/m³', + }), + 'context': , + 'entity_id': 'sensor.my_home_station_air_density', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.96139', + }) +# --- # name: test_all_entities[sensor.my_home_station_atmospheric_pressure-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -115,6 +169,61 @@ 'state': '1006.2', }) # --- +# name: test_all_entities[sensor.my_home_station_dew_point-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_dew_point', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Dew point', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'dew_point', + 'unique_id': '24432_dew_point', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_dew_point-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'temperature', + 'friendly_name': 'My Home Station Dew point', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_dew_point', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '-10.4', + }) +# --- # name: test_all_entities[sensor.my_home_station_distance-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -160,11 +269,371 @@ 'unit_of_measurement': , }), 'context': , - 'entity_id': 'sensor.my_home_station_distance', + 'entity_id': 'sensor.my_home_station_distance', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '26', + }) +# --- +# name: test_all_entities[sensor.my_home_station_feels_like-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_feels_like', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Feels like', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'feels_like', + 'unique_id': '24432_feels_like', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_feels_like-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'temperature', + 'friendly_name': 'My Home Station Feels like', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_feels_like', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10.5', + }) +# --- +# name: test_all_entities[sensor.my_home_station_heat_index-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_heat_index', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Heat index', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'heat_index', + 'unique_id': '24432_heat_index', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_heat_index-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'temperature', + 'friendly_name': 'My Home Station Heat index', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_heat_index', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10.5', + }) +# --- +# name: test_all_entities[sensor.my_home_station_lightning_count-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_lightning_count', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Lightning count', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'lightning_strike_count', + 'unique_id': '24432_lightning_strike_count', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.my_home_station_lightning_count-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'friendly_name': 'My Home Station Lightning count', + 'state_class': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_lightning_count', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- +# name: test_all_entities[sensor.my_home_station_lightning_count_last_1_hr-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_lightning_count_last_1_hr', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Lightning count last 1 hr', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'lightning_strike_count_last_1hr', + 'unique_id': '24432_lightning_strike_count_last_1hr', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.my_home_station_lightning_count_last_1_hr-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'friendly_name': 'My Home Station Lightning count last 1 hr', + 'state_class': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_lightning_count_last_1_hr', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- +# name: test_all_entities[sensor.my_home_station_lightning_count_last_3_hr-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_lightning_count_last_3_hr', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Lightning count last 3 hr', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'lightning_strike_count_last_3hr', + 'unique_id': '24432_lightning_strike_count_last_3hr', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.my_home_station_lightning_count_last_3_hr-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'friendly_name': 'My Home Station Lightning count last 3 hr', + 'state_class': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_lightning_count_last_3_hr', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- +# name: test_all_entities[sensor.my_home_station_lightning_last_distance-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_lightning_last_distance', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Lightning last distance', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'lightning_strike_last_distance', + 'unique_id': '24432_lightning_strike_last_distance', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_lightning_last_distance-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'distance', + 'friendly_name': 'My Home Station Lightning last distance', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_lightning_last_distance', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '26', + }) +# --- +# name: test_all_entities[sensor.my_home_station_lightning_last_strike-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_lightning_last_strike', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Lightning last strike', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'lightning_strike_last_epoch', + 'unique_id': '24432_lightning_strike_last_epoch', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.my_home_station_lightning_last_strike-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'timestamp', + 'friendly_name': 'My Home Station Lightning last strike', + }), + 'context': , + 'entity_id': 'sensor.my_home_station_lightning_last_strike', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '26', + 'state': '2024-02-07T23:01:15+00:00', }) # --- # name: test_all_entities[sensor.my_home_station_none-entry] @@ -371,6 +840,122 @@ 'state': '0', }) # --- +# name: test_all_entities[sensor.my_home_station_pressure_barometric-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_pressure_barometric', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 3, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Pressure barometric', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'barometric_pressure', + 'unique_id': '24432_barometric_pressure', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_pressure_barometric-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'atmospheric_pressure', + 'friendly_name': 'My Home Station Pressure barometric', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_pressure_barometric', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '782.8', + }) +# --- +# name: test_all_entities[sensor.my_home_station_pressure_sea_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_pressure_sea_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 3, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Pressure sea level', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'sea_level_pressure', + 'unique_id': '24432_sea_level_pressure', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_pressure_sea_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'atmospheric_pressure', + 'friendly_name': 'My Home Station Pressure sea level', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_pressure_sea_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1006.2', + }) +# --- # name: test_all_entities[sensor.my_home_station_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -804,3 +1389,168 @@ 'state': '2024-02-07T23:01:15+00:00', }) # --- +# name: test_all_entities[sensor.my_home_station_wet_bulb_globe_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_wet_bulb_globe_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Wet bulb globe temperature', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'wet_bulb_globe_temperature', + 'unique_id': '24432_wet_bulb_globe_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_wet_bulb_globe_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'temperature', + 'friendly_name': 'My Home Station Wet bulb globe temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_wet_bulb_globe_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '4.6', + }) +# --- +# name: test_all_entities[sensor.my_home_station_wet_bulb_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_wet_bulb_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Wet bulb temperature', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'wet_bulb_temperature', + 'unique_id': '24432_wet_bulb_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_wet_bulb_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'temperature', + 'friendly_name': 'My Home Station Wet bulb temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_wet_bulb_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2.1', + }) +# --- +# name: test_all_entities[sensor.my_home_station_wind_chill-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.my_home_station_wind_chill', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Wind chill', + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'wind_chill', + 'unique_id': '24432_wind_chill', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.my_home_station_wind_chill-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'device_class': 'temperature', + 'friendly_name': 'My Home Station Wind chill', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.my_home_station_wind_chill', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10.5', + }) +# --- From 90e03d987de8cd410449be2362ff6727b42b8def Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Fri, 12 Jul 2024 10:52:03 -0600 Subject: [PATCH 30/38] added total class --- homeassistant/components/weatherflow_cloud/sensor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index a667cfb1e1290b..69f7779634b607 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -131,19 +131,19 @@ class WeatherFlowCloudSensorEntityDescription( WeatherFlowCloudSensorEntityDescription( key="lightning_strike_count", translation_key="lightning_strike_count", - state_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.TOTAL, value_fn=lambda data: data.lightning_strike_count, ), WeatherFlowCloudSensorEntityDescription( key="lightning_strike_count_last_1hr", translation_key="lightning_strike_count_last_1hr", - state_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.TOTAL, value_fn=lambda data: data.lightning_strike_count_last_1hr, ), WeatherFlowCloudSensorEntityDescription( key="lightning_strike_count_last_3hr", translation_key="lightning_strike_count_last_3hr", - state_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.TOTAL, value_fn=lambda data: data.lightning_strike_count_last_3hr, ), WeatherFlowCloudSensorEntityDescription( From e9ecdc4994d336547385e7de2140592cad453594 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Fri, 12 Jul 2024 12:22:08 -0600 Subject: [PATCH 31/38] updated snapshots --- .../weatherflow_cloud/snapshots/test_sensor.ambr | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr b/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr index 6db24054d408b5..7eb4a617c5520f 100644 --- a/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr +++ b/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr @@ -696,7 +696,7 @@ }), 'area_id': None, 'capabilities': dict({ - 'state_class': , + 'state_class': , }), 'config_entry_id': , 'device_class': None, @@ -730,7 +730,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', 'friendly_name': 'My Home Station None', - 'state_class': , + 'state_class': , }), 'context': , 'entity_id': 'sensor.my_home_station_none_2', @@ -746,7 +746,7 @@ }), 'area_id': None, 'capabilities': dict({ - 'state_class': , + 'state_class': , }), 'config_entry_id': , 'device_class': None, @@ -780,7 +780,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', 'friendly_name': 'My Home Station None', - 'state_class': , + 'state_class': , }), 'context': , 'entity_id': 'sensor.my_home_station_none_3', @@ -796,7 +796,7 @@ }), 'area_id': None, 'capabilities': dict({ - 'state_class': , + 'state_class': , }), 'config_entry_id': , 'device_class': None, @@ -830,7 +830,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', 'friendly_name': 'My Home Station None', - 'state_class': , + 'state_class': , }), 'context': , 'entity_id': 'sensor.my_home_station_none_4', From b35e864219f95cf7d26d319b7f783300a7361316 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Fri, 12 Jul 2024 12:54:59 -0600 Subject: [PATCH 32/38] minor tweak --- homeassistant/components/weatherflow_cloud/sensor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index 69f7779634b607..8a9a13e6b66a49 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -36,6 +36,7 @@ class WeatherFlowCloudSensorEntityDescription( WF_SENSORS: tuple[WeatherFlowCloudSensorEntityDescription, ...] = ( + # Air Sensors WeatherFlowCloudSensorEntityDescription( key="air_density", translation_key="air_density", From ecac7fe74f0877e57dd4cd9c0ddd42dd5fd7ef97 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Fri, 12 Jul 2024 13:00:11 -0600 Subject: [PATCH 33/38] snapshot away --- homeassistant/components/weatherflow_cloud/sensor.py | 6 +++--- .../weatherflow_cloud/snapshots/test_sensor.ambr | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index 8a9a13e6b66a49..98264adf6740f4 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -132,19 +132,19 @@ class WeatherFlowCloudSensorEntityDescription( WeatherFlowCloudSensorEntityDescription( key="lightning_strike_count", translation_key="lightning_strike_count", - state_class=SensorStateClass.TOTAL, + state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.lightning_strike_count, ), WeatherFlowCloudSensorEntityDescription( key="lightning_strike_count_last_1hr", translation_key="lightning_strike_count_last_1hr", - state_class=SensorStateClass.TOTAL, + state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.lightning_strike_count_last_1hr, ), WeatherFlowCloudSensorEntityDescription( key="lightning_strike_count_last_3hr", translation_key="lightning_strike_count_last_3hr", - state_class=SensorStateClass.TOTAL, + state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.lightning_strike_count_last_3hr, ), WeatherFlowCloudSensorEntityDescription( diff --git a/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr b/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr index 7eb4a617c5520f..6db24054d408b5 100644 --- a/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr +++ b/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr @@ -696,7 +696,7 @@ }), 'area_id': None, 'capabilities': dict({ - 'state_class': , + 'state_class': , }), 'config_entry_id': , 'device_class': None, @@ -730,7 +730,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', 'friendly_name': 'My Home Station None', - 'state_class': , + 'state_class': , }), 'context': , 'entity_id': 'sensor.my_home_station_none_2', @@ -746,7 +746,7 @@ }), 'area_id': None, 'capabilities': dict({ - 'state_class': , + 'state_class': , }), 'config_entry_id': , 'device_class': None, @@ -780,7 +780,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', 'friendly_name': 'My Home Station None', - 'state_class': , + 'state_class': , }), 'context': , 'entity_id': 'sensor.my_home_station_none_3', @@ -796,7 +796,7 @@ }), 'area_id': None, 'capabilities': dict({ - 'state_class': , + 'state_class': , }), 'config_entry_id': , 'device_class': None, @@ -830,7 +830,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', 'friendly_name': 'My Home Station None', - 'state_class': , + 'state_class': , }), 'context': , 'entity_id': 'sensor.my_home_station_none_4', From dede7e76e5b42f99e0000c486a8fa9bd1dd2f9c9 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Mon, 15 Jul 2024 10:47:08 -0600 Subject: [PATCH 34/38] adding more coverage --- .../snapshots/test_weather.ambr | 62 +++++++++++++++++++ .../weatherflow_cloud/test_weather.py | 30 +++++++++ 2 files changed, 92 insertions(+) create mode 100644 tests/components/weatherflow_cloud/snapshots/test_weather.ambr create mode 100644 tests/components/weatherflow_cloud/test_weather.py diff --git a/tests/components/weatherflow_cloud/snapshots/test_weather.ambr b/tests/components/weatherflow_cloud/snapshots/test_weather.ambr new file mode 100644 index 00000000000000..569b744529c930 --- /dev/null +++ b/tests/components/weatherflow_cloud/snapshots/test_weather.ambr @@ -0,0 +1,62 @@ +# serializer version: 1 +# name: test_weather[weather.my_home_station-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'weather', + 'entity_category': None, + 'entity_id': 'weather.my_home_station', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': None, + 'platform': 'weatherflow_cloud', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': 'weatherflow_forecast_24432', + 'unit_of_measurement': None, + }) +# --- +# name: test_weather[weather.my_home_station-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', + 'dew_point': -13.0, + 'friendly_name': 'My Home Station', + 'humidity': 27, + 'precipitation_unit': , + 'pressure': 795.8, + 'pressure_unit': , + 'supported_features': , + 'temperature': 4.0, + 'temperature_unit': , + 'uv_index': 2, + 'visibility_unit': , + 'wind_bearing': 40.0, + 'wind_gust_speed': 14.4, + 'wind_speed': 7.2, + 'wind_speed_unit': , + }), + 'context': , + 'entity_id': 'weather.my_home_station', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'sunny', + }) +# --- diff --git a/tests/components/weatherflow_cloud/test_weather.py b/tests/components/weatherflow_cloud/test_weather.py new file mode 100644 index 00000000000000..16c2028873b669 --- /dev/null +++ b/tests/components/weatherflow_cloud/test_weather.py @@ -0,0 +1,30 @@ +"""Tests for the WeatherFlow Cloud sensor platform.""" + +from unittest.mock import AsyncMock, patch + +from syrupy import SnapshotAssertion + +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from . import setup_integration + +from tests.common import MockConfigEntry, snapshot_platform + + +async def test_weather( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + mock_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, + mock_api: AsyncMock, +) -> None: + """Test all entities.""" + with patch( + "homeassistant.components.weatherflow_cloud.PLATFORMS", [Platform.WEATHER] + ): + await setup_integration(hass, mock_config_entry) + await hass.async_block_till_done() + + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) From 97808dfbda917bf23cfea17b95012008990d18f7 Mon Sep 17 00:00:00 2001 From: Jeff Stein Date: Mon, 15 Jul 2024 10:49:10 -0600 Subject: [PATCH 35/38] switch back to total --- homeassistant/components/weatherflow_cloud/sensor.py | 6 +++--- .../weatherflow_cloud/snapshots/test_sensor.ambr | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index 98264adf6740f4..8a9a13e6b66a49 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -132,19 +132,19 @@ class WeatherFlowCloudSensorEntityDescription( WeatherFlowCloudSensorEntityDescription( key="lightning_strike_count", translation_key="lightning_strike_count", - state_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.TOTAL, value_fn=lambda data: data.lightning_strike_count, ), WeatherFlowCloudSensorEntityDescription( key="lightning_strike_count_last_1hr", translation_key="lightning_strike_count_last_1hr", - state_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.TOTAL, value_fn=lambda data: data.lightning_strike_count_last_1hr, ), WeatherFlowCloudSensorEntityDescription( key="lightning_strike_count_last_3hr", translation_key="lightning_strike_count_last_3hr", - state_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.TOTAL, value_fn=lambda data: data.lightning_strike_count_last_3hr, ), WeatherFlowCloudSensorEntityDescription( diff --git a/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr b/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr index 6db24054d408b5..f7b635eb4fa9a5 100644 --- a/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr +++ b/tests/components/weatherflow_cloud/snapshots/test_sensor.ambr @@ -392,7 +392,7 @@ }), 'area_id': None, 'capabilities': dict({ - 'state_class': , + 'state_class': , }), 'config_entry_id': , 'device_class': None, @@ -426,7 +426,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', 'friendly_name': 'My Home Station Lightning count', - 'state_class': , + 'state_class': , }), 'context': , 'entity_id': 'sensor.my_home_station_lightning_count', @@ -442,7 +442,7 @@ }), 'area_id': None, 'capabilities': dict({ - 'state_class': , + 'state_class': , }), 'config_entry_id': , 'device_class': None, @@ -476,7 +476,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', 'friendly_name': 'My Home Station Lightning count last 1 hr', - 'state_class': , + 'state_class': , }), 'context': , 'entity_id': 'sensor.my_home_station_lightning_count_last_1_hr', @@ -492,7 +492,7 @@ }), 'area_id': None, 'capabilities': dict({ - 'state_class': , + 'state_class': , }), 'config_entry_id': , 'device_class': None, @@ -526,7 +526,7 @@ 'attributes': ReadOnlyDict({ 'attribution': 'Weather data delivered by WeatherFlow/Tempest REST Api', 'friendly_name': 'My Home Station Lightning count last 3 hr', - 'state_class': , + 'state_class': , }), 'context': , 'entity_id': 'sensor.my_home_station_lightning_count_last_3_hr', From 008e6cf9d8e100f677551374124e671b5935dd54 Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Fri, 19 Jul 2024 10:21:41 +0200 Subject: [PATCH 36/38] Apply suggestions from code review --- homeassistant/components/weatherflow_cloud/sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index 8a9a13e6b66a49..a2db08e837f813 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -32,7 +32,7 @@ class WeatherFlowCloudSensorEntityDescription( ): """Describes a weatherflow sensor.""" - value_fn: Callable[[Observation], int | str | datetime | None] + value_fn: Callable[[Observation], StateType | datetime] WF_SENSORS: tuple[WeatherFlowCloudSensorEntityDescription, ...] = ( @@ -204,6 +204,6 @@ def __init__( self._attr_unique_id = f"{station_id}_{description.key}" @property - def native_value(self) -> StateType | date | datetime | Decimal | None: + def native_value(self) -> StateType | datetime: """Return the state of the sensor.""" return self.entity_description.value_fn(self.station.observation.obs[0]) From bec068ffe8127835f589a6c1e9bbbdcd3c1bf07e Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Fri, 19 Jul 2024 10:22:24 +0200 Subject: [PATCH 37/38] Apply suggestions from code review --- tests/components/weatherflow_cloud/test_sensor.py | 1 - tests/components/weatherflow_cloud/test_weather.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/components/weatherflow_cloud/test_sensor.py b/tests/components/weatherflow_cloud/test_sensor.py index 9d041d820bf809..35ce098f5a7883 100644 --- a/tests/components/weatherflow_cloud/test_sensor.py +++ b/tests/components/weatherflow_cloud/test_sensor.py @@ -25,6 +25,5 @@ async def test_all_entities( "homeassistant.components.weatherflow_cloud.PLATFORMS", [Platform.SENSOR] ): await setup_integration(hass, mock_config_entry) - await hass.async_block_till_done() await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) diff --git a/tests/components/weatherflow_cloud/test_weather.py b/tests/components/weatherflow_cloud/test_weather.py index 16c2028873b669..04da96df423f74 100644 --- a/tests/components/weatherflow_cloud/test_weather.py +++ b/tests/components/weatherflow_cloud/test_weather.py @@ -1,4 +1,4 @@ -"""Tests for the WeatherFlow Cloud sensor platform.""" +"""Tests for the WeatherFlow Cloud weather platform.""" from unittest.mock import AsyncMock, patch @@ -25,6 +25,5 @@ async def test_weather( "homeassistant.components.weatherflow_cloud.PLATFORMS", [Platform.WEATHER] ): await setup_integration(hass, mock_config_entry) - await hass.async_block_till_done() await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) From 341b141525246044a4c512503e65048baa380162 Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Fri, 19 Jul 2024 10:24:27 +0200 Subject: [PATCH 38/38] Apply suggestions from code review --- homeassistant/components/weatherflow_cloud/sensor.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/weatherflow_cloud/sensor.py b/homeassistant/components/weatherflow_cloud/sensor.py index a2db08e837f813..9314c77a65c4bf 100644 --- a/homeassistant/components/weatherflow_cloud/sensor.py +++ b/homeassistant/components/weatherflow_cloud/sensor.py @@ -4,8 +4,7 @@ from collections.abc import Callable from dataclasses import dataclass -from datetime import UTC, date, datetime -from decimal import Decimal +from datetime import UTC, datetime from weatherflow4py.models.rest.observation import Observation