mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 00:07:10 +00:00
Add Sensors to Weatherflow Cloud (#111651)
* continue * Rebase dev * signle function to generate attr_entity info * rewrite icon determination as an if block * handling PR * Removing wind sensors for now - separate future PR * ruff * Update coordinator.py Thought i already did this * Update sensor.py * Update icons.json * Update sensor.py * Update homeassistant/components/weatherflow_cloud/entity.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * working on a unified entity * working on a unified entity * sensor refactor * addressing entity comment * Update homeassistant/components/weatherflow_cloud/entity.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Update homeassistant/components/weatherflow_cloud/sensor.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * doc * pr comments again * fixing PR * fixing PR * applying entity class in sensor * Update homeassistant/components/weatherflow_cloud/sensor.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * Cleaning up weather.py * station id cleanup for weather class * rewrite adding sensors the correct way * Adding snapshot testing * snapshot update * added total class * updated snapshots * minor tweak * snapshot away * adding more coverage * switch back to total * Apply suggestions from code review * Apply suggestions from code review * Apply suggestions from code review --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
7810dc213a
commit
de18be235d
@ -9,7 +9,7 @@ from homeassistant.core import HomeAssistant
|
||||
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:
|
||||
|
@ -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=60),
|
||||
)
|
||||
|
||||
async def _async_update_data(self) -> dict[int, WeatherFlowDataREST]:
|
||||
|
38
homeassistant/components/weatherflow_cloud/entity.py
Normal file
38
homeassistant/components/weatherflow_cloud/entity.py
Normal file
@ -0,0 +1,38 @@
|
||||
"""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
|
||||
|
||||
from .const import ATTR_ATTRIBUTION, DOMAIN, MANUFACTURER
|
||||
from .coordinator import WeatherFlowCloudDataUpdateCoordinator
|
||||
|
||||
|
||||
class WeatherFlowCloudEntity(CoordinatorEntity[WeatherFlowCloudDataUpdateCoordinator]):
|
||||
"""Base entity class to use for everything."""
|
||||
|
||||
_attr_attribution = ATTR_ATTRIBUTION
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: WeatherFlowCloudDataUpdateCoordinator,
|
||||
station_id: int,
|
||||
) -> None:
|
||||
"""Class initializer."""
|
||||
super().__init__(coordinator)
|
||||
self.station_id = station_id
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
name=self.station.station.name,
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
identifiers={(DOMAIN, str(station_id))},
|
||||
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]
|
42
homeassistant/components/weatherflow_cloud/icons.json
Normal file
42
homeassistant/components/weatherflow_cloud/icons.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -5,5 +5,6 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/weatherflow_cloud",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["weatherflow4py"],
|
||||
"requirements": ["weatherflow4py==0.2.21"]
|
||||
}
|
||||
|
208
homeassistant/components/weatherflow_cloud/sensor.py
Normal file
208
homeassistant/components/weatherflow_cloud/sensor.py
Normal file
@ -0,0 +1,208 @@
|
||||
"""Sensors for cloud based weatherflow."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from weatherflow4py.models.rest.observation import Observation
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
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
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import WeatherFlowCloudDataUpdateCoordinator
|
||||
from .entity import WeatherFlowCloudEntity
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class WeatherFlowCloudSensorEntityDescription(
|
||||
SensorEntityDescription,
|
||||
):
|
||||
"""Describes a weatherflow sensor."""
|
||||
|
||||
value_fn: Callable[[Observation], StateType | datetime]
|
||||
|
||||
|
||||
WF_SENSORS: tuple[WeatherFlowCloudSensorEntityDescription, ...] = (
|
||||
# Air Sensors
|
||||
WeatherFlowCloudSensorEntityDescription(
|
||||
key="air_density",
|
||||
translation_key="air_density",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=5,
|
||||
value_fn=lambda data: data.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.air_temperature,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
),
|
||||
WeatherFlowCloudSensorEntityDescription(
|
||||
key="dew_point",
|
||||
translation_key="dew_point",
|
||||
value_fn=lambda data: data.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.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.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.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.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.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.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.sea_level_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",
|
||||
translation_key="lightning_strike_count",
|
||||
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.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.TOTAL,
|
||||
value_fn=lambda data: data.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.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.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()
|
||||
|
||||
async_add_entities(
|
||||
WeatherFlowCloudSensor(coordinator, sensor_description, station_id)
|
||||
for station_id in stations
|
||||
for sensor_description in WF_SENSORS
|
||||
)
|
||||
|
||||
|
||||
class WeatherFlowCloudSensor(WeatherFlowCloudEntity, SensorEntity):
|
||||
"""Implementation of a WeatherFlow sensor."""
|
||||
|
||||
entity_description: WeatherFlowCloudSensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: WeatherFlowCloudDataUpdateCoordinator,
|
||||
description: WeatherFlowCloudSensorEntityDescription,
|
||||
station_id: int,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
# Initialize the Entity Class
|
||||
super().__init__(coordinator, station_id)
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{station_id}_{description.key}"
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType | datetime:
|
||||
"""Return the state of the sensor."""
|
||||
return self.entity_description.value_fn(self.station.observation.obs[0])
|
@ -23,5 +23,65 @@
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"air_density": {
|
||||
"name": "Air density"
|
||||
},
|
||||
"barometric_pressure": {
|
||||
"name": "Pressure barometric"
|
||||
},
|
||||
"sea_level_pressure": {
|
||||
"name": "Pressure sea level"
|
||||
},
|
||||
|
||||
"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 strike"
|
||||
},
|
||||
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,11 +17,11 @@ from homeassistant.const import (
|
||||
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
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@ -43,13 +43,11 @@ async def async_setup_entry(
|
||||
|
||||
|
||||
class WeatherFlowWeather(
|
||||
SingleCoordinatorWeatherEntity[WeatherFlowCloudDataUpdateCoordinator]
|
||||
WeatherFlowCloudEntity,
|
||||
SingleCoordinatorWeatherEntity[WeatherFlowCloudDataUpdateCoordinator],
|
||||
):
|
||||
"""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
|
||||
@ -65,19 +63,9 @@ class WeatherFlowWeather(
|
||||
station_id: int,
|
||||
) -> None:
|
||||
"""Initialise the platform with a data instance and station."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
self.station_id = station_id
|
||||
super().__init__(coordinator, 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."""
|
||||
|
@ -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()
|
||||
|
@ -5,6 +5,15 @@ from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
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
|
||||
|
4783
tests/components/weatherflow_cloud/fixtures/forecast.json
Normal file
4783
tests/components/weatherflow_cloud/fixtures/forecast.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
}
|
132
tests/components/weatherflow_cloud/fixtures/stations.json
Normal file
132
tests/components/weatherflow_cloud/fixtures/stations.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
1556
tests/components/weatherflow_cloud/snapshots/test_sensor.ambr
Normal file
1556
tests/components/weatherflow_cloud/snapshots/test_sensor.ambr
Normal file
File diff suppressed because it is too large
Load Diff
@ -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': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'weather',
|
||||
'entity_category': None,
|
||||
'entity_id': 'weather.my_home_station',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'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': <WeatherEntityFeature: 3>,
|
||||
'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': <UnitOfPrecipitationDepth.MILLIMETERS: 'mm'>,
|
||||
'pressure': 795.8,
|
||||
'pressure_unit': <UnitOfPressure.HPA: 'hPa'>,
|
||||
'supported_features': <WeatherEntityFeature: 3>,
|
||||
'temperature': 4.0,
|
||||
'temperature_unit': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
'uv_index': 2,
|
||||
'visibility_unit': <UnitOfLength.KILOMETERS: 'km'>,
|
||||
'wind_bearing': 40.0,
|
||||
'wind_gust_speed': 14.4,
|
||||
'wind_speed': 7.2,
|
||||
'wind_speed_unit': <UnitOfSpeed.KILOMETERS_PER_HOUR: 'km/h'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'weather.my_home_station',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'sunny',
|
||||
})
|
||||
# ---
|
29
tests/components/weatherflow_cloud/test_sensor.py
Normal file
29
tests/components/weatherflow_cloud/test_sensor.py
Normal file
@ -0,0 +1,29 @@
|
||||
"""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 snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
29
tests/components/weatherflow_cloud/test_weather.py
Normal file
29
tests/components/weatherflow_cloud/test_weather.py
Normal file
@ -0,0 +1,29 @@
|
||||
"""Tests for the WeatherFlow Cloud weather 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 snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
Loading…
x
Reference in New Issue
Block a user