Add Google Weather sensors (#147141)

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
tronikos
2025-11-23 06:43:08 -08:00
committed by GitHub
parent 41e42b9581
commit f8bf7ec1ff
7 changed files with 1395 additions and 2 deletions

View File

@@ -20,7 +20,7 @@ from .coordinator import (
GoogleWeatherSubEntryRuntimeData,
)
_PLATFORMS: list[Platform] = [Platform.WEATHER]
_PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.WEATHER]
async def async_setup_entry(

View File

@@ -16,10 +16,15 @@ class GoogleWeatherBaseEntity(Entity):
_attr_has_entity_name = True
def __init__(
self, config_entry: GoogleWeatherConfigEntry, subentry: ConfigSubentry
self,
config_entry: GoogleWeatherConfigEntry,
subentry: ConfigSubentry,
unique_id_suffix: str | None = None,
) -> None:
"""Initialize base entity."""
self._attr_unique_id = subentry.subentry_id
if unique_id_suffix is not None:
self._attr_unique_id += f"_{unique_id_suffix.lower()}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, subentry.subentry_id)},
name=subentry.title,

View File

@@ -0,0 +1,27 @@
{
"entity": {
"sensor": {
"cloud_coverage": {
"default": "mdi:weather-cloudy"
},
"precipitation_probability": {
"default": "mdi:weather-rainy"
},
"precipitation_qpf": {
"default": "mdi:cup-water"
},
"thunderstorm_probability": {
"default": "mdi:weather-lightning"
},
"uv_index": {
"default": "mdi:weather-sunny-alert"
},
"visibility": {
"default": "mdi:eye"
},
"weather_condition": {
"default": "mdi:card-text-outline"
}
}
}
}

View File

@@ -0,0 +1,233 @@
"""Support for Google Weather sensors."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from google_weather_api import CurrentConditionsResponse
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.config_entries import ConfigSubentry
from homeassistant.const import (
DEGREE,
PERCENTAGE,
UV_INDEX,
UnitOfLength,
UnitOfPressure,
UnitOfSpeed,
UnitOfTemperature,
UnitOfVolumetricFlux,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .coordinator import (
GoogleWeatherConfigEntry,
GoogleWeatherCurrentConditionsCoordinator,
)
from .entity import GoogleWeatherBaseEntity
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class GoogleWeatherSensorDescription(SensorEntityDescription):
"""Class describing Google Weather sensor entities."""
value_fn: Callable[[CurrentConditionsResponse], str | int | float | None]
SENSOR_TYPES: tuple[GoogleWeatherSensorDescription, ...] = (
GoogleWeatherSensorDescription(
key="temperature",
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: data.temperature.degrees,
),
GoogleWeatherSensorDescription(
key="feelsLikeTemperature",
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: data.feels_like_temperature.degrees,
translation_key="apparent_temperature",
),
GoogleWeatherSensorDescription(
key="dewPoint",
device_class=SensorDeviceClass.TEMPERATURE,
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: data.dew_point.degrees,
translation_key="dew_point",
),
GoogleWeatherSensorDescription(
key="heatIndex",
device_class=SensorDeviceClass.TEMPERATURE,
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: data.heat_index.degrees,
translation_key="heat_index",
),
GoogleWeatherSensorDescription(
key="windChill",
device_class=SensorDeviceClass.TEMPERATURE,
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
value_fn=lambda data: data.wind_chill.degrees,
translation_key="wind_chill",
),
GoogleWeatherSensorDescription(
key="relativeHumidity",
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda data: data.relative_humidity,
),
GoogleWeatherSensorDescription(
key="uvIndex",
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UV_INDEX,
value_fn=lambda data: data.uv_index,
translation_key="uv_index",
),
GoogleWeatherSensorDescription(
key="precipitation_probability",
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda data: data.precipitation.probability.percent,
translation_key="precipitation_probability",
),
GoogleWeatherSensorDescription(
key="precipitation_qpf",
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
value_fn=lambda data: data.precipitation.qpf.quantity,
),
GoogleWeatherSensorDescription(
key="thunderstormProbability",
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda data: data.thunderstorm_probability,
translation_key="thunderstorm_probability",
),
GoogleWeatherSensorDescription(
key="airPressure",
device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE,
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
native_unit_of_measurement=UnitOfPressure.HPA,
value_fn=lambda data: data.air_pressure.mean_sea_level_millibars,
),
GoogleWeatherSensorDescription(
key="wind_direction",
device_class=SensorDeviceClass.WIND_DIRECTION,
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT_ANGLE,
native_unit_of_measurement=DEGREE,
value_fn=lambda data: data.wind.direction.degrees,
),
GoogleWeatherSensorDescription(
key="wind_speed",
device_class=SensorDeviceClass.WIND_SPEED,
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
value_fn=lambda data: data.wind.speed.value,
),
GoogleWeatherSensorDescription(
key="wind_gust",
device_class=SensorDeviceClass.WIND_SPEED,
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
value_fn=lambda data: data.wind.gust.value,
translation_key="wind_gust_speed",
),
GoogleWeatherSensorDescription(
key="visibility",
device_class=SensorDeviceClass.DISTANCE,
entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfLength.KILOMETERS,
value_fn=lambda data: data.visibility.distance,
translation_key="visibility",
),
GoogleWeatherSensorDescription(
key="cloudCover",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda data: data.cloud_cover,
translation_key="cloud_coverage",
),
GoogleWeatherSensorDescription(
key="weatherCondition",
entity_registry_enabled_default=False,
value_fn=lambda data: data.weather_condition.description.text,
translation_key="weather_condition",
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: GoogleWeatherConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Add Google Weather entities from a config_entry."""
for subentry in entry.subentries.values():
subentry_runtime_data = entry.runtime_data.subentries_runtime_data[
subentry.subentry_id
]
coordinator = subentry_runtime_data.coordinator_observation
async_add_entities(
(
GoogleWeatherSensor(coordinator, subentry, description)
for description in SENSOR_TYPES
if description.value_fn(coordinator.data) is not None
),
config_subentry_id=subentry.subentry_id,
)
class GoogleWeatherSensor(
CoordinatorEntity[GoogleWeatherCurrentConditionsCoordinator],
GoogleWeatherBaseEntity,
SensorEntity,
):
"""Define a Google Weather entity."""
entity_description: GoogleWeatherSensorDescription
def __init__(
self,
coordinator: GoogleWeatherCurrentConditionsCoordinator,
subentry: ConfigSubentry,
description: GoogleWeatherSensorDescription,
) -> None:
"""Initialize."""
super().__init__(coordinator)
GoogleWeatherBaseEntity.__init__(
self, coordinator.config_entry, subentry, description.key
)
self.entity_description = description
@property
def native_value(self) -> str | int | float | None:
"""Return the state."""
return self.entity_description.value_fn(self.coordinator.data)

View File

@@ -61,5 +61,42 @@
}
}
}
},
"entity": {
"sensor": {
"apparent_temperature": {
"name": "Apparent temperature"
},
"cloud_coverage": {
"name": "Cloud coverage"
},
"dew_point": {
"name": "Dew point"
},
"heat_index": {
"name": "Heat index temperature"
},
"precipitation_probability": {
"name": "Precipitation probability"
},
"thunderstorm_probability": {
"name": "Thunderstorm probability"
},
"uv_index": {
"name": "UV index"
},
"visibility": {
"name": "Visibility"
},
"weather_condition": {
"name": "Weather condition"
},
"wind_chill": {
"name": "Wind chill temperature"
},
"wind_gust_speed": {
"name": "Wind gust speed"
}
}
}
}

View File

@@ -0,0 +1,923 @@
# serializer version: 1
# name: test_sensor[sensor.home_apparent_temperature-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_apparent_temperature',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 1,
}),
}),
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
'original_icon': None,
'original_name': 'Apparent temperature',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'apparent_temperature',
'unique_id': 'home-subentry-id_feelsliketemperature',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
# name: test_sensor[sensor.home_apparent_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Home Apparent temperature',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.home_apparent_temperature',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '13.1',
})
# ---
# name: test_sensor[sensor.home_atmospheric_pressure-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_atmospheric_pressure',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
}),
'original_device_class': <SensorDeviceClass.ATMOSPHERIC_PRESSURE: 'atmospheric_pressure'>,
'original_icon': None,
'original_name': 'Atmospheric pressure',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'home-subentry-id_airpressure',
'unit_of_measurement': <UnitOfPressure.HPA: 'hPa'>,
})
# ---
# name: test_sensor[sensor.home_atmospheric_pressure-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'atmospheric_pressure',
'friendly_name': 'Home Atmospheric pressure',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPressure.HPA: 'hPa'>,
}),
'context': <ANY>,
'entity_id': 'sensor.home_atmospheric_pressure',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '1019.16',
})
# ---
# name: test_sensor[sensor.home_cloud_coverage-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_cloud_coverage',
'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': 'Cloud coverage',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'cloud_coverage',
'unique_id': 'home-subentry-id_cloudcover',
'unit_of_measurement': '%',
})
# ---
# name: test_sensor[sensor.home_cloud_coverage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Home Cloud coverage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.home_cloud_coverage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_sensor[sensor.home_dew_point-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_dew_point',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 1,
}),
}),
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
'original_icon': None,
'original_name': 'Dew point',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'dew_point',
'unique_id': 'home-subentry-id_dewpoint',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
# name: test_sensor[sensor.home_dew_point-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Home Dew point',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.home_dew_point',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '1.1',
})
# ---
# name: test_sensor[sensor.home_heat_index_temperature-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_heat_index_temperature',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 1,
}),
}),
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
'original_icon': None,
'original_name': 'Heat index temperature',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'heat_index',
'unique_id': 'home-subentry-id_heatindex',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
# name: test_sensor[sensor.home_heat_index_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Home Heat index temperature',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.home_heat_index_temperature',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '13.7',
})
# ---
# name: test_sensor[sensor.home_humidity-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_humidity',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.HUMIDITY: 'humidity'>,
'original_icon': None,
'original_name': 'Humidity',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'home-subentry-id_relativehumidity',
'unit_of_measurement': '%',
})
# ---
# name: test_sensor[sensor.home_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'humidity',
'friendly_name': 'Home Humidity',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.home_humidity',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '42',
})
# ---
# name: test_sensor[sensor.home_precipitation_intensity-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_precipitation_intensity',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
}),
'original_device_class': <SensorDeviceClass.PRECIPITATION_INTENSITY: 'precipitation_intensity'>,
'original_icon': None,
'original_name': 'Precipitation intensity',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'home-subentry-id_precipitation_qpf',
'unit_of_measurement': <UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR: 'mm/h'>,
})
# ---
# name: test_sensor[sensor.home_precipitation_intensity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'precipitation_intensity',
'friendly_name': 'Home Precipitation intensity',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR: 'mm/h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.home_precipitation_intensity',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0.0',
})
# ---
# name: test_sensor[sensor.home_precipitation_probability-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_precipitation_probability',
'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': 'Precipitation probability',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'precipitation_probability',
'unique_id': 'home-subentry-id_precipitation_probability',
'unit_of_measurement': '%',
})
# ---
# name: test_sensor[sensor.home_precipitation_probability-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Home Precipitation probability',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.home_precipitation_probability',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_sensor[sensor.home_temperature-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_temperature',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 1,
}),
}),
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
'original_icon': None,
'original_name': 'Temperature',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'home-subentry-id_temperature',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
# name: test_sensor[sensor.home_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Home Temperature',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.home_temperature',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '13.7',
})
# ---
# name: test_sensor[sensor.home_thunderstorm_probability-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_thunderstorm_probability',
'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': 'Thunderstorm probability',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'thunderstorm_probability',
'unique_id': 'home-subentry-id_thunderstormprobability',
'unit_of_measurement': '%',
})
# ---
# name: test_sensor[sensor.home_thunderstorm_probability-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Home Thunderstorm probability',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.home_thunderstorm_probability',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_sensor[sensor.home_uv_index-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_uv_index',
'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': 'UV index',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'uv_index',
'unique_id': 'home-subentry-id_uvindex',
'unit_of_measurement': 'UV index',
})
# ---
# name: test_sensor[sensor.home_uv_index-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Home UV index',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'UV index',
}),
'context': <ANY>,
'entity_id': 'sensor.home_uv_index',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '1',
})
# ---
# name: test_sensor[sensor.home_visibility-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_visibility',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
}),
'original_device_class': <SensorDeviceClass.DISTANCE: 'distance'>,
'original_icon': None,
'original_name': 'Visibility',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'visibility',
'unique_id': 'home-subentry-id_visibility',
'unit_of_measurement': <UnitOfLength.KILOMETERS: 'km'>,
})
# ---
# name: test_sensor[sensor.home_visibility-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'distance',
'friendly_name': 'Home Visibility',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfLength.KILOMETERS: 'km'>,
}),
'context': <ANY>,
'entity_id': 'sensor.home_visibility',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '16.0',
})
# ---
# name: test_sensor[sensor.home_weather_condition-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_weather_condition',
'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': 'Weather condition',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'weather_condition',
'unique_id': 'home-subentry-id_weathercondition',
'unit_of_measurement': None,
})
# ---
# name: test_sensor[sensor.home_weather_condition-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Home Weather condition',
}),
'context': <ANY>,
'entity_id': 'sensor.home_weather_condition',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'Sunny',
})
# ---
# name: test_sensor[sensor.home_wind_chill_temperature-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_wind_chill_temperature',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 1,
}),
}),
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
'original_icon': None,
'original_name': 'Wind chill temperature',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'wind_chill',
'unique_id': 'home-subentry-id_windchill',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
# name: test_sensor[sensor.home_wind_chill_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Home Wind chill temperature',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.home_wind_chill_temperature',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '13.1',
})
# ---
# name: test_sensor[sensor.home_wind_direction-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT_ANGLE: 'measurement_angle'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_wind_direction',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.WIND_DIRECTION: 'wind_direction'>,
'original_icon': None,
'original_name': 'Wind direction',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'home-subentry-id_wind_direction',
'unit_of_measurement': '°',
})
# ---
# name: test_sensor[sensor.home_wind_direction-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'wind_direction',
'friendly_name': 'Home Wind direction',
'state_class': <SensorStateClass.MEASUREMENT_ANGLE: 'measurement_angle'>,
'unit_of_measurement': '°',
}),
'context': <ANY>,
'entity_id': 'sensor.home_wind_direction',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '335',
})
# ---
# name: test_sensor[sensor.home_wind_gust_speed-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_wind_gust_speed',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
}),
'original_device_class': <SensorDeviceClass.WIND_SPEED: 'wind_speed'>,
'original_icon': None,
'original_name': 'Wind gust speed',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'wind_gust_speed',
'unique_id': 'home-subentry-id_wind_gust',
'unit_of_measurement': <UnitOfSpeed.KILOMETERS_PER_HOUR: 'km/h'>,
})
# ---
# name: test_sensor[sensor.home_wind_gust_speed-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'wind_speed',
'friendly_name': 'Home Wind gust speed',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfSpeed.KILOMETERS_PER_HOUR: 'km/h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.home_wind_gust_speed',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '18.0',
})
# ---
# name: test_sensor[sensor.home_wind_speed-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.home_wind_speed',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
}),
'original_device_class': <SensorDeviceClass.WIND_SPEED: 'wind_speed'>,
'original_icon': None,
'original_name': 'Wind speed',
'platform': 'google_weather',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'home-subentry-id_wind_speed',
'unit_of_measurement': <UnitOfSpeed.KILOMETERS_PER_HOUR: 'km/h'>,
})
# ---
# name: test_sensor[sensor.home_wind_speed-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'wind_speed',
'friendly_name': 'Home Wind speed',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfSpeed.KILOMETERS_PER_HOUR: 'km/h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.home_wind_speed',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '8.0',
})
# ---

View File

@@ -0,0 +1,168 @@
"""Test sensor of Google Weather integration."""
from datetime import timedelta
from unittest.mock import AsyncMock, patch
from freezegun.api import FrozenDateTimeFactory
from google_weather_api import GoogleWeatherApiError
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_UNIT_OF_MEASUREMENT,
STATE_UNAVAILABLE,
Platform,
UnitOfLength,
UnitOfPressure,
UnitOfSpeed,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry,
mock_google_weather_api: AsyncMock,
snapshot: SnapshotAssertion,
) -> None:
"""Test states of the sensor."""
with patch("homeassistant.components.google_weather._PLATFORMS", [Platform.SENSOR]):
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_availability(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_google_weather_api: AsyncMock,
freezer: FrozenDateTimeFactory,
) -> None:
"""Ensure that we mark the entities unavailable correctly when service is offline."""
entity_id = "sensor.home_temperature"
await hass.config_entries.async_setup(mock_config_entry.entry_id)
state = hass.states.get(entity_id)
assert state
assert state.state != STATE_UNAVAILABLE
assert state.state == "13.7"
mock_google_weather_api.async_get_current_conditions.side_effect = (
GoogleWeatherApiError()
)
freezer.tick(timedelta(minutes=15))
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state
assert state.state == STATE_UNAVAILABLE
mock_google_weather_api.async_get_current_conditions.side_effect = None
freezer.tick(timedelta(minutes=15))
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state
assert state.state != STATE_UNAVAILABLE
assert state.state == "13.7"
mock_google_weather_api.async_get_current_conditions.assert_called_with(
latitude=10.1, longitude=20.1
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_manual_update_entity(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_google_weather_api: AsyncMock,
) -> None:
"""Test manual update entity via service homeassistant/update_entity."""
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await async_setup_component(hass, "homeassistant", {})
mock_google_weather_api.async_get_current_conditions.assert_called_once_with(
latitude=10.1, longitude=20.1
)
await hass.services.async_call(
"homeassistant",
"update_entity",
{ATTR_ENTITY_ID: ["sensor.home_temperature"]},
blocking=True,
)
assert mock_google_weather_api.async_get_current_conditions.call_count == 2
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_imperial_units(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_google_weather_api: AsyncMock,
) -> None:
"""Test states of the sensor with imperial units."""
hass.config.units = US_CUSTOMARY_SYSTEM
await hass.config_entries.async_setup(mock_config_entry.entry_id)
state = hass.states.get("sensor.home_temperature")
assert state
assert state.state == "56.66"
assert (
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.FAHRENHEIT
)
state = hass.states.get("sensor.home_wind_speed")
assert state
assert float(state.state) == pytest.approx(4.97097)
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfSpeed.MILES_PER_HOUR
state = hass.states.get("sensor.home_visibility")
assert state
assert float(state.state) == pytest.approx(9.94194)
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfLength.MILES
state = hass.states.get("sensor.home_atmospheric_pressure")
assert state
assert float(state.state) == pytest.approx(30.09578)
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfPressure.INHG
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_state_update(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_google_weather_api: AsyncMock,
freezer: FrozenDateTimeFactory,
) -> None:
"""Ensure the sensor state changes after updating the data."""
entity_id = "sensor.home_temperature"
await hass.config_entries.async_setup(mock_config_entry.entry_id)
state = hass.states.get(entity_id)
assert state
assert state.state == "13.7"
mock_google_weather_api.async_get_current_conditions.return_value.temperature.degrees = 15.0
freezer.tick(timedelta(minutes=15))
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state
assert state.state == "15.0"