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:
Jeef 2024-07-19 02:30:01 -06:00 committed by GitHub
parent 7810dc213a
commit de18be235d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 7116 additions and 20 deletions

View File

@ -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:

View File

@ -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]:

View 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]

View 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"
}
}
}
}

View File

@ -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"]
}

View 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])

View File

@ -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"
}
}
}
}

View File

@ -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."""

View File

@ -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()

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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"
}

View 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"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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',
})
# ---

View 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)

View 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)