Compare commits

..

19 Commits

Author SHA1 Message Date
Franck Nijhof 1096c8af13 Bump version to 2026.7.0b0 2026-06-24 16:36:39 +00:00
Leonardo Merza cc2944d626 Fix ecobee active sensor reporting for custom presets and shared device names (#174417) 2026-06-24 17:21:45 +01:00
Mick Vleeshouwer 548ec5cacf Enable action queue to batch concurrent commands in Overkiz (#174275) 2026-06-24 17:19:15 +02:00
karwosts dc6eef2844 Fix date-only input_datetime timestamp attribute to use the correct TZ (#174357) 2026-06-24 17:15:09 +02:00
Stefan Agner 0808e30e37 Add repair when IPv6 is disabled for Matter (#174653)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-24 17:11:54 +02:00
Manu f0ed257f47 Refactor Steam integration config flow and tests (#174504)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-06-24 17:06:29 +02:00
Petro31 b4b710b474 Add restore state framework for template entities (#172847) 2026-06-24 16:54:56 +02:00
Bram Kragten 0004a82fe4 Update frontend to 20260624.0 (#174657) 2026-06-24 15:46:44 +01:00
epenet 0c4bc95bdd Migrate base entity attributes to StrEnum (#174633) 2026-06-24 15:38:49 +01:00
Manu 5fdab795e8 Remove Avi-on integration (#174649) 2026-06-24 16:35:22 +02:00
Manu 2193665909 Remove BeeWi SmartClim (#174651) 2026-06-24 16:32:05 +02:00
Martin Hjelmare c9d91d5812 Add enabled entity limit per config entry (#174194) 2026-06-24 16:07:54 +02:00
Erik Montnemery de9d9c66c1 Add additional sun conditions (#174537) 2026-06-24 16:00:08 +02:00
Paul Bottein dfcc4d1ae4 Fix friendly name for restored unavailable entities (#174614) 2026-06-24 14:47:39 +01:00
TimL d71812f09b Bump pysmlight to 0.4.0 (#174640) 2026-06-24 15:19:35 +02:00
Ariel Ebersberger a323ebe634 Reword "advanced operation" channel warning in Home Assistant Hardware (#174645) 2026-06-24 14:57:18 +02:00
Ariel Ebersberger 024bba55cf Reword "Configure advanced voice settings" in ElevenLabs (#174642) 2026-06-24 14:33:41 +02:00
Ariel Ebersberger a5546566e7 Rename "(advanced)" service names in Z-Wave (#174644) 2026-06-24 14:32:38 +02:00
Ariel Ebersberger 3d9994ee4f Rename "Local Risco Panel (advanced)" option in Risco (#174643) 2026-06-24 14:31:55 +02:00
1273 changed files with 39727 additions and 38677 deletions
@@ -16,7 +16,6 @@ description: Everything you need to know to build, test and review Home Assistan
- When validation guarantees a key is present, prefer direct dictionary indexing (`data["key"]`) over `.get("key")` so invalid assumptions fail fast.
- Integrations should be thin wrappers. Protocol parsing, device state machines, or other domain logic belong in a separate PyPI library, not in the integration itself. If unsure, ask before inlining.
- Integrations should not implement fixes or workarounds for limitations in libraries. Instead, the library should be updated to fix the issue.
- When catching exceptions, try-clauses should be as small as possible, i.e. avoid wrapping large blocks of code in a try-clause, and avoid catching exceptions from functions that are not expected to raise them.
The following platforms have extra guidelines:
- **Diagnostics**: [`platform-diagnostics.md`](platform-diagnostics.md) for diagnostic data collection
@@ -19,7 +19,6 @@ excludeAgent: "cloud-agent"
- When validation guarantees a key is present, prefer direct dictionary indexing (`data["key"]`) over `.get("key")` so invalid assumptions fail fast.
- Integrations should be thin wrappers. Protocol parsing, device state machines, or other domain logic belong in a separate PyPI library, not in the integration itself. If unsure, ask before inlining.
- Integrations should not implement fixes or workarounds for limitations in libraries. Instead, the library should be updated to fix the issue.
- When catching exceptions, try-clauses should be as small as possible, i.e. avoid wrapping large blocks of code in a try-clause, and avoid catching exceptions from functions that are not expected to raise them.
The following platforms have extra guidelines:
- **Diagnostics**: [`platform-diagnostics.md`](platform-diagnostics.md) for diagnostic data collection
Generated
-1
View File
@@ -230,7 +230,6 @@ CLAUDE.md @home-assistant/core
/tests/components/battery/ @home-assistant/core
/homeassistant/components/bayesian/ @HarvsG
/tests/components/bayesian/ @HarvsG
/homeassistant/components/beewi_smartclim/ @alemuro
/homeassistant/components/binary_sensor/ @home-assistant/core
/tests/components/binary_sensor/ @home-assistant/core
/homeassistant/components/bizkaibus/ @UgaitzEtxebarria
@@ -1 +0,0 @@
"""The avion component."""
-123
View File
@@ -1,123 +0,0 @@
"""Support for Avion dimmers."""
import importlib
import time
from typing import Any, override
import voluptuous as vol
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA,
ColorMode,
LightEntity,
)
from homeassistant.const import (
CONF_API_KEY,
CONF_DEVICES,
CONF_ID,
CONF_NAME,
CONF_PASSWORD,
CONF_USERNAME,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
DEVICE_SCHEMA = vol.Schema(
{
vol.Required(CONF_API_KEY): cv.string,
vol.Optional(CONF_ID): cv.positive_int,
vol.Optional(CONF_NAME): cv.string,
}
)
PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA},
vol.Optional(CONF_USERNAME): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
}
)
def setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up an Avion switch."""
avion = importlib.import_module("avion")
lights = [
AvionLight(
avion.Avion(
mac=address,
passphrase=device_config[CONF_API_KEY],
name=device_config.get(CONF_NAME),
object_id=device_config.get(CONF_ID),
connect=False,
)
)
for address, device_config in config[CONF_DEVICES].items()
]
if CONF_USERNAME in config and CONF_PASSWORD in config:
lights.extend(
AvionLight(device)
for device in avion.get_devices(
config[CONF_USERNAME], config[CONF_PASSWORD]
)
)
add_entities(lights)
class AvionLight(LightEntity):
"""Representation of an Avion light."""
_attr_support_color_mode = ColorMode.BRIGHTNESS
_attr_support_color_modes = {ColorMode.BRIGHTNESS}
_attr_should_poll = False
_attr_assumed_state = True
_attr_is_on = True
def __init__(self, device):
"""Initialize the light."""
self._attr_name = device.name
self._attr_unique_id = device.mac
self._attr_brightness = 255
self._switch = device
def set_state(self, brightness):
"""Set the state of this lamp to the provided brightness."""
avion = importlib.import_module("avion")
# Bluetooth LE is unreliable, and the connection may drop at any
# time. Make an effort to re-establish the link.
initial = time.monotonic()
while True:
if time.monotonic() - initial >= 10:
return False
try:
self._switch.set_brightness(brightness)
break
except avion.AvionException:
self._switch.connect()
return True
@override
def turn_on(self, **kwargs: Any) -> None:
"""Turn the specified or all lights on."""
if (brightness := kwargs.get(ATTR_BRIGHTNESS)) is not None:
self._attr_brightness = brightness
self.set_state(self.brightness)
self._attr_is_on = True
@override
def turn_off(self, **kwargs: Any) -> None:
"""Turn the specified or all lights off."""
self.set_state(0)
self._attr_is_on = False
@@ -1,9 +0,0 @@
{
"domain": "avion",
"name": "Avi-on",
"codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/avion",
"iot_class": "assumed_state",
"quality_scale": "legacy",
"requirements": ["avion==0.10"]
}
@@ -1 +0,0 @@
"""The beewi_smartclim component."""
@@ -1,10 +0,0 @@
{
"domain": "beewi_smartclim",
"name": "BeeWi SmartClim BLE sensor",
"codeowners": ["@alemuro"],
"documentation": "https://www.home-assistant.io/integrations/beewi_smartclim",
"iot_class": "local_polling",
"loggers": ["beewi_smartclim"],
"quality_scale": "legacy",
"requirements": ["beewi-smartclim==0.0.10"]
}
@@ -1,84 +0,0 @@
"""Platform for beewi_smartclim integration."""
from beewi_smartclim import BeewiSmartClimPoller
import voluptuous as vol
from homeassistant.components.sensor import (
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
SensorDeviceClass,
SensorEntity,
)
from homeassistant.const import CONF_MAC, CONF_NAME, PERCENTAGE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
# Default values
DEFAULT_NAME = "BeeWi SmartClim"
# Sensor config
SENSOR_TYPES = [
[SensorDeviceClass.TEMPERATURE, "Temperature", UnitOfTemperature.CELSIUS],
[SensorDeviceClass.HUMIDITY, "Humidity", PERCENTAGE],
[SensorDeviceClass.BATTERY, "Battery", PERCENTAGE],
]
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_MAC): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
}
)
def setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the beewi_smartclim platform."""
mac = config[CONF_MAC]
prefix = config[CONF_NAME]
poller = BeewiSmartClimPoller(mac)
sensors = []
for sensor_type in SENSOR_TYPES:
device = sensor_type[0]
name = sensor_type[1]
unit = sensor_type[2]
# `prefix` is the name configured by the user for the sensor, we're appending
# the device type at the end of the name (garden -> garden temperature)
if prefix:
name = f"{prefix} {name}"
sensors.append(BeewiSmartclimSensor(poller, name, mac, device, unit))
add_entities(sensors)
class BeewiSmartclimSensor(SensorEntity):
"""Representation of a Sensor."""
def __init__(self, poller, name, mac, device, unit):
"""Initialize the sensor."""
self._poller = poller
self._attr_name = name
self._device = device
self._attr_native_unit_of_measurement = unit
self._attr_device_class = self._device
self._attr_unique_id = f"{mac}_{device}"
def update(self) -> None:
"""Fetch new state data from the poller."""
self._poller.update_sensor()
self._attr_native_value = None
if self._device == SensorDeviceClass.TEMPERATURE:
self._attr_native_value = self._poller.get_temperature()
if self._device == SensorDeviceClass.HUMIDITY:
self._attr_native_value = self._poller.get_humidity()
if self._device == SensorDeviceClass.BATTERY:
self._attr_native_value = self._poller.get_battery()
+20 -10
View File
@@ -641,25 +641,34 @@ class Thermostat(ClimateEntity):
for device in device_registry.devices.values()
for sensor_info in sensors_info
if device.name == sensor_info["name"]
and any(identifier[0] == DOMAIN for identifier in device.identifiers)
]
def _active_climate_name(self) -> str:
"""Return the ecobee climate name of the active comfort setting.
``preset_mode`` is the climate *name*, but ``_preset_modes`` is keyed by
climateRef, so the built-in presets are translated back to their ecobee
name. Holds that are not a comfort setting (temperature/vacation/
indefinite away) are not real climates; per ecobee they follow the Home
comfort setting's sensor participation, so fall back to "Home".
"""
# https://support.ecobee.com/s/articles/SmartSensors-Sensor-Participation
preset_mode = self.preset_mode
if preset_mode is None:
return "Home"
mode = HASS_TO_ECOBEE_PRESET.get(preset_mode, preset_mode)
return mode if mode in self._preset_modes.values() else "Home"
@property
def active_sensors_in_preset_mode(self) -> list:
"""Return the currently active/participating sensors."""
# https://support.ecobee.com/s/articles/SmartSensors-Sensor-Participation
# During a manual hold, the ecobee will follow the Sensor Participation
# rules for the Home Comfort Settings
mode = self._preset_modes.get(self.preset_mode, "Home")
return self._sensors_in_preset_mode(mode)
return self._sensors_in_preset_mode(self._active_climate_name())
@property
def active_sensor_devices_in_preset_mode(self) -> list:
"""Return the currently active/participating sensor devices."""
# https://support.ecobee.com/s/articles/SmartSensors-Sensor-Participation
# During a manual hold, the ecobee will follow the Sensor Participation
# rules for the Home Comfort Settings
mode = self._preset_modes.get(self.preset_mode, "Home")
return self._sensor_devices_in_preset_mode(mode)
return self._sensor_devices_in_preset_mode(self._active_climate_name())
@override
def set_preset_mode(self, preset_mode: str) -> None:
@@ -950,6 +959,7 @@ class Thermostat(ClimateEntity):
for device in device_registry.devices.values()
for sensor_name in sensor_names
if device.name == sensor_name
and any(identifier[0] == DOMAIN for identifier in device.identifiers)
]
)
@@ -31,14 +31,14 @@
"step": {
"init": {
"data": {
"configure_voice": "Configure advanced voice settings",
"configure_voice": "Configure voice settings",
"model": "Model",
"stt_auto_language": "Auto-detect language",
"stt_model": "Speech-to-text model",
"voice": "Voice"
},
"data_description": {
"configure_voice": "Configure advanced voice settings. Find more information in the ElevenLabs documentation.",
"configure_voice": "Configure voice settings. Find more information in the ElevenLabs documentation.",
"model": "ElevenLabs model to use. Please note that not all models support all languages equally well.",
"stt_auto_language": "Automatically detect the spoken language for speech-to-text.",
"stt_model": "Speech-to-text model to use.",
@@ -21,5 +21,5 @@
"integration_type": "system",
"preview_features": { "winter_mode": {} },
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20260527.7"]
"requirements": ["home-assistant-frontend==20260624.0"]
}
@@ -127,7 +127,7 @@
"data": {
"channel": "Channel"
},
"description": "Start a channel change for your Zigbee and Thread networks.\n\nNote: this is an advanced operation and can leave your Thread and Zigbee networks inoperable if the new channel is congested. Depending on existing network conditions, many of your devices may not migrate to the new channel and will require re-joining before they start working again. Use with caution.\n\nOnce you have selected **Submit**, the channel change starts quietly in the background and will finish after a few minutes.",
"description": "Start a channel change for your Zigbee and Thread networks.\n\nNote: this operation can leave your Thread and Zigbee networks inoperable if the new channel is congested. Depending on existing network conditions, many of your devices may not migrate to the new channel and will require re-joining before they start working again. Use with caution.\n\nOnce you have selected **Submit**, the channel change starts quietly in the background and will finish after a few minutes.",
"title": "[%key:component::homeassistant_hardware::silabs_multiprotocol_hardware::options::step::reconfigure_addon::title%]"
},
"install_addon": {
@@ -389,7 +389,9 @@ class InputDatetime(collection.CollectionEntity, RestoreEntity):
elif not self.has_time:
extended = py_datetime.datetime.combine(
self._current_datetime, py_datetime.time(0, 0)
self._current_datetime,
py_datetime.time(0, 0),
dt_util.get_default_time_zone(),
)
attrs[InputDatetimeEntityStateAttribute.TIMESTAMP] = extended.timestamp()
+58 -1
View File
@@ -4,6 +4,7 @@ import asyncio
from functools import cache
from typing import TYPE_CHECKING
from aiohasupervisor.models import InterfaceMethod
from matter_server.client import MatterClient
from matter_server.client.exceptions import (
CannotConnect,
@@ -15,13 +16,20 @@ from matter_server.client.exceptions import (
from matter_server.common.errors import MatterError, NodeNotExists
from yarl import URL
from homeassistant.components.hassio import AddonError, AddonManager, AddonState
from homeassistant.components.hassio import (
AddonError,
AddonManager,
AddonState,
SupervisorError,
get_supervisor_client,
)
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import CONF_URL, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.issue_registry import (
IssueSeverity,
async_create_issue,
@@ -123,6 +131,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: MatterConfigEntry) -> bo
async_delete_issue(hass, DOMAIN, "server_version_version_too_old")
async_delete_issue(hass, DOMAIN, "server_version_version_too_new")
await _async_check_ipv6_enabled(hass)
ble_proxy: MatterBleProxy | None = None
async def on_hass_stop(event: Event) -> None:
@@ -252,6 +262,53 @@ def _derive_ble_proxy_url(matter_ws_url: str) -> str | None:
return str(parsed.with_path(new_path))
async def _async_check_ipv6_enabled(hass: HomeAssistant) -> None:
"""Raise a repair issue when IPv6 is disabled in Supervisor network settings.
Matter relies on IPv6 to communicate with devices. On Supervised/HAOS
installations the host network IPv6 method can be disabled per interface,
which silently breaks Matter, so we surface a repair pointing the user at
the network settings.
"""
if not is_hassio(hass):
return
client = get_supervisor_client(hass)
try:
network_info = await client.network.info()
except SupervisorError as err:
LOGGER.debug("Failed to fetch Supervisor network info: %s", err)
return
connected_interfaces = [
interface
for interface in network_info.interfaces
if interface.enabled and interface.connected
]
# Without a connected interface we can't tell whether IPv6 is disabled or
# the network is simply not up yet, so avoid raising a false repair.
if not connected_interfaces:
return
if any(
interface.ipv6 is not None
and interface.ipv6.method is not InterfaceMethod.DISABLED
for interface in connected_interfaces
):
async_delete_issue(hass, DOMAIN, "ipv6_disabled")
return
async_create_issue(
hass,
DOMAIN,
"ipv6_disabled",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="ipv6_disabled",
learn_more_url="homeassistant://config/network",
)
async def _client_listen(
hass: HomeAssistant,
entry: MatterConfigEntry,
@@ -721,6 +721,10 @@
}
},
"issues": {
"ipv6_disabled": {
"description": "Matter relies on IPv6 to communicate with some devices, but IPv6 is disabled on all of your connected network interfaces. Locally connected Wi-Fi and Ethernet devices may still work, but features such as using an external Thread border router need IPv6 enabled. Select \"Learn more\" to open the network settings.",
"title": "IPv6 is disabled but required by Matter"
},
"server_version_version_too_new": {
"description": "The version of the Matter Server you are currently running is too new for this version of Home Assistant. Please update Home Assistant or downgrade the Matter Server to an older version to fix this issue.",
"title": "Older version of Matter Server needed"
+8 -2
View File
@@ -5,6 +5,7 @@ from dataclasses import dataclass
from typing import cast
from aiohttp import ClientError
from pyoverkiz.action_queue import ActionQueueSettings
from pyoverkiz.auth.credentials import (
LocalTokenCredentials,
RexelTokenCredentials,
@@ -317,7 +318,9 @@ def create_local_client(
credentials=LocalTokenCredentials(token),
session=session,
verify_ssl=verify_ssl,
settings=OverkizClientSettings(default_rts_command_duration=0),
settings=OverkizClientSettings(
action_queue=ActionQueueSettings(), default_rts_command_duration=0
),
)
@@ -333,7 +336,9 @@ def create_cloud_client(
server=server,
credentials=UsernamePasswordCredentials(username, password),
session=session,
settings=OverkizClientSettings(default_rts_command_duration=0),
settings=OverkizClientSettings(
action_queue=ActionQueueSettings(), default_rts_command_duration=0
),
)
@@ -360,4 +365,5 @@ async def create_rexel_client(
gateway_id=entry.data[CONF_GATEWAY_ID],
),
session=async_create_clientsession(hass),
settings=OverkizClientSettings(action_queue=ActionQueueSettings()),
)
@@ -76,7 +76,7 @@ class OverkizDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Device]]):
self.data = {}
self.client = client
self.devices: dict[str, Device] = {d.device_url: d for d in devices}
self.executions: dict[str, dict[str, str]] = {}
self.executions: dict[str, list[dict[str, str]]] = {}
self.areas = self._places_to_area(places) if places else None
self._default_update_interval = UPDATE_INTERVAL
@@ -228,7 +228,7 @@ async def on_execution_registered(
) -> None:
"""Handle execution registered event."""
if event.exec_id not in coordinator.executions:
coordinator.executions[event.exec_id] = {}
coordinator.executions[event.exec_id] = []
if not coordinator.is_stateless:
coordinator.update_interval = timedelta(seconds=1)
+2 -1
View File
@@ -857,7 +857,8 @@ class OverkizCover(OverkizDescriptiveEntity, CoverEntity):
return any(
execution.get("device_url") == self.device.device_url
and execution.get("command_name") == command
for execution in self.coordinator.executions.values()
for executions in self.coordinator.executions.values()
for execution in executions
)
@property
+17 -11
View File
@@ -71,12 +71,15 @@ class OverkizExecutor:
) as exception:
raise HomeAssistantError("Failed to connect") from exception
# ExecutionRegisteredEvent doesn't contain the
# device_url, thus we need to register it here
self.coordinator.executions[exec_id] = {
"device_url": self.device.device_url,
"command_name": command_name,
}
# ExecutionRegisteredEvent doesn't contain the device_url, thus we need
# to register it here. The action queue can merge concurrent action
# groups under one exec_id, so accumulate rather than overwrite.
self.coordinator.executions.setdefault(exec_id, []).append(
{
"device_url": self.device.device_url,
"command_name": command_name,
}
)
if refresh_afterwards:
await self.coordinator.async_refresh()
@@ -110,10 +113,12 @@ class OverkizExecutor:
) as exception:
raise HomeAssistantError("Failed to connect") from exception
self.coordinator.executions[exec_id] = {
"device_url": self.device.device_url,
"command_name": commands[-1].name,
}
self.coordinator.executions.setdefault(exec_id, []).append(
{
"device_url": self.device.device_url,
"command_name": commands[-1].name,
}
)
if refresh_afterwards:
await self.coordinator.async_refresh()
@@ -129,7 +134,8 @@ class OverkizExecutor:
(
exec_id
# Reverse dictionary to cancel the last added execution
for exec_id, execution in reversed(self.coordinator.executions.items())
for exec_id, executions in reversed(self.coordinator.executions.items())
for execution in executions
if execution.get("device_url") == self.device.device_url
and execution.get("command_name") in commands_to_cancel
),
+1 -1
View File
@@ -27,7 +27,7 @@
"user": {
"menu_options": {
"cloud": "Risco Cloud (recommended)",
"local": "Local Risco Panel (advanced)"
"local": "Local Risco Panel"
}
}
}
@@ -12,7 +12,7 @@
"integration_type": "device",
"iot_class": "local_push",
"quality_scale": "silver",
"requirements": ["pysmlight==0.3.2"],
"requirements": ["pysmlight==0.4.0"],
"zeroconf": [
{
"type": "_slzb-06._tcp.local."
@@ -3,14 +3,13 @@
from collections.abc import Iterator, Mapping
from typing import Any, override
import steam
import steam.api
import voluptuous as vol
from homeassistant.config_entries import (
SOURCE_REAUTH,
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
OptionsFlowWithReload,
)
from homeassistant.const import CONF_API_KEY, Platform
from homeassistant.core import callback
@@ -22,6 +21,14 @@ from .coordinator import SteamConfigEntry
# To avoid too long request URIs, the amount of ids to request is limited
MAX_IDS_TO_REQUEST = 275
STEP_USER_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_API_KEY): str,
vol.Required(CONF_ACCOUNT): str,
}
)
STEP_REAUTH_DATA_SCHEMA = vol.Schema({vol.Required(CONF_API_KEY): str})
def validate_input(user_input: dict[str, str]) -> dict[str, str | int]:
"""Handle common flow input validation."""
@@ -49,29 +56,23 @@ class SteamFlowHandler(ConfigFlow, domain=DOMAIN):
) -> ConfigFlowResult:
"""Handle a flow initiated by the user."""
errors = {}
if user_input is None and self.source == SOURCE_REAUTH:
user_input = {CONF_ACCOUNT: self._get_reauth_entry().data[CONF_ACCOUNT]}
elif user_input is not None:
if user_input is not None:
await self.async_set_unique_id(user_input[CONF_ACCOUNT])
self._abort_if_unique_id_configured()
try:
res = await self.hass.async_add_executor_job(validate_input, user_input)
if res is not None:
name = str(res["personaname"])
else:
errors["base"] = "invalid_account"
except (steam.api.HTTPError, steam.api.HTTPTimeoutError) as ex:
errors["base"] = "cannot_connect"
if "403" in str(ex):
errors["base"] = "invalid_auth"
except steam.api.HTTPError as ex:
errors["base"] = (
"invalid_auth" if "403" in str(ex) else "cannot_connect"
)
except Exception as ex: # noqa: BLE001
LOGGER.exception("Unknown exception: %s", ex)
errors["base"] = "unknown"
if not errors:
entry = await self.async_set_unique_id(user_input[CONF_ACCOUNT])
if entry and self.source == SOURCE_REAUTH:
self.hass.config_entries.async_update_entry(entry, data=user_input)
await self.hass.config_entries.async_reload(entry.entry_id)
return self.async_abort(reason="reauth_successful")
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=name,
data=user_input,
@@ -80,15 +81,8 @@ class SteamFlowHandler(ConfigFlow, domain=DOMAIN):
user_input = user_input or {}
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(
CONF_API_KEY, default=user_input.get(CONF_API_KEY) or ""
): str,
vol.Required(
CONF_ACCOUNT, default=user_input.get(CONF_ACCOUNT) or ""
): str,
}
data_schema=self.add_suggested_values_to_schema(
data_schema=STEP_USER_DATA_SCHEMA, suggested_values=user_input
),
errors=errors,
description_placeholders=PLACEHOLDERS,
@@ -104,12 +98,34 @@ class SteamFlowHandler(ConfigFlow, domain=DOMAIN):
self, user_input: dict[str, str] | None = None
) -> ConfigFlowResult:
"""Confirm reauth dialog."""
if user_input is not None:
return await self.async_step_user()
errors: dict[str, str] = {}
entry = self._get_reauth_entry()
self._set_confirm_only()
if user_input is not None:
try:
if not await self.hass.async_add_executor_job(
validate_input, {**entry.data, **user_input}
):
errors["base"] = "invalid_account"
except steam.api.HTTPError as ex:
errors["base"] = (
"invalid_auth" if "403" in str(ex) else "cannot_connect"
)
except Exception as ex: # noqa: BLE001
LOGGER.exception("Unknown exception: %s", ex)
errors["base"] = "unknown"
if not errors:
return self.async_update_reload_and_abort(
entry, data_updates=user_input
)
return self.async_show_form(
step_id="reauth_confirm", description_placeholders=PLACEHOLDERS
step_id="reauth_confirm",
data_schema=self.add_suggested_values_to_schema(
data_schema=STEP_REAUTH_DATA_SCHEMA, suggested_values=user_input
),
errors=errors,
description_placeholders=PLACEHOLDERS,
)
@@ -118,7 +134,7 @@ def _batch_ids(ids: list[str]) -> Iterator[list[str]]:
yield ids[i : i + MAX_IDS_TO_REQUEST]
class SteamOptionsFlowHandler(OptionsFlow):
class SteamOptionsFlowHandler(OptionsFlowWithReload):
"""Handle Steam client options."""
def __init__(self, entry: SteamConfigEntry) -> None:
@@ -145,7 +161,6 @@ class SteamOptionsFlowHandler(OptionsFlow):
if _id in user_input[CONF_ACCOUNTS]
}
}
await self.hass.config_entries.async_reload(self.config_entry.entry_id)
return self.async_create_entry(title="", data=channel_data)
error = None
try:
@@ -3,7 +3,7 @@
from datetime import timedelta
from typing import override
import steam
import steam.api
from steam.api import _interface_method as INTMethod
from homeassistant.config_entries import ConfigEntry
@@ -70,7 +70,7 @@ class SteamDataUpdateCoordinator(
try:
return await self.hass.async_add_executor_job(self._update)
except (steam.api.HTTPError, steam.api.HTTPTimeoutError) as ex:
except steam.api.HTTPError as ex:
if "401" in str(ex):
raise ConfigEntryAuthFailed from ex
raise UpdateFailed(ex) from ex
@@ -12,7 +12,13 @@
},
"step": {
"reauth_confirm": {
"description": "The Steam integration needs to be manually re-authenticated\n\nYou can find your Steam Web API key [**here**]({api_key_url}).",
"data": {
"api_key": "[%key:component::steam_online::config::step::user::data::api_key%]"
},
"data_description": {
"api_key": "[%key:component::steam_online::config::step::user::data_description::api_key%]"
},
"description": "The Steam integration requires re-authentication.\n\nYou can find your Steam Web API key [**here**]({api_key_url}).",
"title": "[%key:common::config_flow::title::reauth%]"
},
"user": {
+217 -3
View File
@@ -3,23 +3,52 @@
from datetime import datetime, timedelta
from typing import Any, Unpack, cast, override
import astral.sun
import voluptuous as vol
from homeassistant.const import CONF_OPTIONS, SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET
from homeassistant.const import (
CONF_ENTITY_ID,
CONF_OPTIONS,
CONF_TARGET,
CONF_TYPE,
DEGREE,
SUN_EVENT_SUNRISE,
SUN_EVENT_SUNSET,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.automation import move_top_level_schema_fields_to_options
from homeassistant.helpers.automation import (
DomainSpec,
move_top_level_schema_fields_to_options,
)
from homeassistant.helpers.condition import (
ATTR_BEHAVIOR,
BEHAVIOR_ANY,
Condition,
ConditionCheckParams,
ConditionConfig,
EntityNumericalConditionBase,
condition_trace_set_result,
condition_trace_update_result,
)
from homeassistant.helpers.sun import get_astral_event_date
from homeassistant.helpers.selector import (
NumericThresholdMode,
NumericThresholdSelector,
NumericThresholdSelectorConfig,
)
from homeassistant.helpers.sun import get_astral_event_date, get_astral_observer
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import dt as dt_util
from .const import (
DOMAIN,
ELEVATION_ASTRONOMICAL,
ELEVATION_CIVIL,
ELEVATION_HORIZON,
ELEVATION_NAUTICAL,
STATE_ATTR_ELEVATION,
)
_OPTIONS_SCHEMA_DICT: dict[vol.Marker, Any] = {
vol.Optional("before"): cv.sun_event,
vol.Optional("before_offset"): cv.time_period,
@@ -167,8 +196,193 @@ class SunCondition(Condition):
)
# The sun is a singleton, so these conditions take no target and no options.
_STATE_CONDITION_SCHEMA = vol.Schema({vol.Required(CONF_OPTIONS, default=dict): {}})
# The sun is a singleton, so the elevation condition always targets sun.sun
# instead of asking the user to pick an entity.
_SUN_ENTITY_ID = f"{DOMAIN}.{DOMAIN}"
_ELEVATION_DOMAIN_SPECS = {DOMAIN: DomainSpec(value_source=STATE_ATTR_ELEVATION)}
def _solar_position(hass: HomeAssistant) -> tuple[float, bool]:
"""Return the sun's current elevation in degrees and whether it is rising."""
observer = get_astral_observer(hass)
now = dt_util.utcnow()
elevation = astral.sun.elevation(observer, now)
rising = astral.sun.elevation(observer, now + timedelta(minutes=1)) > elevation
return elevation, rising
class _SunStateCondition(Condition):
"""Base class for the option-less sun state conditions."""
@classmethod
@override
async def async_validate_config(
cls, hass: HomeAssistant, config: ConfigType
) -> ConfigType:
"""Validate config."""
return cast(ConfigType, _STATE_CONDITION_SCHEMA(config))
class _UpCondition(_SunStateCondition):
"""Test if the sun is up."""
@override
def _async_check(self, **kwargs: Unpack[ConditionCheckParams]) -> bool:
"""Check the condition."""
elevation, _ = _solar_position(self._hass)
return elevation >= ELEVATION_HORIZON
class _SetCondition(_SunStateCondition):
"""Test if the sun is set."""
@override
def _async_check(self, **kwargs: Unpack[ConditionCheckParams]) -> bool:
"""Check the condition."""
elevation, _ = _solar_position(self._hass)
return elevation < ELEVATION_HORIZON
class _AscendingCondition(_SunStateCondition):
"""Test if the sun is ascending."""
@override
def _async_check(self, **kwargs: Unpack[ConditionCheckParams]) -> bool:
"""Check the condition."""
_, rising = _solar_position(self._hass)
return rising
class _DescendingCondition(_SunStateCondition):
"""Test if the sun is descending."""
@override
def _async_check(self, **kwargs: Unpack[ConditionCheckParams]) -> bool:
"""Check the condition."""
_, rising = _solar_position(self._hass)
return not rising
class _NightCondition(_SunStateCondition):
"""Test if it is night (the sun is below all twilight)."""
@override
def _async_check(self, **kwargs: Unpack[ConditionCheckParams]) -> bool:
"""Check the condition."""
elevation, _ = _solar_position(self._hass)
return elevation <= ELEVATION_ASTRONOMICAL
_TWILIGHT_ANY = "any"
_TWILIGHT_CIVIL = "civil"
_TWILIGHT_NAUTICAL = "nautical"
_TWILIGHT_ASTRONOMICAL = "astronomical"
# Elevation band (min, max) in degrees for each twilight type, bounded by the
# horizon and the twilight elevations.
_TWILIGHT_BANDS = {
_TWILIGHT_ANY: (ELEVATION_ASTRONOMICAL, ELEVATION_HORIZON),
_TWILIGHT_CIVIL: (ELEVATION_CIVIL, ELEVATION_HORIZON),
_TWILIGHT_NAUTICAL: (ELEVATION_NAUTICAL, ELEVATION_CIVIL),
_TWILIGHT_ASTRONOMICAL: (ELEVATION_ASTRONOMICAL, ELEVATION_NAUTICAL),
}
_TWILIGHT_CONDITION_SCHEMA = vol.Schema(
{
vol.Required(CONF_OPTIONS, default=dict): {
vol.Optional(CONF_TYPE, default=_TWILIGHT_ANY): vol.In(_TWILIGHT_BANDS),
}
}
)
class _TwilightCondition(Condition):
"""Base class for the morning and evening twilight conditions.
The sun is in twilight when its elevation is within the selected band;
morning twilight requires the sun to be rising and evening twilight to be
descending.
"""
_rising: bool
@classmethod
@override
async def async_validate_config(
cls, hass: HomeAssistant, config: ConfigType
) -> ConfigType:
"""Validate config."""
return cast(ConfigType, _TWILIGHT_CONDITION_SCHEMA(config))
def __init__(self, hass: HomeAssistant, config: ConditionConfig) -> None:
"""Initialize condition."""
super().__init__(hass, config)
assert config.options is not None
self._low, self._high = _TWILIGHT_BANDS[config.options[CONF_TYPE]]
@override
def _async_check(self, **kwargs: Unpack[ConditionCheckParams]) -> bool:
"""Check the condition."""
elevation, rising = _solar_position(self._hass)
return rising == self._rising and self._low <= elevation <= self._high
class _MorningTwilightCondition(_TwilightCondition):
"""Test if it is morning twilight (the sun is rising through twilight)."""
_rising = True
class _EveningTwilightCondition(_TwilightCondition):
"""Test if it is evening twilight (the sun is descending through twilight)."""
_rising = False
_ELEVATION_CONDITION_SCHEMA = vol.Schema(
{
vol.Required(CONF_OPTIONS, default=dict): {
vol.Required("threshold"): NumericThresholdSelector(
NumericThresholdSelectorConfig(mode=NumericThresholdMode.IS)
),
}
}
)
class _ElevationCondition(EntityNumericalConditionBase):
"""Test the sun's elevation against a threshold."""
_domain_specs = _ELEVATION_DOMAIN_SPECS
_valid_unit = DEGREE
_schema = _ELEVATION_CONDITION_SCHEMA
@classmethod
@override
async def async_validate_config(
cls, hass: HomeAssistant, config: ConfigType
) -> ConfigType:
"""Validate config and target the singleton sun entity."""
config = cast(ConfigType, cls._schema(config))
config[CONF_TARGET] = {CONF_ENTITY_ID: [_SUN_ENTITY_ID]}
# `behavior` is needed by `EntityConditionBase.__init__`.
config[CONF_OPTIONS][ATTR_BEHAVIOR] = BEHAVIOR_ANY
return config
CONDITIONS: dict[str, type[Condition]] = {
"_": SunCondition,
"is_up": _UpCondition,
"is_set": _SetCondition,
"is_ascending": _AscendingCondition,
"is_descending": _DescendingCondition,
"elevation": _ElevationCondition,
"is_night": _NightCondition,
"is_morning_twilight": _MorningTwilightCondition,
"is_evening_twilight": _EveningTwilightCondition,
}
@@ -0,0 +1,46 @@
.type: &condition_type
required: true
default: any
selector:
select:
translation_key: twilight_type
options:
- any
- civil
- nautical
- astronomical
.elevation_threshold_entity: &condition_elevation_threshold_entity
- domain: input_number
unit_of_measurement: "°"
- domain: number
unit_of_measurement: "°"
- domain: sensor
unit_of_measurement: "°"
.elevation_threshold_number: &condition_elevation_threshold_number
min: -90
max: 90
mode: box
unit_of_measurement: "°"
is_up: {}
is_set: {}
is_ascending: {}
is_descending: {}
is_night: {}
elevation:
fields:
threshold:
required: true
selector:
numeric_threshold:
entity: *condition_elevation_threshold_entity
mode: is
number: *condition_elevation_threshold_number
is_morning_twilight:
fields:
type: *condition_type
is_evening_twilight:
fields:
type: *condition_type
+11
View File
@@ -2,10 +2,21 @@
from typing import Final
import astral
DOMAIN: Final = "sun"
DEFAULT_NAME: Final = "Sun"
# Elevation of the sun's center at the horizon, in degrees. This is the value
# astral uses for sunrise/sunset (atmospheric refraction plus the sun's radius).
ELEVATION_HORIZON: Final = -0.833
# Sun elevation, in degrees, at each twilight boundary
ELEVATION_CIVIL: Final[float] = -astral.Depression.CIVIL.value
ELEVATION_NAUTICAL: Final[float] = -astral.Depression.NAUTICAL.value
ELEVATION_ASTRONOMICAL: Final[float] = -astral.Depression.ASTRONOMICAL.value
SIGNAL_POSITION_CHANGED = f"{DOMAIN}_position_changed"
SIGNAL_EVENTS_CHANGED = f"{DOMAIN}_events_changed"
+22 -20
View File
@@ -24,6 +24,10 @@ from homeassistant.helpers.sun import (
from homeassistant.util import dt as dt_util
from .const import (
ELEVATION_ASTRONOMICAL,
ELEVATION_CIVIL,
ELEVATION_HORIZON,
ELEVATION_NAUTICAL,
SIGNAL_EVENTS_CHANGED,
SIGNAL_POSITION_CHANGED,
STATE_ABOVE_HORIZON,
@@ -67,12 +71,8 @@ PHASE_SMALL_DAY = "small_day"
# > 10° above horizon
PHASE_DAY = "day"
# Depression angle (degrees below the horizon) of the sun at each dawn/dusk
# phase boundary. A negative value means the sun is above the horizon.
DEPRESSION_ASTRONOMICAL = 18.0
DEPRESSION_NAUTICAL = 12.0
DEPRESSION_CIVIL = 6.0
DEPRESSION_SMALL_DAY = -10.0
# Sun elevation (degrees above the horizon) at the start of the "small day" phase.
_ELEVATION_SMALL_DAY = 10.0
# 4 mins is one degree of arc change of the sun on its circle.
# During the night and the middle of the day we don't update
@@ -162,8 +162,7 @@ class Sun(Entity):
@override
def state(self) -> str:
"""Return the state of the sun."""
# 0.8333 is the same value as astral uses
if self.solar_elevation > -0.833:
if self.solar_elevation > ELEVATION_HORIZON:
return STATE_ABOVE_HORIZON
return STATE_BELOW_HORIZON
@@ -189,8 +188,11 @@ class Sun(Entity):
utc_point_in_time: datetime,
sun_event: str,
before: str | None,
depression: float | None = None,
elevation: float | None = None,
) -> datetime:
# astral takes a depression (degrees below the horizon), i.e. the
# negated elevation.
depression = None if elevation is None else -elevation
next_utc = get_observer_astral_event_next(
self.observer, sun_event, utc_point_in_time, depression=depression
)
@@ -209,36 +211,36 @@ class Sun(Entity):
# Work our way around the solar cycle, figure out the next
# phase. Some of these are stored.
self._check_event(
utc_point_in_time, "dawn", PHASE_NIGHT, DEPRESSION_ASTRONOMICAL
utc_point_in_time, "dawn", PHASE_NIGHT, ELEVATION_ASTRONOMICAL
)
self._check_event(
utc_point_in_time, "dawn", PHASE_ASTRONOMICAL_TWILIGHT, DEPRESSION_NAUTICAL
utc_point_in_time, "dawn", PHASE_ASTRONOMICAL_TWILIGHT, ELEVATION_NAUTICAL
)
self.next_dawn = self._check_event(
utc_point_in_time, "dawn", PHASE_NAUTICAL_TWILIGHT, DEPRESSION_CIVIL
utc_point_in_time, "dawn", PHASE_NAUTICAL_TWILIGHT, ELEVATION_CIVIL
)
self.next_rising = self._check_event(
utc_point_in_time, SUN_EVENT_SUNRISE, PHASE_TWILIGHT
)
self._check_event(
utc_point_in_time, "dawn", PHASE_SMALL_DAY, DEPRESSION_SMALL_DAY
utc_point_in_time, "dawn", PHASE_SMALL_DAY, _ELEVATION_SMALL_DAY
)
self.next_noon = self._check_event(utc_point_in_time, "noon", None)
self._check_event(utc_point_in_time, "dusk", PHASE_DAY, DEPRESSION_SMALL_DAY)
self._check_event(utc_point_in_time, "dusk", PHASE_DAY, _ELEVATION_SMALL_DAY)
self.next_setting = self._check_event(
utc_point_in_time, SUN_EVENT_SUNSET, PHASE_SMALL_DAY
)
self.next_dusk = self._check_event(
utc_point_in_time, "dusk", PHASE_TWILIGHT, DEPRESSION_CIVIL
utc_point_in_time, "dusk", PHASE_TWILIGHT, ELEVATION_CIVIL
)
self._check_event(
utc_point_in_time, "dusk", PHASE_NAUTICAL_TWILIGHT, DEPRESSION_NAUTICAL
utc_point_in_time, "dusk", PHASE_NAUTICAL_TWILIGHT, ELEVATION_NAUTICAL
)
self._check_event(
utc_point_in_time,
"dusk",
PHASE_ASTRONOMICAL_TWILIGHT,
DEPRESSION_ASTRONOMICAL,
ELEVATION_ASTRONOMICAL,
)
self.next_midnight = self._check_event(utc_point_in_time, "midnight", None)
@@ -252,11 +254,11 @@ class Sun(Entity):
self.phase = PHASE_DAY
elif elevation >= 0:
self.phase = PHASE_SMALL_DAY
elif elevation >= -6:
elif elevation >= ELEVATION_CIVIL:
self.phase = PHASE_TWILIGHT
elif elevation >= -12:
elif elevation >= ELEVATION_NAUTICAL:
self.phase = PHASE_NAUTICAL_TWILIGHT
elif elevation >= -18:
elif elevation >= ELEVATION_ASTRONOMICAL:
self.phase = PHASE_ASTRONOMICAL_TWILIGHT
else:
self.phase = PHASE_NIGHT
+26
View File
@@ -1,4 +1,30 @@
{
"conditions": {
"elevation": {
"condition": "mdi:sun-angle"
},
"is_ascending": {
"condition": "mdi:weather-sunset-up"
},
"is_descending": {
"condition": "mdi:weather-sunset-down"
},
"is_evening_twilight": {
"condition": "mdi:weather-sunset-down"
},
"is_morning_twilight": {
"condition": "mdi:weather-sunset-up"
},
"is_night": {
"condition": "mdi:weather-night"
},
"is_set": {
"condition": "mdi:weather-sunny-off"
},
"is_up": {
"condition": "mdi:weather-sunny"
}
},
"entity": {
"binary_sensor": {
"solar_rising": {
+53
View File
@@ -1,10 +1,62 @@
{
"common": {
"condition_threshold_name": "Threshold type",
"trigger_for_name": "For at least",
"trigger_threshold_name": "Threshold type",
"twilight_type_description": "The phase of twilight.",
"twilight_type_name": "Twilight type"
},
"conditions": {
"elevation": {
"description": "Tests the sun's elevation against a threshold you set.",
"fields": {
"threshold": {
"name": "[%key:component::sun::common::condition_threshold_name%]"
}
},
"name": "Sun elevation"
},
"is_ascending": {
"description": "Tests if the sun is ascending.",
"name": "Sun is ascending"
},
"is_descending": {
"description": "Tests if the sun is descending.",
"name": "Sun is descending"
},
"is_evening_twilight": {
"description": "Tests if it is evening twilight, optionally of a specific type.",
"fields": {
"type": {
"description": "[%key:component::sun::common::twilight_type_description%]",
"name": "[%key:component::sun::common::twilight_type_name%]"
}
},
"name": "It is evening twilight"
},
"is_morning_twilight": {
"description": "Tests if it is morning twilight, optionally of a specific type.",
"fields": {
"type": {
"description": "[%key:component::sun::common::twilight_type_description%]",
"name": "[%key:component::sun::common::twilight_type_name%]"
}
},
"name": "It is morning twilight"
},
"is_night": {
"description": "Tests if it is night.",
"name": "It is night"
},
"is_set": {
"description": "Tests if the sun is set.",
"name": "Sun is set"
},
"is_up": {
"description": "Tests if the sun is up.",
"name": "Sun is up"
}
},
"config": {
"step": {
"user": {
@@ -45,6 +97,7 @@
"selector": {
"twilight_type": {
"options": {
"any": "Any",
"astronomical": "Astronomical",
"civil": "Civil",
"nautical": "Nautical"
+17 -10
View File
@@ -3,7 +3,6 @@
from datetime import datetime, timedelta
from typing import Any, cast, override
import astral
import voluptuous as vol
from homeassistant.const import (
@@ -47,7 +46,13 @@ from homeassistant.helpers.trigger import (
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import dt as dt_util
from .const import DOMAIN, STATE_ATTR_ELEVATION
from .const import (
DOMAIN,
ELEVATION_ASTRONOMICAL,
ELEVATION_CIVIL,
ELEVATION_NAUTICAL,
STATE_ATTR_ELEVATION,
)
# Names of solar events supported by the astral.sun module
_SUN_EVENT_SOLAR_NOON = "noon"
@@ -59,11 +64,11 @@ _TWILIGHT_CIVIL = "civil"
_TWILIGHT_NAUTICAL = "nautical"
_TWILIGHT_ASTRONOMICAL = "astronomical"
# Sun depression below the horizon for each twilight phase, as defined by astral.
_TWILIGHT_DEPRESSIONS = {
_TWILIGHT_CIVIL: astral.Depression.CIVIL,
_TWILIGHT_NAUTICAL: astral.Depression.NAUTICAL,
_TWILIGHT_ASTRONOMICAL: astral.Depression.ASTRONOMICAL,
# Sun elevation at each twilight boundary.
_TWILIGHT_ELEVATIONS = {
_TWILIGHT_CIVIL: ELEVATION_CIVIL,
_TWILIGHT_NAUTICAL: ELEVATION_NAUTICAL,
_TWILIGHT_ASTRONOMICAL: ELEVATION_ASTRONOMICAL,
}
# The sun is a singleton, so the elevation triggers always target sun.sun
@@ -228,7 +233,7 @@ _DAWN_DUSK_TRIGGER_SCHEMA = vol.Schema(
{
vol.Required(CONF_OPTIONS, default=dict): {
vol.Optional(CONF_TYPE, default=_TWILIGHT_CIVIL): vol.In(
_TWILIGHT_DEPRESSIONS
_TWILIGHT_ELEVATIONS
),
}
}
@@ -244,7 +249,7 @@ class SunDawnDuskTrigger(SunEventTrigger):
"""Initialize the trigger."""
super().__init__(hass, config)
self._twilight: str = self._options[CONF_TYPE]
self._depression = _TWILIGHT_DEPRESSIONS[self._twilight]
self._elevation = _TWILIGHT_ELEVATIONS[self._twilight]
@override
def _get_next_event(self, utc_point_in_time: datetime) -> datetime:
@@ -252,7 +257,9 @@ class SunDawnDuskTrigger(SunEventTrigger):
get_astral_observer(self._hass),
self._event,
utc_point_in_time,
depression=self._depression,
# astral takes a depression (degrees below the horizon), i.e. the
# negated elevation.
depression=-self._elevation,
)
@override
@@ -15,14 +15,8 @@ from homeassistant.components.alarm_control_panel import (
CodeFormat,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_CODE,
CONF_NAME,
CONF_STATE,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.const import ATTR_CODE, CONF_NAME, CONF_STATE
from homeassistant.core import HomeAssistant, State, callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
@@ -171,6 +165,7 @@ class AbstractTemplateAlarmControlPanel(
_entity_id_format = ENTITY_ID_FORMAT
_optimistic_entity = True
_state_option = CONF_STATE
_restore_state_properties = ("_attr_alarm_state",)
# The super init is not called because
# TemplateEntity calls AbstractTemplateEntity.__init__.
@@ -204,17 +199,6 @@ class AbstractTemplateAlarmControlPanel(
self.add_script(action_id, action_config, name, DOMAIN)
self._attr_supported_features |= supported_feature
async def _async_handle_restored_state(self) -> None:
if (
(last_state := await self.async_get_last_state()) is not None
and last_state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE)
and last_state.state in AlarmControlPanelState
# The trigger might have fired already while we waited for stored data,
# then we should not restore state
and self._attr_alarm_state is None
):
self._attr_alarm_state = AlarmControlPanelState(last_state.state)
async def _async_alarm_arm(self, state: Any, script: Script | None, code: Any):
"""Arm the panel to specified state with supplied script."""
@@ -290,6 +274,15 @@ class AbstractTemplateAlarmControlPanel(
code=code,
)
@override
def restore_last_state_state(self, last_state: State) -> bool:
"""Restore the state from the last state."""
if last_state.state in AlarmControlPanelState:
self._attr_alarm_state = AlarmControlPanelState(last_state.state)
return True
return False
class StateAlarmControlPanelEntity(TemplateEntity, AbstractTemplateAlarmControlPanel):
"""Representation of a templated Alarm Control Panel."""
@@ -310,12 +303,6 @@ class StateAlarmControlPanelEntity(TemplateEntity, AbstractTemplateAlarmControlP
AbstractTemplateAlarmControlPanel.__init__(self, name)
@override
async def async_added_to_hass(self) -> None:
"""Restore last state."""
await super().async_added_to_hass()
await self._async_handle_restored_state()
class TriggerAlarmControlPanelEntity(TriggerEntity, AbstractTemplateAlarmControlPanel):
"""Alarm Control Panel entity based on trigger data."""
@@ -332,9 +319,3 @@ class TriggerAlarmControlPanelEntity(TriggerEntity, AbstractTemplateAlarmControl
TriggerEntity.__init__(self, hass, coordinator, config)
self._attr_name = name = self._rendered.get(CONF_NAME, DEFAULT_NAME)
AbstractTemplateAlarmControlPanel.__init__(self, name)
@override
async def async_added_to_hass(self) -> None:
"""Restore last state."""
await super().async_added_to_hass()
await self._async_handle_restored_state()
@@ -21,10 +21,8 @@ from homeassistant.const import (
CONF_STATE,
CONF_UNIT_OF_MEASUREMENT,
STATE_ON,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, State, callback
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv, template
from homeassistant.helpers.entity_platform import (
@@ -129,6 +127,7 @@ class AbstractTemplateBinarySensor(
_entity_id_format = ENTITY_ID_FORMAT
_state_option = CONF_STATE
_restore_state_properties = ("_attr_is_on",)
# The super init is not called because TemplateEntity
# and TriggerEntity will call
@@ -160,6 +159,12 @@ class AbstractTemplateBinarySensor(
except vol.Invalid:
self.setup_template(CONF_DELAY_OFF, "_delay_off", cv.positive_time_period)
@override
def restore_last_state_state(self, last_state: State) -> bool:
"""Restore the state from the last state."""
self._attr_is_on = last_state.state == STATE_ON
return True
@callback
@abstractmethod
def _update_state(self, result: Any) -> None:
@@ -181,22 +186,6 @@ class StateBinarySensorEntity(TemplateEntity, AbstractTemplateBinarySensor):
TemplateEntity.__init__(self, hass, config, unique_id)
AbstractTemplateBinarySensor.__init__(self, config)
@override
async def async_added_to_hass(self) -> None:
"""Restore state."""
if (
(
CONF_DELAY_ON in self._templates
or CONF_DELAY_OFF in self._templates
or self._delay_on is not None
or self._delay_off is not None
)
and (last_state := await self.async_get_last_state()) is not None
and last_state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE)
):
self._attr_is_on = last_state.state == STATE_ON
await super().async_added_to_hass()
@callback
@override
def _update_state(self, result):
@@ -233,10 +222,49 @@ class StateBinarySensorEntity(TemplateEntity, AbstractTemplateBinarySensor):
self._delay_cancel = async_call_later(self.hass, delay, _set_state)
@dataclass
class AutoOffExtraStoredData(ExtraStoredData):
"""Object to hold extra stored data."""
auto_off_time: datetime | None
@override
def as_dict(self) -> dict[str, Any]:
"""Return a dict representation of additional data."""
auto_off_time: datetime | dict[str, str] | None = self.auto_off_time
if isinstance(auto_off_time, datetime):
auto_off_time = {
"__type": str(type(auto_off_time)),
"isoformat": auto_off_time.isoformat(),
}
return {
"auto_off_time": auto_off_time,
}
@classmethod
def from_dict(cls, restored: dict[str, Any]) -> Self | None:
"""Initialize a stored binary sensor state from a dict."""
try:
auto_off_time = restored["auto_off_time"]
except KeyError:
return None
try:
type_ = auto_off_time["__type"]
if type_ == "<class 'datetime.datetime'>":
auto_off_time = dt_util.parse_datetime(auto_off_time["isoformat"])
except TypeError:
pass
except KeyError:
return None
return cls(auto_off_time)
class TriggerBinarySensorEntity(TriggerEntity, AbstractTemplateBinarySensor):
"""Sensor entity based on trigger data."""
domain = BINARY_SENSOR_DOMAIN
_restore_state_extra_data = AutoOffExtraStoredData
# delay on and delay off are validated when the state is validated.
skip_rendered_result = (CONF_DELAY_ON, CONF_DELAY_OFF)
@@ -264,32 +292,19 @@ class TriggerBinarySensorEntity(TriggerEntity, AbstractTemplateBinarySensor):
self._parse_result.add(CONF_AUTO_OFF)
@override
async def async_added_to_hass(self) -> None:
"""Restore last state."""
await super().async_added_to_hass()
def restore_extra_data(self, extra_data: AutoOffExtraStoredData) -> None:
"""Restore extra data from the last state."""
if CONF_AUTO_OFF not in self._config:
return
if (
(last_state := await self.async_get_last_state()) is not None
and (extra_data := await self.async_get_last_binary_sensor_data())
is not None
and last_state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE)
# The trigger might have fired already while we waited for stored data,
# then we should not restore state
and self._attr_is_on is None
):
self._attr_is_on = last_state.state == STATE_ON
self.restore_attributes(last_state)
auto_off_time := extra_data.auto_off_time
) is not None and auto_off_time <= dt_util.utcnow():
# It's already past the saved auto off time
self._attr_is_on = False
if CONF_AUTO_OFF not in self._config:
return
if (
auto_off_time := extra_data.auto_off_time
) is not None and auto_off_time <= dt_util.utcnow():
# It's already past the saved auto off time
self._attr_is_on = False
if self._attr_is_on and auto_off_time is not None:
self._set_auto_off(auto_off_time)
if self._attr_is_on and auto_off_time is not None:
self._set_auto_off(auto_off_time)
@callback
def _cancel_delays(self):
@@ -401,51 +416,3 @@ class TriggerBinarySensorEntity(TriggerEntity, AbstractTemplateBinarySensor):
def extra_restore_state_data(self) -> AutoOffExtraStoredData:
"""Return specific state data to be restored."""
return AutoOffExtraStoredData(self._auto_off_time)
async def async_get_last_binary_sensor_data(
self,
) -> AutoOffExtraStoredData | None:
"""Restore auto_off_time."""
if (restored_last_extra_data := await self.async_get_last_extra_data()) is None:
return None
return AutoOffExtraStoredData.from_dict(restored_last_extra_data.as_dict())
@dataclass
class AutoOffExtraStoredData(ExtraStoredData):
"""Object to hold extra stored data."""
auto_off_time: datetime | None
@override
def as_dict(self) -> dict[str, Any]:
"""Return a dict representation of additional data."""
auto_off_time: datetime | dict[str, str] | None = self.auto_off_time
if isinstance(auto_off_time, datetime):
auto_off_time = {
"__type": str(type(auto_off_time)),
"isoformat": auto_off_time.isoformat(),
}
return {
"auto_off_time": auto_off_time,
}
@classmethod
def from_dict(cls, restored: dict[str, Any]) -> Self | None:
"""Initialize a stored binary sensor state from a dict."""
try:
auto_off_time = restored["auto_off_time"]
except KeyError:
return None
try:
type_ = auto_off_time["__type"]
if type_ == "<class 'datetime.datetime'>":
auto_off_time = dt_util.parse_datetime(auto_off_time["isoformat"])
except TypeError:
# native_value is not a dict
pass
except KeyError:
# native_value is a dict, but does not have all values
return None
return cls(auto_off_time)
+108 -3
View File
@@ -5,15 +5,27 @@ from collections.abc import Callable, Sequence
from dataclasses import dataclass
from typing import Any, override
from homeassistant.const import CONF_DEVICE_ID, CONF_OPTIMISTIC
from homeassistant.core import Context, HomeAssistant, callback
from homeassistant.const import (
ATTR_ENTITY_PICTURE,
ATTR_FRIENDLY_NAME,
ATTR_ICON,
CONF_DEVICE_ID,
CONF_ICON,
CONF_NAME,
CONF_OPTIMISTIC,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import Context, HomeAssistant, State, callback
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.entity import Entity, async_generate_entity_id
from homeassistant.helpers.script import Script, _VarsType
from homeassistant.helpers.template import Template, TemplateStateFromEntityId
from homeassistant.helpers.typing import ConfigType
from .const import CONF_DEFAULT_ENTITY_ID
from .const import CONF_ATTRIBUTES, CONF_DEFAULT_ENTITY_ID, CONF_PICTURE
_SENTINEL = object()
@dataclass
@@ -34,6 +46,11 @@ class AbstractTemplateEntity(Entity):
_optimistic_entity: bool = False
_extra_optimistic_options: tuple[str, ...] | None = None
_state_option: str | None = None
_restore_state_extra_data: Any | None = None
# Restore state properties. The state will be restored if set to None.
# If a tuple is supplied, all properties must be None for the state to restore.
_restore_state_properties: tuple[str, ...] | None = None
def __init__(
self,
@@ -46,6 +63,10 @@ class AbstractTemplateEntity(Entity):
self._config = config
self._templates: dict[str, EntityTemplate] = {}
self._action_scripts: dict[str, Script] = {}
self._attr_extra_state_attributes = {}
self._attribute_templates: dict[str, Template] | None = config.get(
CONF_ATTRIBUTES
)
if self._optimistic_entity:
optimistic = config.get(CONF_OPTIMISTIC)
@@ -200,3 +221,87 @@ class AbstractTemplateEntity(Entity):
},
context=context,
)
async def _async_get_last_template_data(
self,
) -> Any | None:
"""Get the last template data."""
if self._restore_state_extra_data is None or not hasattr(
self, "async_get_last_extra_data"
):
return _SENTINEL
if (restored_last_extra_data := await self.async_get_last_extra_data()) is None:
return None
return self._restore_state_extra_data.from_dict(
restored_last_extra_data.as_dict()
)
def restore_extra_data(self, extra_data: Any) -> None:
"""Restore extra data from the last state."""
async def async_restore_last_state(self) -> None:
"""Restore the state from the last state."""
if not hasattr(self, "async_get_last_state"):
return
last_state: State | None = await self.async_get_last_state()
if last_state is None:
return
# Handle extra data.
extra_data = _SENTINEL
if self._restore_state_extra_data is not None:
extra_data = await self._async_get_last_template_data()
if (
extra_data is None
or last_state.state in (STATE_UNKNOWN, STATE_UNAVAILABLE)
or (
self._restore_state_properties is not None
and any(
getattr(self, attr) is not None
for attr in self._restore_state_properties
)
)
):
return
if not self.restore_last_state_state(last_state):
return
self.restore_last_state_attributes(last_state)
# Extra data should be loaded last
if extra_data is not _SENTINEL:
self.restore_extra_data(extra_data)
def restore_last_state_state(self, last_state: State) -> bool:
"""Restore the state from the last state."""
return True
@abstractmethod
def restore_attribute(self, conf_attr: str, attr: str, restored_value: Any) -> None:
"""Restore an attribute from the last value."""
def restore_last_state_attributes(self, last_state: State) -> None:
"""Restore attributes from the last state."""
# Restore built-in attributes from templates
for conf_key, attr, _attr in (
(CONF_ICON, ATTR_ICON, "_attr_icon"),
(CONF_NAME, ATTR_FRIENDLY_NAME, "_attr_name"),
(CONF_PICTURE, ATTR_ENTITY_PICTURE, "_attr_entity_picture"),
):
if conf_key not in self._config or attr not in last_state.attributes:
continue
value = last_state.attributes[attr]
self.restore_attribute(conf_key, _attr, value)
self._attr_extra_state_attributes = {}
# Restore attributes from template attributes
if self._attribute_templates:
for attr in self._config[CONF_ATTRIBUTES]:
if attr not in last_state.attributes:
continue
self._attr_extra_state_attributes[attr] = last_state.attributes[attr]
+11 -22
View File
@@ -16,16 +16,11 @@ from homeassistant.components.sensor import (
STATE_CLASSES_SCHEMA,
RestoreSensor,
SensorDeviceClass,
SensorExtraStoredData,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_DEVICE_CLASS,
CONF_STATE,
CONF_UNIT_OF_MEASUREMENT,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.const import CONF_DEVICE_CLASS, CONF_STATE, CONF_UNIT_OF_MEASUREMENT
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import (
@@ -186,6 +181,8 @@ class AbstractTemplateSensor(AbstractTemplateEntity, RestoreSensor):
_entity_id_format = ENTITY_ID_FORMAT
_state_option = CONF_STATE
_restore_state_extra_data = SensorExtraStoredData
_restore_state_properties = ("_attr_native_value",)
# The super init is not called because TemplateEntity
# and TriggerEntity will call
@@ -230,6 +227,13 @@ class AbstractTemplateSensor(AbstractTemplateEntity, RestoreSensor):
return validate_datetime(self, CONF_STATE, self.device_class)(result)
@override
def restore_extra_data(self, extra_data: SensorExtraStoredData) -> None:
"""Restore the extra data."""
# Do not restore native_unit_of_measurement, this is always pulled from the
# sensor configuration.
self._attr_native_value = extra_data.native_value
class StateSensorEntity(TemplateEntity, AbstractTemplateSensor):
"""Representation of a Template Sensor."""
@@ -262,18 +266,3 @@ class TriggerSensorEntity(TriggerEntity, AbstractTemplateSensor):
"""Initialize."""
TriggerEntity.__init__(self, hass, coordinator, config)
AbstractTemplateSensor.__init__(self, config)
@override
async def async_added_to_hass(self) -> None:
"""Restore last state."""
await super().async_added_to_hass()
if (
(last_state := await self.async_get_last_state()) is not None
and (extra_data := await self.async_get_last_sensor_data()) is not None
and last_state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE)
# The trigger might have fired already while we waited for stored data,
# then we should not restore state
and CONF_STATE not in self._rendered
):
self._attr_native_value = extra_data.native_value
self.restore_attributes(last_state)
+9 -32
View File
@@ -10,14 +10,8 @@ from homeassistant.components.switch import (
SwitchEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_NAME,
CONF_STATE,
STATE_ON,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.const import CONF_NAME, CONF_STATE, STATE_ON
from homeassistant.core import HomeAssistant, State, callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
@@ -124,6 +118,7 @@ class AbstractTemplateSwitch(AbstractTemplateEntity, SwitchEntity, RestoreEntity
_entity_id_format = ENTITY_ID_FORMAT
_optimistic_entity = True
_state_option = CONF_STATE
_restore_state_properties = ("_attr_is_on",)
# The super init is not called because TemplateEntity
# and TriggerEntity will call
@@ -162,6 +157,12 @@ class AbstractTemplateSwitch(AbstractTemplateEntity, SwitchEntity, RestoreEntity
self._attr_is_on = False
self.async_write_ha_state()
@override
def restore_last_state_state(self, last_state: State) -> bool:
"""Restore the state from the last state."""
self._attr_is_on = last_state.state == STATE_ON
return True
class StateSwitchEntity(TemplateEntity, AbstractTemplateSwitch):
"""Representation of a Template switch."""
@@ -181,16 +182,6 @@ class StateSwitchEntity(TemplateEntity, AbstractTemplateSwitch):
assert name is not None
AbstractTemplateSwitch.__init__(self, name, config)
@override
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
if CONF_STATE not in self._templates:
# restore state after startup
await super().async_added_to_hass()
if state := await self.async_get_last_state():
self._attr_is_on = state.state == STATE_ON
await super().async_added_to_hass()
class TriggerSwitchEntity(TriggerEntity, AbstractTemplateSwitch):
"""Switch entity based on trigger data."""
@@ -207,17 +198,3 @@ class TriggerSwitchEntity(TriggerEntity, AbstractTemplateSwitch):
TriggerEntity.__init__(self, hass, coordinator, config)
name = self._rendered.get(CONF_NAME, DEFAULT_NAME)
AbstractTemplateSwitch.__init__(self, name, config)
@override
async def async_added_to_hass(self) -> None:
"""Restore last state."""
await super().async_added_to_hass()
if (
(last_state := await self.async_get_last_state()) is not None
and last_state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE)
# The trigger might have fired already while we waited for stored data,
# then we should not restore state
and self.is_on is None
):
self._attr_is_on = last_state.state == STATE_ON
self.restore_attributes(last_state)
@@ -43,7 +43,7 @@ from homeassistant.helpers.template import (
)
from homeassistant.helpers.typing import ConfigType
from .const import CONF_ATTRIBUTES, CONF_AVAILABILITY, CONF_PICTURE
from .const import CONF_AVAILABILITY, CONF_PICTURE
from .entity import AbstractTemplateEntity
_LOGGER = logging.getLogger(__name__)
@@ -161,7 +161,6 @@ class TemplateEntity(AbstractTemplateEntity):
AbstractTemplateEntity.__init__(self, hass, config)
self._template_attrs: dict[Template, list[_TemplateAttribute]] = {}
self._template_result_info: TrackTemplateResultInfo | None = None
self._attr_extra_state_attributes = {}
self._self_ref_update_count = 0
self._attr_unique_id = unique_id
self._preview_callback: (
@@ -177,7 +176,6 @@ class TemplateEntity(AbstractTemplateEntity):
| None
) = None
self._run_variables: ScriptVariables | dict
self._attribute_templates = config.get(CONF_ATTRIBUTES)
self._availability_template = config.get(CONF_AVAILABILITY)
self._run_variables = config.get(CONF_VARIABLES, {})
self._blueprint_inputs = config.get("raw_blueprint_inputs")
@@ -569,12 +567,20 @@ class TemplateEntity(AbstractTemplateEntity):
preview_callback(None, None, None, str(err))
return self._call_on_remove_callbacks
@override
def restore_attribute(self, conf_attr: str, attr: str, restored_value: Any) -> None:
"""Restore an attribute from the last value."""
setattr(self, attr, restored_value)
@override
async def async_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
await super().async_added_to_hass()
self._async_setup_templates()
async_at_start(self.hass, self._async_template_startup)
await self.async_restore_last_state()
async def async_update(self) -> None:
"""Call for forced update."""
@@ -54,7 +54,12 @@ class TriggerEntity( # pylint: disable=home-assistant-enforce-class-module
"""Handle being added to Home Assistant."""
await super().async_added_to_hass()
if self.coordinator.data is not None:
# The trigger already produced data; rendering it must win over
# restored state, so skip restore entirely to avoid clobbering the
# freshly rendered attributes.
self._process_data()
else:
await self.async_restore_last_state()
@override
def _set_unique_id(self, unique_id: str | None) -> None:
@@ -151,6 +156,18 @@ class TriggerEntity( # pylint: disable=home-assistant-enforce-class-module
return super().available
@property
@override
def extra_state_attributes(self) -> dict[str, Any] | None:
"""Return the state attributes."""
# Override TriggerBaseEntity's extra_state_attributes property to restore Entity's extra state attributes behavior.
return self._attr_extra_state_attributes
@override
def restore_attribute(self, conf_attr: str, attr: str, restored_value: Any) -> None:
"""Restore an attribute from the last value."""
self._rendered[conf_attr] = restored_value
@callback
@override
def _render_script_variables(self) -> dict:
@@ -209,7 +226,20 @@ class TriggerEntity( # pylint: disable=home-assistant-enforce-class-module
self._render_single_templates(
rendered, variables, [state_option] if state_option else []
)
self._render_attributes(rendered, variables)
if self._attribute_templates:
attributes = {}
for attribute, template in self._attribute_templates.items():
try:
value = template_render_complex(template, variables)
attributes[attribute] = value
variables.update({attribute: value})
except TemplateError as err:
log_triggered_template_error(
self.entity_id, err, attribute=attribute
)
self._attr_extra_state_attributes = attributes
self._rendered = rendered
def _handle_rendered_results(self) -> bool:
+10 -21
View File
@@ -15,13 +15,8 @@ from homeassistant.components.update import (
UpdateEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_DEVICE_CLASS,
CONF_NAME,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME
from homeassistant.core import HomeAssistant, State, callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
@@ -143,6 +138,7 @@ class AbstractTemplateUpdate(AbstractTemplateEntity, UpdateEntity):
"""Representation of a template update features."""
_entity_id_format = ENTITY_ID_FORMAT
_restore_state_properties = ("_attr_installed_version", "_attr_latest_version")
# The super init is not called because TemplateEntity
# and TriggerEntity will call
@@ -245,6 +241,13 @@ class AbstractTemplateUpdate(AbstractTemplateEntity, UpdateEntity):
context=self._context,
)
@override
def restore_last_state_state(self, last_state: State) -> bool:
"""Restore the state from the last state."""
self._attr_installed_version = last_state.attributes[ATTR_INSTALLED_VERSION]
self._attr_latest_version = last_state.attributes[ATTR_LATEST_VERSION]
return True
class StateUpdateEntity(TemplateEntity, AbstractTemplateUpdate):
"""Representation of a Template update."""
@@ -300,20 +303,6 @@ class TriggerUpdateEntity(TriggerEntity, AbstractTemplateUpdate):
if CONF_PICTURE in config:
self._parse_result.add(CONF_PICTURE)
@override
async def async_added_to_hass(self) -> None:
"""Restore last state."""
await super().async_added_to_hass()
if (
(last_state := await self.async_get_last_state()) is not None
and last_state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE)
and self._attr_installed_version is None
and self._attr_latest_version is None
):
self._attr_installed_version = last_state.attributes[ATTR_INSTALLED_VERSION]
self._attr_latest_version = last_state.attributes[ATTR_LATEST_VERSION]
self.restore_attributes(last_state)
@property
@override
def entity_picture(self) -> str | None:
+119 -129
View File
@@ -38,10 +38,8 @@ from homeassistant.const import (
CONF_ICON_TEMPLATE,
CONF_NAME,
CONF_TEMPERATURE_UNIT,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import HomeAssistant, State, callback
from homeassistant.helpers import config_validation as cv, template
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
@@ -404,12 +402,76 @@ def validate_forecast(
return validate
class AbstractTemplateWeather(AbstractTemplateEntity, WeatherEntity):
@dataclass(kw_only=True)
class WeatherExtraStoredData(ExtraStoredData):
"""Object to hold extra stored data."""
last_apparent_temperature: float | None
last_cloud_coverage: int | None
last_dew_point: float | None
last_humidity: float | None
last_ozone: float | None
last_pressure: float | None
last_temperature: float | None
last_uv_index: float | None
last_visibility: float | None
last_wind_bearing: float | str | None
last_wind_gust_speed: float | None
last_wind_speed: float | None
@override
def as_dict(self) -> dict[str, Any]:
"""Return a dict representation of the event data."""
return asdict(self)
@classmethod
def from_dict(cls, restored: dict[str, Any]) -> Self | None:
"""Initialize a stored event state from a dict."""
for key, vtypes in (
("last_apparent_temperature", (float, int)),
("last_cloud_coverage", (float, int)),
("last_dew_point", (float, int)),
("last_humidity", (float, int)),
("last_ozone", (float, int)),
("last_pressure", (float, int)),
("last_temperature", (float, int)),
("last_uv_index", (float, int)),
("last_visibility", (float, int)),
("last_wind_bearing", (float, int, str)),
("last_wind_gust_speed", (float, int)),
("last_wind_speed", (float, int)),
):
# This is needed to safeguard against previous restore data that has strings
# instead of floats or ints.
if key not in restored or (
(value := restored[key]) is not None and not isinstance(value, vtypes)
):
return None
return cls(
last_apparent_temperature=restored["last_apparent_temperature"],
last_cloud_coverage=restored["last_cloud_coverage"],
last_dew_point=restored["last_dew_point"],
last_humidity=restored["last_humidity"],
last_ozone=restored["last_ozone"],
last_pressure=restored["last_pressure"],
last_temperature=restored["last_temperature"],
last_uv_index=restored["last_uv_index"],
last_visibility=restored["last_visibility"],
last_wind_bearing=restored["last_wind_bearing"],
last_wind_gust_speed=restored["last_wind_gust_speed"],
last_wind_speed=restored["last_wind_speed"],
)
class AbstractTemplateWeather(AbstractTemplateEntity, WeatherEntity, RestoreEntity):
"""Representation of a template weathers features."""
_entity_id_format = ENTITY_ID_FORMAT
_state_option = CONF_CONDITION
_optimistic_entity = True
_restore_state_extra_data = WeatherExtraStoredData
_restore_state_properties = ("_attr_condition",)
# The super init is not called because TemplateEntity
# and TriggerEntity will call
@@ -554,126 +616,6 @@ class AbstractTemplateWeather(AbstractTemplateEntity, WeatherEntity):
"""Return the daily forecast in native units."""
return self._forecast_twice_daily or []
class StateWeatherEntity(TemplateEntity, AbstractTemplateWeather):
"""Representation of a Template weather."""
_attr_should_poll = False
def __init__(
self,
hass: HomeAssistant,
config: ConfigType,
unique_id: str | None,
) -> None:
"""Initialize the Template weather."""
TemplateEntity.__init__(self, hass, config, unique_id)
AbstractTemplateWeather.__init__(self, config)
@dataclass(kw_only=True)
class WeatherExtraStoredData(ExtraStoredData):
"""Object to hold extra stored data."""
last_apparent_temperature: float | None
last_cloud_coverage: int | None
last_dew_point: float | None
last_humidity: float | None
last_ozone: float | None
last_pressure: float | None
last_temperature: float | None
last_uv_index: float | None
last_visibility: float | None
last_wind_bearing: float | str | None
last_wind_gust_speed: float | None
last_wind_speed: float | None
@override
def as_dict(self) -> dict[str, Any]:
"""Return a dict representation of the event data."""
return asdict(self)
@classmethod
def from_dict(cls, restored: dict[str, Any]) -> Self | None:
"""Initialize a stored event state from a dict."""
for key, vtypes in (
("last_apparent_temperature", (float, int)),
("last_cloud_coverage", (float, int)),
("last_dew_point", (float, int)),
("last_humidity", (float, int)),
("last_ozone", (float, int)),
("last_pressure", (float, int)),
("last_temperature", (float, int)),
("last_uv_index", (float, int)),
("last_visibility", (float, int)),
("last_wind_bearing", (float, int, str)),
("last_wind_gust_speed", (float, int)),
("last_wind_speed", (float, int)),
):
# This is needed to safeguard against previous restore data that has strings
# instead of floats or ints.
if key not in restored or (
(value := restored[key]) is not None and not isinstance(value, vtypes)
):
return None
return cls(
last_apparent_temperature=restored["last_apparent_temperature"],
last_cloud_coverage=restored["last_cloud_coverage"],
last_dew_point=restored["last_dew_point"],
last_humidity=restored["last_humidity"],
last_ozone=restored["last_ozone"],
last_pressure=restored["last_pressure"],
last_temperature=restored["last_temperature"],
last_uv_index=restored["last_uv_index"],
last_visibility=restored["last_visibility"],
last_wind_bearing=restored["last_wind_bearing"],
last_wind_gust_speed=restored["last_wind_gust_speed"],
last_wind_speed=restored["last_wind_speed"],
)
class TriggerWeatherEntity(TriggerEntity, AbstractTemplateWeather, RestoreEntity):
"""Weather entity based on trigger data."""
domain = WEATHER_DOMAIN
def __init__(
self,
hass: HomeAssistant,
coordinator: TriggerUpdateCoordinator,
config: ConfigType,
) -> None:
"""Initialize."""
TriggerEntity.__init__(self, hass, coordinator, config)
AbstractTemplateWeather.__init__(self, config)
@override
async def async_added_to_hass(self) -> None:
"""Restore last state."""
await super().async_added_to_hass()
if (
(state := await self.async_get_last_state())
and state.state is not None
and state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE)
and (weather_data := await self.async_get_last_weather_data())
):
self._attr_native_apparent_temperature = (
weather_data.last_apparent_temperature
)
self._attr_cloud_coverage = weather_data.last_cloud_coverage
self._attr_condition = state.state
self._attr_native_dew_point = weather_data.last_dew_point
self._attr_humidity = weather_data.last_humidity
self._attr_ozone = weather_data.last_ozone
self._attr_native_pressure = weather_data.last_pressure
self._attr_native_temperature = weather_data.last_temperature
self._attr_uv_index = weather_data.last_uv_index
self._attr_native_visibility = weather_data.last_visibility
self._attr_wind_bearing = weather_data.last_wind_bearing
self._attr_native_wind_gust_speed = weather_data.last_wind_gust_speed
self._attr_native_wind_speed = weather_data.last_wind_speed
@property
@override
def extra_restore_state_data(self) -> WeatherExtraStoredData:
@@ -693,8 +635,56 @@ class TriggerWeatherEntity(TriggerEntity, AbstractTemplateWeather, RestoreEntity
last_wind_speed=self.native_wind_speed,
)
async def async_get_last_weather_data(self) -> WeatherExtraStoredData | None:
"""Restore weather specific state data."""
if (restored_last_extra_data := await self.async_get_last_extra_data()) is None:
return None
return WeatherExtraStoredData.from_dict(restored_last_extra_data.as_dict())
@override
def restore_last_state_state(self, last_state: State) -> bool:
"""Restore the state from the last state."""
self._attr_condition = last_state.state
return True
@override
def restore_extra_data(self, extra_data: WeatherExtraStoredData) -> None:
"""Restore the extra data."""
self._attr_native_apparent_temperature = extra_data.last_apparent_temperature
self._attr_cloud_coverage = extra_data.last_cloud_coverage
self._attr_native_dew_point = extra_data.last_dew_point
self._attr_humidity = extra_data.last_humidity
self._attr_ozone = extra_data.last_ozone
self._attr_native_pressure = extra_data.last_pressure
self._attr_native_temperature = extra_data.last_temperature
self._attr_uv_index = extra_data.last_uv_index
self._attr_native_visibility = extra_data.last_visibility
self._attr_wind_bearing = extra_data.last_wind_bearing
self._attr_native_wind_gust_speed = extra_data.last_wind_gust_speed
self._attr_native_wind_speed = extra_data.last_wind_speed
class StateWeatherEntity(TemplateEntity, AbstractTemplateWeather):
"""Representation of a Template weather."""
_attr_should_poll = False
def __init__(
self,
hass: HomeAssistant,
config: ConfigType,
unique_id: str | None,
) -> None:
"""Initialize the Template weather."""
TemplateEntity.__init__(self, hass, config, unique_id)
AbstractTemplateWeather.__init__(self, config)
class TriggerWeatherEntity(TriggerEntity, AbstractTemplateWeather):
"""Weather entity based on trigger data."""
domain = WEATHER_DOMAIN
def __init__(
self,
hass: HomeAssistant,
coordinator: TriggerUpdateCoordinator,
config: ConfigType,
) -> None:
"""Initialize."""
TriggerEntity.__init__(self, hass, coordinator, config)
AbstractTemplateWeather.__init__(self, config)
@@ -455,7 +455,7 @@
"name": "[%key:component::zwave_js::services::set_config_parameter::fields::value::name%]"
}
},
"name": "Bulk set partial configuration parameters (advanced)"
"name": "Bulk set partial configuration parameters"
},
"clear_lock_usercode": {
"description": "Clears a user code from a lock.",
@@ -559,7 +559,7 @@
"name": "Parameters"
}
},
"name": "Invoke a Command Class API on a node (advanced)"
"name": "Invoke a Command Class API on a node"
},
"multicast_set_value": {
"description": "Changes any value that Z-Wave recognizes on multiple Z-Wave devices using multicast, so all devices receive the message simultaneously. This action has minimal validation so only use this action if you know what you are doing.",
@@ -605,7 +605,7 @@
"name": "[%key:component::zwave_js::services::set_config_parameter::fields::value::name%]"
}
},
"name": "Set a value on multiple devices via multicast (advanced)"
"name": "Set a value on multiple devices via multicast"
},
"ping": {
"description": "Forces Z-Wave to try to reach a node. This can be used to update the status of the node in Z-Wave when you think it doesn't accurately reflect reality, e.g. reviving a failed/dead node or marking the node as asleep.",
@@ -649,7 +649,7 @@
"name": "Notification Type"
}
},
"name": "Refresh notifications on a node (advanced)"
"name": "Refresh notifications on a node"
},
"refresh_value": {
"description": "Force updates the values of a Z-Wave entity.",
@@ -869,7 +869,7 @@
"name": "Wait for result"
}
},
"name": "Set a value (advanced)"
"name": "Set a value"
}
}
}
+21 -1
View File
@@ -22,7 +22,7 @@ if TYPE_CHECKING:
APPLICATION_NAME: Final = "HomeAssistant"
MAJOR_VERSION: Final = 2026
MINOR_VERSION: Final = 7
PATCH_VERSION: Final = "0.dev0"
PATCH_VERSION: Final = "0b0"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 14, 2)
@@ -455,6 +455,26 @@ ATTR_TEMPERATURE: Final = "temperature"
ATTR_PERSONS: Final = "persons"
class EntityCapabilityAttribute(StrEnum):
"""Capability attributes shared by all entities."""
GROUP_ENTITIES = "group_entities"
class EntityStateAttribute(StrEnum):
"""State attributes shared by all entities."""
ASSUMED_STATE = "assumed_state"
ATTRIBUTION = "attribution"
DEVICE_CLASS = "device_class"
ENTITY_PICTURE = "entity_picture"
FRIENDLY_NAME = "friendly_name"
ICON = "icon"
RESTORED = "restored"
SUPPORTED_FEATURES = "supported_features"
UNIT_OF_MEASUREMENT = "unit_of_measurement"
# #### UNITS OF MEASUREMENT ####
# Apparent power units
class UnitOfApparentPower(StrEnum):
-12
View File
@@ -653,12 +653,6 @@
"config_flow": true,
"iot_class": "cloud_polling"
},
"avion": {
"name": "Avi-on",
"integration_type": "hub",
"config_flow": false,
"iot_class": "assumed_state"
},
"avosdim": {
"name": "Avosdim",
"integration_type": "virtual",
@@ -735,12 +729,6 @@
"config_flow": false,
"iot_class": "local_polling"
},
"beewi_smartclim": {
"name": "BeeWi SmartClim BLE sensor",
"integration_type": "hub",
"config_flow": false,
"iot_class": "local_polling"
},
"bega": {
"name": "BEGA",
"iot_standards": [
+16 -21
View File
@@ -30,21 +30,14 @@ from propcache.api import cached_property
import voluptuous as vol
from homeassistant.const import (
ATTR_ASSUMED_STATE,
ATTR_ATTRIBUTION,
ATTR_DEVICE_CLASS,
ATTR_ENTITY_PICTURE,
ATTR_FRIENDLY_NAME,
ATTR_GROUP_ENTITIES,
ATTR_ICON,
ATTR_SUPPORTED_FEATURES,
ATTR_UNIT_OF_MEASUREMENT,
DEVICE_DEFAULT_NAME,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
EntityCapabilityAttribute,
EntityCategory,
EntityStateAttribute,
)
from homeassistant.core import (
CALLBACK_TYPE,
@@ -167,7 +160,7 @@ def get_device_class(hass: HomeAssistant, entity_id: str) -> str | None:
First try the statemachine, then entity registry.
"""
if state := hass.states.get(entity_id):
return state.attributes.get(ATTR_DEVICE_CLASS)
return state.attributes.get(EntityStateAttribute.DEVICE_CLASS)
entity_registry = er.async_get(hass)
if not (entry := entity_registry.async_get(entity_id)):
@@ -192,7 +185,7 @@ def get_supported_features(hass: HomeAssistant, entity_id: str) -> int:
First try the statemachine, then entity registry.
"""
if state := hass.states.get(entity_id):
return state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) # type: ignore[no-any-return]
return state.attributes.get(EntityStateAttribute.SUPPORTED_FEATURES, 0) # type: ignore[no-any-return]
entity_registry = er.async_get(hass)
if not (entry := entity_registry.async_get(entity_id)):
@@ -207,7 +200,7 @@ def get_unit_of_measurement(hass: HomeAssistant, entity_id: str) -> str | None:
First try the statemachine, then entity registry.
"""
if state := hass.states.get(entity_id):
return state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
return state.attributes.get(EntityStateAttribute.UNIT_OF_MEASUREMENT)
entity_registry = er.async_get(hass)
if not (entry := entity_registry.async_get(entity_id)):
@@ -1116,7 +1109,9 @@ class Entity(
capability_attr = self.capability_attributes
if self.__group is not None:
capability_attr = capability_attr.copy() if capability_attr else {}
capability_attr[ATTR_GROUP_ENTITIES] = self.__group.member_entity_ids.copy()
capability_attr[EntityCapabilityAttribute.GROUP_ENTITIES] = (
self.__group.member_entity_ids.copy()
)
attr = capability_attr.copy() if capability_attr else {}
@@ -1129,25 +1124,25 @@ class Entity(
attr |= extra_state_attributes
if (unit_of_measurement := self.unit_of_measurement) is not None:
attr[ATTR_UNIT_OF_MEASUREMENT] = unit_of_measurement
attr[EntityStateAttribute.UNIT_OF_MEASUREMENT] = unit_of_measurement
if assumed_state := self.assumed_state:
attr[ATTR_ASSUMED_STATE] = assumed_state
attr[EntityStateAttribute.ASSUMED_STATE] = assumed_state
if (attribution := self.attribution) is not None:
attr[ATTR_ATTRIBUTION] = attribution
attr[EntityStateAttribute.ATTRIBUTION] = attribution
original_device_class = self.device_class
if (
device_class := (entry and entry.device_class) or original_device_class
) is not None:
attr[ATTR_DEVICE_CLASS] = str(device_class)
attr[EntityStateAttribute.DEVICE_CLASS] = str(device_class)
if (entity_picture := self.entity_picture) is not None:
attr[ATTR_ENTITY_PICTURE] = entity_picture
attr[EntityStateAttribute.ENTITY_PICTURE] = entity_picture
if (icon := (entry and entry.icon) or self.icon) is not None:
attr[ATTR_ICON] = icon
attr[EntityStateAttribute.ICON] = icon
original_name = self.name
if original_name is UNDEFINED:
@@ -1169,10 +1164,10 @@ class Entity(
self._cached_friendly_name = (original_name, name)
if name:
attr[ATTR_FRIENDLY_NAME] = name
attr[EntityStateAttribute.FRIENDLY_NAME] = name
if (supported_features := self.supported_features) is not None:
attr[ATTR_SUPPORTED_FEATURES] = supported_features
attr[EntityStateAttribute.SUPPORTED_FEATURES] = supported_features
return (
state,
+32
View File
@@ -32,6 +32,7 @@ from homeassistant.exceptions import (
PlatformNotReady,
)
from homeassistant.generated import languages
from homeassistant.loader import async_suggest_report_issue
from homeassistant.setup import SetupPhases, async_start_setup
from homeassistant.util.async_ import create_eager_task
from homeassistant.util.hass_dict import HassKey
@@ -53,6 +54,8 @@ SLOW_SETUP_MAX_WAIT = 60
SLOW_ADD_ENTITY_MAX_WAIT = 15 # Per Entity
SLOW_ADD_MIN_TIMEOUT = 500
MAX_ENABLED_ENTITIES_PER_CONFIG_ENTRY = 10000
PLATFORM_NOT_READY_RETRIES = 10
DATA_ENTITY_PLATFORM: HassKey[dict[str, list[EntityPlatform]]] = HassKey(
"entity_platform"
@@ -276,6 +279,8 @@ class EntityPlatform:
# Storage for entities for this specific platform only
# which are indexed by entity_id
self.entities: dict[str, Entity] = {}
# Whether we already warned about reaching the config entry entity limit
self._entity_limit_warned = False
self._tasks: list[asyncio.Task[None]] = []
# Stop tracking tasks after setup is completed
self._setup_complete = False
@@ -959,6 +964,32 @@ class EntityPlatform:
if not entity.entity_registry_visible_default:
hidden_by = RegistryEntryHider.INTEGRATION
if (
disabled_by is None
and not registered_entity_id
and self.config_entry is not None
and entity_registry.entities.get_enabled_count_for_config_entry_id(
self.config_entry.entry_id
)
>= MAX_ENABLED_ENTITIES_PER_CONFIG_ENTRY
):
if not self._entity_limit_warned:
report_issue = async_suggest_report_issue(
self.hass, integration_domain=self.platform_name
)
self.logger.warning(
"Reached the maximum of %s enabled entities for config entry "
"%s; not adding more entities for integration %s until "
"existing entities are removed or disabled, please %s",
MAX_ENABLED_ENTITIES_PER_CONFIG_ENTRY,
self.config_entry.entry_id,
self.platform_name,
report_issue,
)
self._entity_limit_warned = True
entity.add_to_platform_abort()
return
entry = entity_registry.async_get_or_create(
self.domain,
self.platform_name,
@@ -1054,6 +1085,7 @@ class EntityPlatform:
This method must be run in the event loop.
"""
self.async_cancel_retry_setup()
self._entity_limit_warned = False
if not self.entities:
return
+30 -16
View File
@@ -20,18 +20,13 @@ import attr
import voluptuous as vol
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_FRIENDLY_NAME,
ATTR_ICON,
ATTR_RESTORED,
ATTR_SUPPORTED_FEATURES,
ATTR_UNIT_OF_MEASUREMENT,
EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP,
MAX_LENGTH_STATE_DOMAIN,
MAX_LENGTH_STATE_ENTITY_ID,
STATE_UNAVAILABLE,
EntityCategory,
EntityStateAttribute,
Platform,
)
from homeassistant.core import (
@@ -429,28 +424,28 @@ class RegistryEntry:
@callback
def write_unavailable_state(self, hass: HomeAssistant) -> None:
"""Write the unavailable state to the state machine."""
attrs: dict[str, Any] = {ATTR_RESTORED: True}
attrs: dict[str, Any] = {EntityStateAttribute.RESTORED: True}
if self.capabilities is not None:
attrs.update(self.capabilities)
device_class = self.device_class or self.original_device_class
if device_class is not None:
attrs[ATTR_DEVICE_CLASS] = device_class
attrs[EntityStateAttribute.DEVICE_CLASS] = device_class
icon = self.icon or self.original_icon
if icon is not None:
attrs[ATTR_ICON] = icon
attrs[EntityStateAttribute.ICON] = icon
name = self.name or self.original_name
if name is not None:
attrs[ATTR_FRIENDLY_NAME] = name
name = async_get_full_entity_name(hass, self)
if name:
attrs[EntityStateAttribute.FRIENDLY_NAME] = name
if self.supported_features is not None:
attrs[ATTR_SUPPORTED_FEATURES] = self.supported_features
attrs[EntityStateAttribute.SUPPORTED_FEATURES] = self.supported_features
if self.unit_of_measurement is not None:
attrs[ATTR_UNIT_OF_MEASUREMENT] = self.unit_of_measurement
attrs[EntityStateAttribute.UNIT_OF_MEASUREMENT] = self.unit_of_measurement
hass.states.async_set(self.entity_id, STATE_UNAVAILABLE, attrs)
@@ -935,6 +930,8 @@ class EntityRegistryItems(BaseRegistryItems[RegistryEntry]):
- device_id -> dict[key, True]
- area_id -> dict[key, True]
- label -> dict[key, True]
Also maintains a count of enabled entries per config entry id.
"""
def __init__(self) -> None:
@@ -943,6 +940,7 @@ class EntityRegistryItems(BaseRegistryItems[RegistryEntry]):
self._entry_ids: dict[str, RegistryEntry] = {}
self._index: dict[tuple[str, str, str], str] = {}
self._config_entry_id_index: RegistryIndexType = defaultdict(dict)
self._config_entry_id_enabled_count: dict[str, int] = {}
self._device_id_index: RegistryIndexType = defaultdict(dict)
self._area_id_index: RegistryIndexType = defaultdict(dict)
self._labels_index: RegistryIndexType = defaultdict(dict)
@@ -956,6 +954,10 @@ class EntityRegistryItems(BaseRegistryItems[RegistryEntry]):
# https://discuss.python.org/t/add-orderedset-to-stdlib/12730
if (config_entry_id := entry.config_entry_id) is not None:
self._config_entry_id_index[config_entry_id][key] = True
if not entry.disabled_by:
self._config_entry_id_enabled_count[config_entry_id] = (
self._config_entry_id_enabled_count.get(config_entry_id, 0) + 1
)
if (device_id := entry.device_id) is not None:
self._device_id_index[device_id][key] = True
if (area_id := entry.area_id) is not None:
@@ -973,6 +975,12 @@ class EntityRegistryItems(BaseRegistryItems[RegistryEntry]):
del self._index[(entry.domain, entry.platform, entry.unique_id)]
if config_entry_id := entry.config_entry_id:
self._unindex_entry_value(key, config_entry_id, self._config_entry_id_index)
if not entry.disabled_by:
count = self._config_entry_id_enabled_count[config_entry_id] - 1
if count:
self._config_entry_id_enabled_count[config_entry_id] = count
else:
del self._config_entry_id_enabled_count[config_entry_id]
if device_id := entry.device_id:
self._unindex_entry_value(key, device_id, self._device_id_index)
if area_id := entry.area_id:
@@ -1013,6 +1021,10 @@ class EntityRegistryItems(BaseRegistryItems[RegistryEntry]):
data[key] for key in self._config_entry_id_index.get(config_entry_id, ())
]
def get_enabled_count_for_config_entry_id(self, config_entry_id: str) -> int:
"""Return the number of enabled entries for a config entry."""
return self._config_entry_id_enabled_count.get(config_entry_id, 0)
def get_entries_for_area_id(self, area_id: str) -> list[RegistryEntry]:
"""Get entries for area."""
data = self.data
@@ -2438,7 +2450,9 @@ def _async_setup_entity_restore(hass: HomeAssistant, registry: EntityRegistry) -
if event.data["action"] == "update":
old_entity_id = event.data["old_entity_id"]
old_state = hass.states.get(old_entity_id)
if old_state is None or not old_state.attributes.get(ATTR_RESTORED):
if old_state is None or not old_state.attributes.get(
EntityStateAttribute.RESTORED
):
return
hass.states.async_remove(old_entity_id, context=event.context)
if entry := registry.async_get(event.data["entity_id"]):
@@ -2447,7 +2461,7 @@ def _async_setup_entity_restore(hass: HomeAssistant, registry: EntityRegistry) -
state = hass.states.get(event.data["entity_id"])
if state is None or not state.attributes.get(ATTR_RESTORED):
if state is None or not state.attributes.get(EntityStateAttribute.RESTORED):
return
hass.states.async_remove(event.data["entity_id"], context=event.context)
+18 -174
View File
@@ -441,19 +441,6 @@ class EntityTriggerBase(Trigger):
"""
return True
def _is_valid_to_state(
self, to_state: State, report_not_triggered: _NotTriggeredReasonReporter
) -> bool:
"""Check if to_state can fire the trigger, reporting why if it cannot.
Firing-path wrapper around `is_valid_state` for the changed entity.
When the state cannot fire the trigger, subclasses may use
`report_not_triggered` to record an interesting reason - e.g. a
non-numeric value or an unsupported unit - in the automation trace.
The base implementation never reports.
"""
return self.is_valid_state(to_state)
def _should_include(self, state: State) -> bool:
"""Check if an entity should participate in all/count checks.
@@ -605,23 +592,11 @@ class EntityTriggerBase(Trigger):
if not from_state or not to_state:
return
@callback
def report_not_triggered(reason: str, /, **data: Any) -> None:
"""Report why this evaluated change did not fire the trigger."""
if did_not_trigger is None:
return
did_not_trigger(
NotTriggeredInfo(reason=reason, data=data), event.context
)
# The trigger should never fire if the new state is excluded.
if to_state.state in self._excluded_states:
return
# The trigger should never fire if the new state is not a target
# state. Interesting reasons (e.g. a non-numeric value or an
# unsupported unit) are reported for the trace.
if not self._is_valid_to_state(to_state, report_not_triggered):
# The trigger should never fire if the new state is excluded
# or not a target state.
if to_state.state in self._excluded_states or not self.is_valid_state(
to_state
):
return
# The trigger should never fire if the origin state is excluded
@@ -828,11 +803,7 @@ class EntityNumericalStateTriggerBase(EntityTriggerBase):
return True
return unit == self._valid_unit
def _get_threshold_value(
self,
threshold: ThresholdConfig | None,
report_not_triggered: _NotTriggeredReasonReporter | None = None,
) -> float | None:
def _get_threshold_value(self, threshold: ThresholdConfig | None) -> float | None:
"""Get threshold value from float or entity state."""
if threshold is None:
return None
@@ -842,26 +813,13 @@ class EntityNumericalStateTriggerBase(EntityTriggerBase):
if not (state := self._hass.states.get(threshold.entity)): # type: ignore[arg-type]
# Entity not found
return None
unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
if not self._is_valid_unit(unit):
if not self._is_valid_unit(state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)):
# Entity unit does not match the expected unit
if report_not_triggered is not None:
report_not_triggered(
"threshold_unit_not_supported",
entity_id=threshold.entity,
unit=unit,
)
return None
try:
return float(state.state)
except TypeError, ValueError:
# Entity state is not a valid number
if report_not_triggered is not None:
report_not_triggered(
"threshold_value_not_numeric",
entity_id=threshold.entity,
value=state.state,
)
return None
@override
@@ -883,53 +841,10 @@ class EntityNumericalStateTriggerBase(EntityTriggerBase):
return None
@override
def _is_valid_to_state(
self, to_state: State, report_not_triggered: _NotTriggeredReasonReporter
) -> bool:
"""Check if to_state can fire, reporting non-numeric / unit reasons."""
return self.is_valid_state(to_state, report_not_triggered)
def _report_tracked_value_problem(
self, state: State, report_not_triggered: _NotTriggeredReasonReporter
) -> None:
"""Report why `_get_tracked_value` rejected this state.
Called only when the tracked value is invalid. It mirrors the failure
modes of `_get_tracked_value` - which integrations override, so the
reason is derived here rather than reported inline: a state-sourced
value with an unsupported unit, otherwise a value that is not a number.
"""
domain_spec = self._domain_specs[state.domain]
raw_value: Any
if domain_spec.value_source is None:
unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
if not self._is_valid_unit(unit):
report_not_triggered(
"entity_unit_not_supported",
entity_id=state.entity_id,
unit=unit,
)
return
raw_value = state.state
else:
raw_value = state.attributes.get(domain_spec.value_source)
report_not_triggered(
"entity_value_not_numeric",
entity_id=state.entity_id,
value=raw_value,
)
@override
def is_valid_state(
self,
state: State,
report_not_triggered: _NotTriggeredReasonReporter | None = None,
) -> bool:
def is_valid_state(self, state: State) -> bool:
"""Check if the new state or state attribute matches the expected one."""
# Handle missing or None value case first to avoid expensive exceptions
if (current_value := self._get_tracked_value(state)) is None:
if report_not_triggered is not None:
self._report_tracked_value_problem(state, report_not_triggered)
return False
if self._threshold_type == NumericThresholdType.ANY:
@@ -938,32 +853,20 @@ class EntityNumericalStateTriggerBase(EntityTriggerBase):
return True
if self._threshold_type == NumericThresholdType.ABOVE:
if (
limit := self._get_threshold_value(self.threshold, report_not_triggered)
) is None:
if (limit := self._get_threshold_value(self.threshold)) is None:
# Entity not found or invalid number, don't trigger
return False
return current_value > limit
if self._threshold_type == NumericThresholdType.BELOW:
if (
limit := self._get_threshold_value(self.threshold, report_not_triggered)
) is None:
if (limit := self._get_threshold_value(self.threshold)) is None:
# Entity not found or invalid number, don't trigger
return False
return current_value < limit
# Mode is BETWEEN or OUTSIDE. Evaluate the lower limit first so at most
# one not-triggered reason is reported per change.
lower_limit = self._get_threshold_value(
self.lower_threshold, report_not_triggered
)
if lower_limit is None:
# Entity not found or invalid number, don't trigger
return False
upper_limit = self._get_threshold_value(
self.upper_threshold, report_not_triggered
)
if upper_limit is None:
# Mode is BETWEEN or OUTSIDE
lower_limit = self._get_threshold_value(self.lower_threshold)
upper_limit = self._get_threshold_value(self.upper_threshold)
if lower_limit is None or upper_limit is None:
# Entity not found or invalid number, don't trigger
return False
between = lower_limit <= current_value <= upper_limit
@@ -983,41 +886,7 @@ class EntityNumericalStateTriggerWithUnitBase(EntityNumericalStateTriggerBase):
return state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
@override
def _report_tracked_value_problem(
self, state: State, report_not_triggered: _NotTriggeredReasonReporter
) -> None:
"""Report why `_get_tracked_value` rejected this state.
Mirrors the with-unit failure modes: a value that is not a number,
otherwise a unit that cannot be converted to the base unit.
"""
domain_spec = self._domain_specs[state.domain]
raw_value: Any
if domain_spec.value_source is None:
raw_value = state.state
else:
raw_value = state.attributes.get(domain_spec.value_source)
try:
float(raw_value)
except TypeError, ValueError:
report_not_triggered(
"entity_value_not_numeric",
entity_id=state.entity_id,
value=raw_value,
)
return
report_not_triggered(
"entity_unit_not_supported",
entity_id=state.entity_id,
unit=self._get_entity_unit(state),
)
@override
def _get_threshold_value(
self,
threshold: ThresholdConfig | None,
report_not_triggered: _NotTriggeredReasonReporter | None = None,
) -> float | None:
def _get_threshold_value(self, threshold: ThresholdConfig | None) -> float | None:
"""Get threshold value from float or entity state."""
if threshold is None:
return None
@@ -1035,25 +904,14 @@ class EntityNumericalStateTriggerWithUnitBase(EntityNumericalStateTriggerBase):
value = float(state.state)
except TypeError, ValueError:
# Entity state is not a valid number
if report_not_triggered is not None:
report_not_triggered(
"threshold_value_not_numeric",
entity_id=threshold.entity,
value=state.state,
)
return None
unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
try:
return self._unit_converter.convert(value, unit, self._base_unit)
return self._unit_converter.convert(
value, state.attributes.get(ATTR_UNIT_OF_MEASUREMENT), self._base_unit
)
except HomeAssistantError:
# Unit conversion failed (i.e. incompatible units), treat as invalid number
if report_not_triggered is not None:
report_not_triggered(
"threshold_unit_not_supported",
entity_id=threshold.entity,
unit=unit,
)
return None
@override
@@ -1430,20 +1288,6 @@ class TriggerNotTriggeredReporter(Protocol):
"""Report that the trigger did not fire."""
class _NotTriggeredReasonReporter(Protocol):
"""Reports why an evaluated change did not fire an entity trigger.
Threaded down the firing-path checks for the changed entity so the leaf
that detects an interesting, user-actionable problem - a non-numeric value
or an unsupported unit - can record it in the automation trace. It wraps
the trigger's ``did_not_trigger`` reporter, building the
``NotTriggeredInfo`` from ``reason`` and the keyword ``data``.
"""
def __call__(self, reason: str, /, **data: Any) -> None:
"""Report, with diagnostic data, why the change did not fire."""
class TriggerNotTriggeredAction(Protocol):
"""Protocol type for the did_not_trigger consumer callback.
@@ -27,7 +27,7 @@ from homeassistant.const import (
CONF_UNIQUE_ID,
CONF_UNIT_OF_MEASUREMENT,
)
from homeassistant.core import HomeAssistant, State, callback
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError
from homeassistant.util.json import JSON_DECODE_EXCEPTIONS, json_loads
@@ -246,21 +246,6 @@ class TriggerBaseEntity(Entity):
"""Set unique id."""
self._unique_id = unique_id
def restore_attributes(self, last_state: State) -> None:
"""Restore attributes."""
for conf_key, attr in CONF_TO_ATTRIBUTE.items():
if conf_key not in self._config or attr not in last_state.attributes:
continue
self._rendered[conf_key] = last_state.attributes[attr]
if CONF_ATTRIBUTES in self._config:
extra_state_attributes = {}
for attr in self._config[CONF_ATTRIBUTES]:
if attr not in last_state.attributes:
continue
extra_state_attributes[attr] = last_state.attributes[attr]
self._rendered[CONF_ATTRIBUTES] = extra_state_attributes
def _template_variables(self, run_variables: dict[str, Any] | None = None) -> dict:
"""Render template variables."""
return {
+1 -1
View File
@@ -39,7 +39,7 @@ habluetooth==6.23.1
hass-nabucasa==2.2.0
hassil==3.8.0
home-assistant-bluetooth==2.0.0
home-assistant-frontend==20260527.7
home-assistant-frontend==20260624.0
home-assistant-intents==2026.6.1
httpx==0.28.1
ifaddr==0.2.0
+1 -1
View File
@@ -5,7 +5,7 @@ To update, run python3 -m script.hassfest
from typing import Final
FRONTEND_VERSION: Final[str] = "20260527.7"
FRONTEND_VERSION: Final[str] = "20260624.0"
MDI_ICONS: Final[set[str]] = {
"ab-testing",
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "homeassistant"
version = "2026.7.0.dev0"
version = "2026.7.0b0"
license = "Apache-2.0"
license-files = ["LICENSE*", "homeassistant/backports/LICENSE*"]
description = "Open-source home automation platform running on Python 3."
+2 -8
View File
@@ -608,9 +608,6 @@ av==17.0.1
# homeassistant.components.avea
avea==1.8.0
# homeassistant.components.avion
# avion==0.10
# homeassistant.components.axis
axis==72
@@ -650,9 +647,6 @@ batinfo==0.4.2
# homeassistant.components.scrape
beautifulsoup4==4.13.3
# homeassistant.components.beewi_smartclim
# beewi-smartclim==0.0.10
# homeassistant.components.bizkaibus
bizkaibus==0.1.1
@@ -1278,7 +1272,7 @@ hole==0.9.2
holidays==0.99
# homeassistant.components.frontend
home-assistant-frontend==20260527.7
home-assistant-frontend==20260624.0
# homeassistant.components.conversation
home-assistant-intents==2026.6.1
@@ -2576,7 +2570,7 @@ pysmhi==2.0.0
pysml==0.1.8
# homeassistant.components.smlight
pysmlight==0.3.2
pysmlight==0.4.0
# homeassistant.components.snmp
pysnmp==7.1.27
-1
View File
@@ -20,7 +20,6 @@ from script.hassfest.model import Config, Integration
# requirements_all.txt.
EXCLUDED_REQUIREMENTS_ALL = {
"atenpdu", # depends on pysnmp which is not maintained at this time
"avion",
"beewi-smartclim", # depends on bluepy
"bluepy",
"evdev",
-4
View File
@@ -172,7 +172,6 @@ INTEGRATIONS_WITHOUT_QUALITY_SCALE_FILE = [
"aurora_abb_powerone",
"aussie_broadband",
"avea",
"avion",
"aws",
"axis",
"azure_data_explorer",
@@ -186,7 +185,6 @@ INTEGRATIONS_WITHOUT_QUALITY_SCALE_FILE = [
"bang_olufsen",
"bayesian",
"bbox",
"beewi_smartclim",
"bitcoin",
"bizkaibus",
"blackbird",
@@ -1123,7 +1121,6 @@ INTEGRATIONS_WITHOUT_SCALE = [
"aurora_abb_powerone",
"aussie_broadband",
"avea",
"avion",
"awair",
"aws",
"axis",
@@ -1138,7 +1135,6 @@ INTEGRATIONS_WITHOUT_SCALE = [
"bang_olufsen",
"bayesian",
"bbox",
"beewi_smartclim",
"bitcoin",
"bizkaibus",
"blackbird",
@@ -39,8 +39,8 @@
# name: test_binary_sensors[binary_sensor.kitchen_lunar_ddeeff_timer_running-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'running',
'friendly_name': 'LUNAR-DDEEFF Timer running',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'running',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'LUNAR-DDEEFF Timer running',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.kitchen_lunar_ddeeff_timer_running',
@@ -39,7 +39,7 @@
# name: test_buttons[button.kitchen_lunar_ddeeff_reset_timer-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'LUNAR-DDEEFF Reset timer',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'LUNAR-DDEEFF Reset timer',
}),
'context': <ANY>,
'entity_id': 'button.kitchen_lunar_ddeeff_reset_timer',
@@ -89,7 +89,7 @@
# name: test_buttons[button.kitchen_lunar_ddeeff_start_stop_timer-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'LUNAR-DDEEFF Start/stop timer',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'LUNAR-DDEEFF Start/stop timer',
}),
'context': <ANY>,
'entity_id': 'button.kitchen_lunar_ddeeff_start_stop_timer',
@@ -139,7 +139,7 @@
# name: test_buttons[button.kitchen_lunar_ddeeff_tare-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'LUNAR-DDEEFF Tare',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'LUNAR-DDEEFF Tare',
}),
'context': <ANY>,
'entity_id': 'button.kitchen_lunar_ddeeff_tare',
@@ -41,10 +41,10 @@
# name: test_sensors[sensor.kitchen_lunar_ddeeff_battery-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'LUNAR-DDEEFF Battery',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'battery',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'LUNAR-DDEEFF Battery',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.kitchen_lunar_ddeeff_battery',
@@ -99,10 +99,10 @@
# name: test_sensors[sensor.kitchen_lunar_ddeeff_volume_flow_rate-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'volume_flow_rate',
'friendly_name': 'LUNAR-DDEEFF Volume flow rate',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'volume_flow_rate',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'LUNAR-DDEEFF Volume flow rate',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfVolumeFlowRate.MILLILITERS_PER_SECOND: 'mL/s'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfVolumeFlowRate.MILLILITERS_PER_SECOND: 'mL/s'>,
}),
'context': <ANY>,
'entity_id': 'sensor.kitchen_lunar_ddeeff_volume_flow_rate',
@@ -157,10 +157,10 @@
# name: test_sensors[sensor.kitchen_lunar_ddeeff_weight-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'weight',
'friendly_name': 'LUNAR-DDEEFF Weight',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'weight',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'LUNAR-DDEEFF Weight',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfMass.OUNCES: 'oz'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfMass.OUNCES: 'oz'>,
}),
'context': <ANY>,
'entity_id': 'sensor.kitchen_lunar_ddeeff_weight',
File diff suppressed because it is too large Load Diff
@@ -457,15 +457,15 @@
StateSnapshot({
'attributes': ReadOnlyDict({
<WeatherEntityStateAttribute.APPARENT_TEMPERATURE: 'apparent_temperature'>: 22.8,
'attribution': 'Data provided by AccuWeather',
<EntityStateAttribute.ATTRIBUTION: 'attribution'>: 'Data provided by AccuWeather',
<WeatherEntityStateAttribute.CLOUD_COVERAGE: 'cloud_coverage'>: 10,
<WeatherEntityStateAttribute.DEW_POINT: 'dew_point'>: 16.2,
'friendly_name': 'Home',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Home',
<WeatherEntityStateAttribute.HUMIDITY: 'humidity'>: 67,
<WeatherEntityStateAttribute.PRECIPITATION_UNIT: 'precipitation_unit'>: <UnitOfPrecipitationDepth.MILLIMETERS: 'mm'>,
<WeatherEntityStateAttribute.PRESSURE: 'pressure'>: 1012.0,
<WeatherEntityStateAttribute.PRESSURE_UNIT: 'pressure_unit'>: <UnitOfPressure.HPA: 'hPa'>,
'supported_features': <WeatherEntityFeature: 3>,
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <WeatherEntityFeature: 3>,
<WeatherEntityStateAttribute.TEMPERATURE: 'temperature'>: 22.6,
<WeatherEntityStateAttribute.TEMPERATURE_UNIT: 'temperature_unit'>: <UnitOfTemperature.CELSIUS: '°C'>,
<WeatherEntityStateAttribute.UV_INDEX: 'uv_index'>: 6,
@@ -51,7 +51,7 @@
'attributes': ReadOnlyDict({
<ClimateEntityStateAttribute.CURRENT_HUMIDITY: 'current_humidity'>: 50.0,
<ClimateEntityStateAttribute.CURRENT_TEMPERATURE: 'current_temperature'>: 22.0,
'friendly_name': 'Living Room',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Living Room',
<ClimateEntityCapabilityAttribute.HVAC_MODES: 'hvac_modes'>: list([
<HVACMode.COOL: 'cool'>,
<HVACMode.HEAT: 'heat'>,
@@ -61,7 +61,7 @@
]),
<ClimateEntityCapabilityAttribute.MAX_TEMP: 'max_temp'>: 30,
<ClimateEntityCapabilityAttribute.MIN_TEMP: 'min_temp'>: 16,
'supported_features': <ClimateEntityFeature: 385>,
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <ClimateEntityFeature: 385>,
<ClimateEntityStateAttribute.TEMPERATURE: 'temperature'>: 24.0,
}),
'context': <ANY>,
@@ -137,7 +137,7 @@
'medium',
'high',
]),
'friendly_name': 'Test System',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test System',
<ClimateEntityCapabilityAttribute.HVAC_MODES: 'hvac_modes'>: list([
<HVACMode.COOL: 'cool'>,
<HVACMode.HEAT: 'heat'>,
@@ -147,7 +147,7 @@
]),
<ClimateEntityCapabilityAttribute.MAX_TEMP: 'max_temp'>: 30.0,
<ClimateEntityCapabilityAttribute.MIN_TEMP: 'min_temp'>: 16.0,
'supported_features': <ClimateEntityFeature: 393>,
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <ClimateEntityFeature: 393>,
<ClimateEntityStateAttribute.TEMPERATURE: 'temperature'>: 24.0,
}),
'context': <ANY>,
@@ -39,7 +39,7 @@
# name: test_switch_entities[switch.test_system_away_mode-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Test System Away mode',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test System Away mode',
}),
'context': <ANY>,
'entity_id': 'switch.test_system_away_mode',
@@ -89,7 +89,7 @@
# name: test_switch_entities[switch.test_system_continuous_fan-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Test System Continuous fan',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test System Continuous fan',
}),
'context': <ANY>,
'entity_id': 'switch.test_system_continuous_fan',
@@ -139,7 +139,7 @@
# name: test_switch_entities[switch.test_system_quiet_mode-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Test System Quiet mode',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test System Quiet mode',
}),
'context': <ANY>,
'entity_id': 'switch.test_system_quiet_mode',
@@ -189,7 +189,7 @@
# name: test_switch_entities[switch.test_system_turbo_mode-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Test System Turbo mode',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test System Turbo mode',
}),
'context': <ANY>,
'entity_id': 'switch.test_system_turbo_mode',
@@ -47,10 +47,10 @@
# name: test_fallback_to_get_rooms[sensor.room_1_energy-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Room 1 Energy',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'energy',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Room 1 Energy',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.room_1_energy',
@@ -105,10 +105,10 @@
# name: test_fallback_to_get_rooms[sensor.room_1_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Room 1 Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Room 1 Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.room_1_temperature',
@@ -166,10 +166,10 @@
# name: test_multiple_devices_create_individual_sensors[sensor.room_1_energy-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Room 1 Energy',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'energy',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Room 1 Energy',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.room_1_energy',
@@ -224,10 +224,10 @@
# name: test_multiple_devices_create_individual_sensors[sensor.room_1_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Room 1 Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Room 1 Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.room_1_temperature',
@@ -285,10 +285,10 @@
# name: test_multiple_devices_create_individual_sensors[sensor.room_2_energy-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Room 2 Energy',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'energy',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Room 2 Energy',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.room_2_energy',
@@ -343,10 +343,10 @@
# name: test_multiple_devices_create_individual_sensors[sensor.room_2_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Room 2 Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Room 2 Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.room_2_temperature',
@@ -404,10 +404,10 @@
# name: test_sensor_cloud[sensor.room_1_energy-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Room 1 Energy',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'energy',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Room 1 Energy',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.room_1_energy',
@@ -462,10 +462,10 @@
# name: test_sensor_cloud[sensor.room_1_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Room 1 Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Room 1 Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.room_1_temperature',
@@ -39,8 +39,8 @@
# name: test_sensors[sensor.adguard_home_average_processing_speed-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Average processing speed',
'unit_of_measurement': <UnitOfTime.MILLISECONDS: 'ms'>,
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'AdGuard Home Average processing speed',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTime.MILLISECONDS: 'ms'>,
}),
'context': <ANY>,
'entity_id': 'sensor.adguard_home_average_processing_speed',
@@ -90,8 +90,8 @@
# name: test_sensors[sensor.adguard_home_dns_queries-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home DNS queries',
'unit_of_measurement': 'queries',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'AdGuard Home DNS queries',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'queries',
}),
'context': <ANY>,
'entity_id': 'sensor.adguard_home_dns_queries',
@@ -141,8 +141,8 @@
# name: test_sensors[sensor.adguard_home_dns_queries_blocked-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home DNS queries blocked',
'unit_of_measurement': 'queries',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'AdGuard Home DNS queries blocked',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'queries',
}),
'context': <ANY>,
'entity_id': 'sensor.adguard_home_dns_queries_blocked',
@@ -192,8 +192,8 @@
# name: test_sensors[sensor.adguard_home_dns_queries_blocked_ratio-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home DNS queries blocked ratio',
'unit_of_measurement': '%',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'AdGuard Home DNS queries blocked ratio',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.adguard_home_dns_queries_blocked_ratio',
@@ -243,8 +243,8 @@
# name: test_sensors[sensor.adguard_home_parental_control_blocked-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Parental control blocked',
'unit_of_measurement': 'requests',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'AdGuard Home Parental control blocked',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'requests',
}),
'context': <ANY>,
'entity_id': 'sensor.adguard_home_parental_control_blocked',
@@ -294,8 +294,8 @@
# name: test_sensors[sensor.adguard_home_rules_count-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Rules count',
'unit_of_measurement': 'rules',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'AdGuard Home Rules count',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'rules',
}),
'context': <ANY>,
'entity_id': 'sensor.adguard_home_rules_count',
@@ -345,8 +345,8 @@
# name: test_sensors[sensor.adguard_home_safe_browsing_blocked-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Safe browsing blocked',
'unit_of_measurement': 'requests',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'AdGuard Home Safe browsing blocked',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'requests',
}),
'context': <ANY>,
'entity_id': 'sensor.adguard_home_safe_browsing_blocked',
@@ -396,8 +396,8 @@
# name: test_sensors[sensor.adguard_home_safe_searches_enforced-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Safe searches enforced',
'unit_of_measurement': 'requests',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'AdGuard Home Safe searches enforced',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'requests',
}),
'context': <ANY>,
'entity_id': 'sensor.adguard_home_safe_searches_enforced',
@@ -39,7 +39,7 @@
# name: test_switch[switch.adguard_home_filtering-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Filtering',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'AdGuard Home Filtering',
}),
'context': <ANY>,
'entity_id': 'switch.adguard_home_filtering',
@@ -89,7 +89,7 @@
# name: test_switch[switch.adguard_home_parental_control-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Parental control',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'AdGuard Home Parental control',
}),
'context': <ANY>,
'entity_id': 'switch.adguard_home_parental_control',
@@ -139,7 +139,7 @@
# name: test_switch[switch.adguard_home_protection-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Protection',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'AdGuard Home Protection',
}),
'context': <ANY>,
'entity_id': 'switch.adguard_home_protection',
@@ -189,7 +189,7 @@
# name: test_switch[switch.adguard_home_query_log-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Query log',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'AdGuard Home Query log',
}),
'context': <ANY>,
'entity_id': 'switch.adguard_home_query_log',
@@ -239,7 +239,7 @@
# name: test_switch[switch.adguard_home_safe_browsing-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Safe browsing',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'AdGuard Home Safe browsing',
}),
'context': <ANY>,
'entity_id': 'switch.adguard_home_safe_browsing',
@@ -289,7 +289,7 @@
# name: test_switch[switch.adguard_home_safe_search-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'AdGuard Home Safe search',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'AdGuard Home Safe search',
}),
'context': <ANY>,
'entity_id': 'switch.adguard_home_safe_search',
@@ -41,15 +41,15 @@
'attributes': ReadOnlyDict({
<UpdateEntityStateAttribute.AUTO_UPDATE: 'auto_update'>: False,
<UpdateEntityStateAttribute.DISPLAY_PRECISION: 'display_precision'>: 0,
'entity_picture': '/api/brands/integration/adguard/icon.png',
'friendly_name': 'AdGuard Home',
<EntityStateAttribute.ENTITY_PICTURE: 'entity_picture'>: '/api/brands/integration/adguard/icon.png',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'AdGuard Home',
<UpdateEntityStateAttribute.IN_PROGRESS: 'in_progress'>: False,
<UpdateEntityStateAttribute.INSTALLED_VERSION: 'installed_version'>: 'v0.107.50',
<UpdateEntityStateAttribute.LATEST_VERSION: 'latest_version'>: 'v0.107.59',
<UpdateEntityStateAttribute.RELEASE_SUMMARY: 'release_summary'>: 'AdGuard Home v0.107.59 is now available!',
<UpdateEntityStateAttribute.RELEASE_URL: 'release_url'>: 'https://github.com/AdguardTeam/AdGuardHome/releases/tag/v0.107.59',
<UpdateEntityStateAttribute.SKIPPED_VERSION: 'skipped_version'>: None,
'supported_features': <UpdateEntityFeature: 1>,
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <UpdateEntityFeature: 1>,
<UpdateEntityStateAttribute.TITLE: 'title'>: None,
<UpdateEntityStateAttribute.UPDATE_PERCENTAGE: 'update_percentage'>: None,
}),
@@ -29,7 +29,7 @@
'high',
'auto',
]),
'friendly_name': 'myauto',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'myauto',
<ClimateEntityStateAttribute.HVAC_ACTION: 'hvac_action'>: <HVACAction.COOLING: 'cooling'>,
<ClimateEntityCapabilityAttribute.HVAC_MODES: 'hvac_modes'>: list([
<HVACMode.OFF: 'off'>,
@@ -47,7 +47,7 @@
'MyTemp',
'MyAuto',
]),
'supported_features': <ClimateEntityFeature: 410>,
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <ClimateEntityFeature: 410>,
<ClimateEntityStateAttribute.TARGET_TEMP_HIGH: 'target_temp_high'>: 24,
<ClimateEntityStateAttribute.TARGET_TEMP_LOW: 'target_temp_low'>: 20,
<ClimateEntityCapabilityAttribute.TARGET_TEMP_STEP: 'target_temp_step'>: 1,
@@ -20,9 +20,9 @@
# name: test_cover_async_setup_entry[switch.myzone_myfan]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'switch',
'friendly_name': 'myzone MyFan',
'icon': 'mdi:fan-auto',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'switch',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'myzone MyFan',
<EntityStateAttribute.ICON: 'icon'>: 'mdi:fan-auto',
}),
'context': <ANY>,
'entity_id': 'switch.myzone_myfan',
@@ -49,7 +49,7 @@
<LightEntityStateAttribute.BRIGHTNESS: 'brightness'>: 255,
<LightEntityStateAttribute.COLOR_MODE: 'color_mode'>: <ColorMode.RGBW: 'rgbw'>,
<LightEntityStateAttribute.COLOR_TEMP_KELVIN: 'color_temp_kelvin'>: None,
'friendly_name': 'Test Light',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test Light',
<LightEntityStateAttribute.HS_COLOR: 'hs_color'>: tuple(
0.0,
0.0,
@@ -71,7 +71,7 @@
<ColorMode.COLOR_TEMP: 'color_temp'>,
<ColorMode.RGBW: 'rgbw'>,
]),
'supported_features': <LightEntityFeature: 0>,
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <LightEntityFeature: 0>,
<LightEntityStateAttribute.XY_COLOR: 'xy_color'>: tuple(
0.323,
0.329,
@@ -39,7 +39,7 @@
# name: test_all_entities[indoor][button.airgradient_calibrate_co2_sensor-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient Calibrate CO2 sensor',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Calibrate CO2 sensor',
}),
'context': <ANY>,
'entity_id': 'button.airgradient_calibrate_co2_sensor',
@@ -89,7 +89,7 @@
# name: test_all_entities[indoor][button.airgradient_test_led_bar-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient Test LED bar',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Test LED bar',
}),
'context': <ANY>,
'entity_id': 'button.airgradient_test_led_bar',
@@ -139,7 +139,7 @@
# name: test_all_entities[outdoor][button.airgradient_calibrate_co2_sensor-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient Calibrate CO2 sensor',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Calibrate CO2 sensor',
}),
'context': <ANY>,
'entity_id': 'button.airgradient_calibrate_co2_sensor',
@@ -44,12 +44,12 @@
# name: test_all_entities[number.airgradient_display_brightness-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient Display brightness',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Display brightness',
<NumberEntityCapabilityAttribute.MAX: 'max'>: 100,
<NumberEntityCapabilityAttribute.MIN: 'min'>: 0,
<NumberEntityCapabilityAttribute.MODE: 'mode'>: <NumberMode.AUTO: 'auto'>,
<NumberEntityCapabilityAttribute.STEP: 'step'>: 1,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'number.airgradient_display_brightness',
@@ -104,12 +104,12 @@
# name: test_all_entities[number.airgradient_led_bar_brightness-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient LED bar brightness',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient LED bar brightness',
<NumberEntityCapabilityAttribute.MAX: 'max'>: 100,
<NumberEntityCapabilityAttribute.MIN: 'min'>: 0,
<NumberEntityCapabilityAttribute.MODE: 'mode'>: <NumberMode.AUTO: 'auto'>,
<NumberEntityCapabilityAttribute.STEP: 'step'>: 1,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'number.airgradient_led_bar_brightness',
@@ -48,7 +48,7 @@
# name: test_all_entities[indoor][select.airgradient_co2_automatic_baseline_duration-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient CO2 automatic baseline duration',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient CO2 automatic baseline duration',
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'1',
'8',
@@ -111,7 +111,7 @@
# name: test_all_entities[indoor][select.airgradient_configuration_source-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient Configuration source',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Configuration source',
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'cloud',
'local',
@@ -170,7 +170,7 @@
# name: test_all_entities[indoor][select.airgradient_display_pm_standard-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient Display PM standard',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Display PM standard',
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'ugm3',
'us_aqi',
@@ -229,7 +229,7 @@
# name: test_all_entities[indoor][select.airgradient_display_temperature_unit-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient Display temperature unit',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Display temperature unit',
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'c',
'f',
@@ -289,7 +289,7 @@
# name: test_all_entities[indoor][select.airgradient_led_bar_mode-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient LED bar mode',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient LED bar mode',
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'off',
'co2',
@@ -352,7 +352,7 @@
# name: test_all_entities[indoor][select.airgradient_nox_index_learning_offset-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient NOx index learning offset',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient NOx index learning offset',
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'12',
'60',
@@ -417,7 +417,7 @@
# name: test_all_entities[indoor][select.airgradient_voc_index_learning_offset-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient VOC index learning offset',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient VOC index learning offset',
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'12',
'60',
@@ -483,7 +483,7 @@
# name: test_all_entities[outdoor][select.airgradient_co2_automatic_baseline_duration-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient CO2 automatic baseline duration',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient CO2 automatic baseline duration',
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'1',
'8',
@@ -546,7 +546,7 @@
# name: test_all_entities[outdoor][select.airgradient_configuration_source-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient Configuration source',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Configuration source',
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'cloud',
'local',
@@ -608,7 +608,7 @@
# name: test_all_entities[outdoor][select.airgradient_nox_index_learning_offset-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient NOx index learning offset',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient NOx index learning offset',
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'12',
'60',
@@ -673,7 +673,7 @@
# name: test_all_entities[outdoor][select.airgradient_voc_index_learning_offset-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient VOC index learning offset',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient VOC index learning offset',
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'12',
'60',
@@ -41,10 +41,10 @@
# name: test_all_entities[indoor][sensor.airgradient_carbon_dioxide-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'carbon_dioxide',
'friendly_name': 'Airgradient Carbon dioxide',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'carbon_dioxide',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Carbon dioxide',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'ppm',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'ppm',
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_carbon_dioxide',
@@ -97,9 +97,9 @@
# name: test_all_entities[indoor][sensor.airgradient_carbon_dioxide_automatic_baseline_calibration-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Airgradient Carbon dioxide automatic baseline calibration',
'unit_of_measurement': <UnitOfTime.DAYS: 'd'>,
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'duration',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Carbon dioxide automatic baseline calibration',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTime.DAYS: 'd'>,
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_carbon_dioxide_automatic_baseline_calibration',
@@ -149,8 +149,8 @@
# name: test_all_entities[indoor][sensor.airgradient_display_brightness-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient Display brightness',
'unit_of_measurement': '%',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Display brightness',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_display_brightness',
@@ -205,8 +205,8 @@
# name: test_all_entities[indoor][sensor.airgradient_display_pm_standard-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Airgradient Display PM standard',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'enum',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Display PM standard',
<SensorEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'ugm3',
'us_aqi',
@@ -265,8 +265,8 @@
# name: test_all_entities[indoor][sensor.airgradient_display_temperature_unit-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Airgradient Display temperature unit',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'enum',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Display temperature unit',
<SensorEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'c',
'f',
@@ -322,10 +322,10 @@
# name: test_all_entities[indoor][sensor.airgradient_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'humidity',
'friendly_name': 'Airgradient Humidity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'humidity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Humidity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_humidity',
@@ -375,8 +375,8 @@
# name: test_all_entities[indoor][sensor.airgradient_led_bar_brightness-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient LED bar brightness',
'unit_of_measurement': '%',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient LED bar brightness',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_led_bar_brightness',
@@ -432,8 +432,8 @@
# name: test_all_entities[indoor][sensor.airgradient_led_bar_mode-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Airgradient LED bar mode',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'enum',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient LED bar mode',
<SensorEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'off',
'co2',
@@ -490,7 +490,7 @@
# name: test_all_entities[indoor][sensor.airgradient_nox_index-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient NOx index',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient NOx index',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'context': <ANY>,
@@ -544,9 +544,9 @@
# name: test_all_entities[indoor][sensor.airgradient_nox_index_learning_offset-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Airgradient NOx index learning offset',
'unit_of_measurement': <UnitOfTime.DAYS: 'd'>,
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'duration',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient NOx index learning offset',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTime.DAYS: 'd'>,
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_nox_index_learning_offset',
@@ -598,9 +598,9 @@
# name: test_all_entities[indoor][sensor.airgradient_pm0_3-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient PM0.3',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient PM0.3',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'particles/dL',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'particles/dL',
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_pm0_3',
@@ -652,10 +652,10 @@
# name: test_all_entities[indoor][sensor.airgradient_pm1-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'pm1',
'friendly_name': 'Airgradient PM1',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'pm1',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient PM1',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_pm1',
@@ -707,10 +707,10 @@
# name: test_all_entities[indoor][sensor.airgradient_pm10-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'pm10',
'friendly_name': 'Airgradient PM10',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'pm10',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient PM10',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_pm10',
@@ -762,10 +762,10 @@
# name: test_all_entities[indoor][sensor.airgradient_pm2_5-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'pm25',
'friendly_name': 'Airgradient PM2.5',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'pm25',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient PM2.5',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_pm2_5',
@@ -817,9 +817,9 @@
# name: test_all_entities[indoor][sensor.airgradient_raw_nox-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient Raw NOx',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Raw NOx',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'ticks',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'ticks',
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_raw_nox',
@@ -871,10 +871,10 @@
# name: test_all_entities[indoor][sensor.airgradient_raw_pm2_5-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'pm25',
'friendly_name': 'Airgradient Raw PM2.5',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'pm25',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Raw PM2.5',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_raw_pm2_5',
@@ -926,9 +926,9 @@
# name: test_all_entities[indoor][sensor.airgradient_raw_voc-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient Raw VOC',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Raw VOC',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'ticks',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'ticks',
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_raw_voc',
@@ -980,10 +980,10 @@
# name: test_all_entities[indoor][sensor.airgradient_signal_strength-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'signal_strength',
'friendly_name': 'Airgradient Signal strength',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'signal_strength',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Signal strength',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'dBm',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'dBm',
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_signal_strength',
@@ -1038,10 +1038,10 @@
# name: test_all_entities[indoor][sensor.airgradient_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Airgradient Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_temperature',
@@ -1093,7 +1093,7 @@
# name: test_all_entities[indoor][sensor.airgradient_voc_index-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient VOC index',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient VOC index',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'context': <ANY>,
@@ -1147,9 +1147,9 @@
# name: test_all_entities[indoor][sensor.airgradient_voc_index_learning_offset-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Airgradient VOC index learning offset',
'unit_of_measurement': <UnitOfTime.DAYS: 'd'>,
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'duration',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient VOC index learning offset',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTime.DAYS: 'd'>,
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_voc_index_learning_offset',
@@ -1202,9 +1202,9 @@
# name: test_all_entities[outdoor][sensor.airgradient_carbon_dioxide_automatic_baseline_calibration-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Airgradient Carbon dioxide automatic baseline calibration',
'unit_of_measurement': <UnitOfTime.DAYS: 'd'>,
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'duration',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Carbon dioxide automatic baseline calibration',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTime.DAYS: 'd'>,
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_carbon_dioxide_automatic_baseline_calibration',
@@ -1256,7 +1256,7 @@
# name: test_all_entities[outdoor][sensor.airgradient_nox_index-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient NOx index',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient NOx index',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'context': <ANY>,
@@ -1310,9 +1310,9 @@
# name: test_all_entities[outdoor][sensor.airgradient_nox_index_learning_offset-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Airgradient NOx index learning offset',
'unit_of_measurement': <UnitOfTime.DAYS: 'd'>,
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'duration',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient NOx index learning offset',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTime.DAYS: 'd'>,
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_nox_index_learning_offset',
@@ -1364,9 +1364,9 @@
# name: test_all_entities[outdoor][sensor.airgradient_raw_nox-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient Raw NOx',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Raw NOx',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'ticks',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'ticks',
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_raw_nox',
@@ -1418,9 +1418,9 @@
# name: test_all_entities[outdoor][sensor.airgradient_raw_voc-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient Raw VOC',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Raw VOC',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'ticks',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'ticks',
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_raw_voc',
@@ -1472,10 +1472,10 @@
# name: test_all_entities[outdoor][sensor.airgradient_signal_strength-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'signal_strength',
'friendly_name': 'Airgradient Signal strength',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'signal_strength',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Signal strength',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'dBm',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'dBm',
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_signal_strength',
@@ -1527,7 +1527,7 @@
# name: test_all_entities[outdoor][sensor.airgradient_voc_index-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient VOC index',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient VOC index',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'context': <ANY>,
@@ -1581,9 +1581,9 @@
# name: test_all_entities[outdoor][sensor.airgradient_voc_index_learning_offset-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Airgradient VOC index learning offset',
'unit_of_measurement': <UnitOfTime.DAYS: 'd'>,
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'duration',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient VOC index learning offset',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTime.DAYS: 'd'>,
}),
'context': <ANY>,
'entity_id': 'sensor.airgradient_voc_index_learning_offset',
@@ -39,7 +39,7 @@
# name: test_all_entities[switch.airgradient_post_data_to_airgradient-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Airgradient Post data to Airgradient',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Post data to Airgradient',
}),
'context': <ANY>,
'entity_id': 'switch.airgradient_post_data_to_airgradient',
@@ -40,17 +40,17 @@
StateSnapshot({
'attributes': ReadOnlyDict({
<UpdateEntityStateAttribute.AUTO_UPDATE: 'auto_update'>: False,
'device_class': 'firmware',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'firmware',
<UpdateEntityStateAttribute.DISPLAY_PRECISION: 'display_precision'>: 0,
'entity_picture': '/api/brands/integration/airgradient/icon.png',
'friendly_name': 'Airgradient Firmware',
<EntityStateAttribute.ENTITY_PICTURE: 'entity_picture'>: '/api/brands/integration/airgradient/icon.png',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airgradient Firmware',
<UpdateEntityStateAttribute.IN_PROGRESS: 'in_progress'>: False,
<UpdateEntityStateAttribute.INSTALLED_VERSION: 'installed_version'>: '3.1.1',
<UpdateEntityStateAttribute.LATEST_VERSION: 'latest_version'>: '3.1.4',
<UpdateEntityStateAttribute.RELEASE_SUMMARY: 'release_summary'>: None,
<UpdateEntityStateAttribute.RELEASE_URL: 'release_url'>: None,
<UpdateEntityStateAttribute.SKIPPED_VERSION: 'skipped_version'>: None,
'supported_features': <UpdateEntityFeature: 0>,
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <UpdateEntityFeature: 0>,
<UpdateEntityStateAttribute.TITLE: 'title'>: None,
<UpdateEntityStateAttribute.UPDATE_PERCENTAGE: 'update_percentage'>: None,
}),
@@ -44,13 +44,13 @@
# name: test_sensor[sensor.home_carbon_monoxide-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by Airly',
'device_class': 'carbon_monoxide',
'friendly_name': 'Home Carbon monoxide',
<EntityStateAttribute.ATTRIBUTION: 'attribution'>: 'Data provided by Airly',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'carbon_monoxide',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Home Carbon monoxide',
'limit': 4000,
'percent': 4,
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.home_carbon_monoxide',
@@ -104,11 +104,11 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'advice': 'Catch your breath!',
'attribution': 'Data provided by Airly',
<EntityStateAttribute.ATTRIBUTION: 'attribution'>: 'Data provided by Airly',
'description': 'Great air here today!',
'friendly_name': 'Home Common air quality index',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Home Common air quality index',
'level': 'very low',
'unit_of_measurement': 'CAQI',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'CAQI',
}),
'context': <ANY>,
'entity_id': 'sensor.home_common_air_quality_index',
@@ -163,11 +163,11 @@
# name: test_sensor[sensor.home_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by Airly',
'device_class': 'humidity',
'friendly_name': 'Home Humidity',
<EntityStateAttribute.ATTRIBUTION: 'attribution'>: 'Data provided by Airly',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'humidity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Home Humidity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.home_humidity',
@@ -222,13 +222,13 @@
# name: test_sensor[sensor.home_nitrogen_dioxide-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by Airly',
'device_class': 'nitrogen_dioxide',
'friendly_name': 'Home Nitrogen dioxide',
<EntityStateAttribute.ATTRIBUTION: 'attribution'>: 'Data provided by Airly',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'nitrogen_dioxide',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Home Nitrogen dioxide',
'limit': 25,
'percent': 64,
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.home_nitrogen_dioxide',
@@ -283,13 +283,13 @@
# name: test_sensor[sensor.home_ozone-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by Airly',
'device_class': 'ozone',
'friendly_name': 'Home Ozone',
<EntityStateAttribute.ATTRIBUTION: 'attribution'>: 'Data provided by Airly',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'ozone',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Home Ozone',
'limit': 100,
'percent': 42,
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.home_ozone',
@@ -344,11 +344,11 @@
# name: test_sensor[sensor.home_pm1-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by Airly',
'device_class': 'pm1',
'friendly_name': 'Home PM1',
<EntityStateAttribute.ATTRIBUTION: 'attribution'>: 'Data provided by Airly',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'pm1',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Home PM1',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.home_pm1',
@@ -403,13 +403,13 @@
# name: test_sensor[sensor.home_pm10-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by Airly',
'device_class': 'pm10',
'friendly_name': 'Home PM10',
<EntityStateAttribute.ATTRIBUTION: 'attribution'>: 'Data provided by Airly',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'pm10',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Home PM10',
'limit': 45,
'percent': 14,
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.home_pm10',
@@ -464,13 +464,13 @@
# name: test_sensor[sensor.home_pm2_5-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by Airly',
'device_class': 'pm25',
'friendly_name': 'Home PM2.5',
<EntityStateAttribute.ATTRIBUTION: 'attribution'>: 'Data provided by Airly',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'pm25',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Home PM2.5',
'limit': 15,
'percent': 29,
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.home_pm2_5',
@@ -525,11 +525,11 @@
# name: test_sensor[sensor.home_pressure-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by Airly',
'device_class': 'pressure',
'friendly_name': 'Home Pressure',
<EntityStateAttribute.ATTRIBUTION: 'attribution'>: 'Data provided by Airly',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'pressure',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Home Pressure',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPressure.HPA: 'hPa'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfPressure.HPA: 'hPa'>,
}),
'context': <ANY>,
'entity_id': 'sensor.home_pressure',
@@ -584,13 +584,13 @@
# name: test_sensor[sensor.home_sulphur_dioxide-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by Airly',
'device_class': 'sulphur_dioxide',
'friendly_name': 'Home Sulphur dioxide',
<EntityStateAttribute.ATTRIBUTION: 'attribution'>: 'Data provided by Airly',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'sulphur_dioxide',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Home Sulphur dioxide',
'limit': 40,
'percent': 35,
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.home_sulphur_dioxide',
@@ -645,11 +645,11 @@
# name: test_sensor[sensor.home_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'attribution': 'Data provided by Airly',
'device_class': 'temperature',
'friendly_name': 'Home Temperature',
<EntityStateAttribute.ATTRIBUTION: 'attribution'>: 'Data provided by Airly',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Home Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.home_temperature',
@@ -39,7 +39,7 @@
# name: test_buttons[button.test_thermostat_recalibrate_co2_sensor-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Test Thermostat Recalibrate CO2 sensor',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test Thermostat Recalibrate CO2 sensor',
}),
'context': <ANY>,
'entity_id': 'button.test_thermostat_recalibrate_co2_sensor',
@@ -89,8 +89,8 @@
# name: test_buttons[button.test_thermostat_restart-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'restart',
'friendly_name': 'Test Thermostat Restart',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'restart',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test Thermostat Restart',
}),
'context': <ANY>,
'entity_id': 'button.test_thermostat_restart',
@@ -52,7 +52,7 @@
'attributes': ReadOnlyDict({
<ClimateEntityStateAttribute.CURRENT_HUMIDITY: 'current_humidity'>: 45.0,
<ClimateEntityStateAttribute.CURRENT_TEMPERATURE: 'current_temperature'>: 22.0,
'friendly_name': 'Test Thermostat',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test Thermostat',
<ClimateEntityStateAttribute.HVAC_ACTION: 'hvac_action'>: <HVACAction.IDLE: 'idle'>,
<ClimateEntityCapabilityAttribute.HVAC_MODES: 'hvac_modes'>: list([
<HVACMode.HEAT: 'heat'>,
@@ -65,7 +65,7 @@
'away',
'boost',
]),
'supported_features': <ClimateEntityFeature: 17>,
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <ClimateEntityFeature: 17>,
<ClimateEntityStateAttribute.TEMPERATURE: 'temperature'>: 22.0,
}),
'context': <ANY>,
@@ -44,13 +44,13 @@
# name: test_number_entities[number.test_thermostat_hysteresis_band-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Test Thermostat Hysteresis band',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test Thermostat Hysteresis band',
<NumberEntityCapabilityAttribute.MAX: 'max'>: 0.5,
<NumberEntityCapabilityAttribute.MIN: 'min'>: 0.0,
<NumberEntityCapabilityAttribute.MODE: 'mode'>: <NumberMode.AUTO: 'auto'>,
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.1,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'number.test_thermostat_hysteresis_band',
@@ -44,10 +44,10 @@
# name: test_sensors[sensor.test_thermostat_air_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Test Thermostat Air temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test Thermostat Air temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.test_thermostat_air_temperature',
@@ -97,8 +97,8 @@
# name: test_sensors[sensor.test_thermostat_device_uptime-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'timestamp',
'friendly_name': 'Test Thermostat Device uptime',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'timestamp',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test Thermostat Device uptime',
}),
'context': <ANY>,
'entity_id': 'sensor.test_thermostat_device_uptime',
@@ -150,7 +150,7 @@
# name: test_sensors[sensor.test_thermostat_error_count-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Test Thermostat Error count',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test Thermostat Error count',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'context': <ANY>,
@@ -209,10 +209,10 @@
# name: test_sensors[sensor.test_thermostat_heating_uptime-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Test Thermostat Heating uptime',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'duration',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test Thermostat Heating uptime',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTime.HOURS: 'h'>,
}),
'context': <ANY>,
'entity_id': 'sensor.test_thermostat_heating_uptime',
@@ -264,10 +264,10 @@
# name: test_sensors[sensor.test_thermostat_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'humidity',
'friendly_name': 'Test Thermostat Humidity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'humidity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test Thermostat Humidity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.test_thermostat_humidity',
@@ -39,7 +39,7 @@
# name: test_switches[switch.test_thermostat_actuator_exercise_disabled-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Test Thermostat Actuator exercise disabled',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test Thermostat Actuator exercise disabled',
}),
'context': <ANY>,
'entity_id': 'switch.test_thermostat_actuator_exercise_disabled',
@@ -89,7 +89,7 @@
# name: test_switches[switch.test_thermostat_child_lock-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Test Thermostat Child lock',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test Thermostat Child lock',
}),
'context': <ANY>,
'entity_id': 'switch.test_thermostat_child_lock',
@@ -39,8 +39,8 @@
# name: test_all_entities[airos_NanoStation_M5_sta_v6.3.16.json][binary_sensor.nanostation_m5_dhcp_client-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'running',
'friendly_name': 'NanoStation M5 DHCP client',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'running',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation M5 DHCP client',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.nanostation_m5_dhcp_client',
@@ -90,8 +90,8 @@
# name: test_all_entities[airos_NanoStation_M5_sta_v6.3.16.json][binary_sensor.nanostation_m5_dhcp_server-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'running',
'friendly_name': 'NanoStation M5 DHCP server',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'running',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation M5 DHCP server',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.nanostation_m5_dhcp_server',
@@ -141,8 +141,8 @@
# name: test_all_entities[airos_NanoStation_M5_sta_v6.3.16.json][binary_sensor.nanostation_m5_pppoe_link-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'connectivity',
'friendly_name': 'NanoStation M5 PPPoE link',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'connectivity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation M5 PPPoE link',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.nanostation_m5_pppoe_link',
@@ -192,8 +192,8 @@
# name: test_all_entities[airos_NanoStation_loco_M5_v6.3.16_XM_sta.json][binary_sensor.nanostation_loco_m5_client_dhcp_client-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'running',
'friendly_name': 'NanoStation loco M5 Client DHCP client',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'running',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation loco M5 Client DHCP client',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.nanostation_loco_m5_client_dhcp_client',
@@ -243,8 +243,8 @@
# name: test_all_entities[airos_NanoStation_loco_M5_v6.3.16_XM_sta.json][binary_sensor.nanostation_loco_m5_client_dhcp_server-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'running',
'friendly_name': 'NanoStation loco M5 Client DHCP server',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'running',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation loco M5 Client DHCP server',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.nanostation_loco_m5_client_dhcp_server',
@@ -294,8 +294,8 @@
# name: test_all_entities[airos_NanoStation_loco_M5_v6.3.16_XM_sta.json][binary_sensor.nanostation_loco_m5_client_pppoe_link-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'connectivity',
'friendly_name': 'NanoStation loco M5 Client PPPoE link',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'connectivity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation loco M5 Client PPPoE link',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.nanostation_loco_m5_client_pppoe_link',
@@ -345,8 +345,8 @@
# name: test_all_entities[airos_liteapgps_ap_ptmp_40mhz.json][binary_sensor.house_bridge_dhcp_client-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'running',
'friendly_name': 'House-Bridge DHCP client',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'running',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'House-Bridge DHCP client',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.house_bridge_dhcp_client',
@@ -396,8 +396,8 @@
# name: test_all_entities[airos_liteapgps_ap_ptmp_40mhz.json][binary_sensor.house_bridge_dhcp_server-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'running',
'friendly_name': 'House-Bridge DHCP server',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'running',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'House-Bridge DHCP server',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.house_bridge_dhcp_server',
@@ -447,8 +447,8 @@
# name: test_all_entities[airos_liteapgps_ap_ptmp_40mhz.json][binary_sensor.house_bridge_dhcpv6_server-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'running',
'friendly_name': 'House-Bridge DHCPv6 server',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'running',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'House-Bridge DHCPv6 server',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.house_bridge_dhcpv6_server',
@@ -498,7 +498,7 @@
# name: test_all_entities[airos_liteapgps_ap_ptmp_40mhz.json][binary_sensor.house_bridge_port_forwarding-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'House-Bridge Port forwarding',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'House-Bridge Port forwarding',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.house_bridge_port_forwarding',
@@ -548,8 +548,8 @@
# name: test_all_entities[airos_liteapgps_ap_ptmp_40mhz.json][binary_sensor.house_bridge_pppoe_link-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'connectivity',
'friendly_name': 'House-Bridge PPPoE link',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'connectivity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'House-Bridge PPPoE link',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.house_bridge_pppoe_link',
@@ -599,8 +599,8 @@
# name: test_all_entities[airos_loco5ac_ap-ptp.json][binary_sensor.nanostation_5ac_ap_name_dhcp_client-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'running',
'friendly_name': 'NanoStation 5AC ap name DHCP client',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'running',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name DHCP client',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.nanostation_5ac_ap_name_dhcp_client',
@@ -650,8 +650,8 @@
# name: test_all_entities[airos_loco5ac_ap-ptp.json][binary_sensor.nanostation_5ac_ap_name_dhcp_server-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'running',
'friendly_name': 'NanoStation 5AC ap name DHCP server',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'running',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name DHCP server',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.nanostation_5ac_ap_name_dhcp_server',
@@ -701,8 +701,8 @@
# name: test_all_entities[airos_loco5ac_ap-ptp.json][binary_sensor.nanostation_5ac_ap_name_dhcpv6_server-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'running',
'friendly_name': 'NanoStation 5AC ap name DHCPv6 server',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'running',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name DHCPv6 server',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.nanostation_5ac_ap_name_dhcpv6_server',
@@ -752,7 +752,7 @@
# name: test_all_entities[airos_loco5ac_ap-ptp.json][binary_sensor.nanostation_5ac_ap_name_port_forwarding-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'NanoStation 5AC ap name Port forwarding',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name Port forwarding',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.nanostation_5ac_ap_name_port_forwarding',
@@ -802,8 +802,8 @@
# name: test_all_entities[airos_loco5ac_ap-ptp.json][binary_sensor.nanostation_5ac_ap_name_pppoe_link-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'connectivity',
'friendly_name': 'NanoStation 5AC ap name PPPoE link',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'connectivity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name PPPoE link',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.nanostation_5ac_ap_name_pppoe_link',
@@ -41,10 +41,10 @@
# name: test_all_entities[sensor.nanostation_5ac_ap_name_antenna_gain-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'signal_strength',
'friendly_name': 'NanoStation 5AC ap name Antenna gain',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'signal_strength',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name Antenna gain',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'dB',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'dB',
}),
'context': <ANY>,
'entity_id': 'sensor.nanostation_5ac_ap_name_antenna_gain',
@@ -99,9 +99,9 @@
# name: test_all_entities[sensor.nanostation_5ac_ap_name_cpu_load-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'NanoStation 5AC ap name CPU load',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name CPU load',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.nanostation_5ac_ap_name_cpu_load',
@@ -159,10 +159,10 @@
# name: test_all_entities[sensor.nanostation_5ac_ap_name_download_capacity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'data_rate',
'friendly_name': 'NanoStation 5AC ap name Download capacity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'data_rate',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name Download capacity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfDataRate.MEGABITS_PER_SECOND: 'Mbit/s'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfDataRate.MEGABITS_PER_SECOND: 'Mbit/s'>,
}),
'context': <ANY>,
'entity_id': 'sensor.nanostation_5ac_ap_name_download_capacity',
@@ -217,8 +217,8 @@
# name: test_all_entities[sensor.nanostation_5ac_ap_name_network_role-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'NanoStation 5AC ap name Network role',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'enum',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name Network role',
<SensorEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'bridge',
'router',
@@ -280,10 +280,10 @@
# name: test_all_entities[sensor.nanostation_5ac_ap_name_throughput_receive_actual-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'data_rate',
'friendly_name': 'NanoStation 5AC ap name Throughput receive (actual)',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'data_rate',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name Throughput receive (actual)',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfDataRate.MEGABITS_PER_SECOND: 'Mbit/s'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfDataRate.MEGABITS_PER_SECOND: 'Mbit/s'>,
}),
'context': <ANY>,
'entity_id': 'sensor.nanostation_5ac_ap_name_throughput_receive_actual',
@@ -341,10 +341,10 @@
# name: test_all_entities[sensor.nanostation_5ac_ap_name_throughput_transmit_actual-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'data_rate',
'friendly_name': 'NanoStation 5AC ap name Throughput transmit (actual)',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'data_rate',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name Throughput transmit (actual)',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfDataRate.MEGABITS_PER_SECOND: 'Mbit/s'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfDataRate.MEGABITS_PER_SECOND: 'Mbit/s'>,
}),
'context': <ANY>,
'entity_id': 'sensor.nanostation_5ac_ap_name_throughput_transmit_actual',
@@ -402,10 +402,10 @@
# name: test_all_entities[sensor.nanostation_5ac_ap_name_upload_capacity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'data_rate',
'friendly_name': 'NanoStation 5AC ap name Upload capacity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'data_rate',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name Upload capacity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfDataRate.MEGABITS_PER_SECOND: 'Mbit/s'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfDataRate.MEGABITS_PER_SECOND: 'Mbit/s'>,
}),
'context': <ANY>,
'entity_id': 'sensor.nanostation_5ac_ap_name_upload_capacity',
@@ -461,9 +461,9 @@
# name: test_all_entities[sensor.nanostation_5ac_ap_name_uptime-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'NanoStation 5AC ap name Uptime',
'unit_of_measurement': <UnitOfTime.DAYS: 'd'>,
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'duration',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name Uptime',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTime.DAYS: 'd'>,
}),
'context': <ANY>,
'entity_id': 'sensor.nanostation_5ac_ap_name_uptime',
@@ -519,9 +519,9 @@
# name: test_all_entities[sensor.nanostation_5ac_ap_name_wireless_distance-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'distance',
'friendly_name': 'NanoStation 5AC ap name Wireless distance',
'unit_of_measurement': <UnitOfLength.KILOMETERS: 'km'>,
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'distance',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name Wireless distance',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfLength.KILOMETERS: 'km'>,
}),
'context': <ANY>,
'entity_id': 'sensor.nanostation_5ac_ap_name_wireless_distance',
@@ -576,10 +576,10 @@
# name: test_all_entities[sensor.nanostation_5ac_ap_name_wireless_frequency-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'frequency',
'friendly_name': 'NanoStation 5AC ap name Wireless frequency',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'frequency',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name Wireless frequency',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfFrequency.MEGAHERTZ: 'MHz'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfFrequency.MEGAHERTZ: 'MHz'>,
}),
'context': <ANY>,
'entity_id': 'sensor.nanostation_5ac_ap_name_wireless_frequency',
@@ -634,8 +634,8 @@
# name: test_all_entities[sensor.nanostation_5ac_ap_name_wireless_mode-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'NanoStation 5AC ap name Wireless mode',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'enum',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name Wireless mode',
<SensorEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'point_to_point',
'point_to_multipoint',
@@ -694,8 +694,8 @@
# name: test_all_entities[sensor.nanostation_5ac_ap_name_wireless_role-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'NanoStation 5AC ap name Wireless role',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'enum',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name Wireless role',
<SensorEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'station',
'access_point',
@@ -749,7 +749,7 @@
# name: test_all_entities[sensor.nanostation_5ac_ap_name_wireless_ssid-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'NanoStation 5AC ap name Wireless SSID',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'NanoStation 5AC ap name Wireless SSID',
}),
'context': <ANY>,
'entity_id': 'sensor.nanostation_5ac_ap_name_wireless_ssid',
@@ -61,7 +61,7 @@
'high',
'auto',
]),
'friendly_name': 'living room',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'living room',
<ClimateEntityCapabilityAttribute.HVAC_MODES: 'hvac_modes'>: list([
<HVACMode.HEAT: 'heat'>,
<HVACMode.COOL: 'cool'>,
@@ -69,7 +69,7 @@
]),
<ClimateEntityCapabilityAttribute.MAX_TEMP: 'max_temp'>: 30.0,
<ClimateEntityCapabilityAttribute.MIN_TEMP: 'min_temp'>: 16.0,
'supported_features': <ClimateEntityFeature: 425>,
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <ClimateEntityFeature: 425>,
<ClimateEntityCapabilityAttribute.SWING_MODES: 'swing_modes'>: list([
'on',
'off',
@@ -148,7 +148,7 @@
'high',
'auto',
]),
'friendly_name': 'living room',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'living room',
<ClimateEntityCapabilityAttribute.HVAC_MODES: 'hvac_modes'>: list([
<HVACMode.HEAT: 'heat'>,
<HVACMode.COOL: 'cool'>,
@@ -156,7 +156,7 @@
]),
<ClimateEntityCapabilityAttribute.MAX_TEMP: 'max_temp'>: 30.0,
<ClimateEntityCapabilityAttribute.MIN_TEMP: 'min_temp'>: 16.0,
'supported_features': <ClimateEntityFeature: 425>,
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <ClimateEntityFeature: 425>,
<ClimateEntityStateAttribute.SWING_MODE: 'swing_mode'>: 'off',
<ClimateEntityCapabilityAttribute.SWING_MODES: 'swing_modes'>: list([
'on',
@@ -41,10 +41,10 @@
# name: test_sensor_with_climate_data[sensor.living_room_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'humidity',
'friendly_name': 'living room Humidity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'humidity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'living room Humidity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.living_room_humidity',
@@ -99,10 +99,10 @@
# name: test_sensor_with_climate_data[sensor.living_room_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'living room Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'living room Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.living_room_temperature',
@@ -47,10 +47,10 @@
# name: test_all_device_types[view_plus][sensor.living_room_atmospheric_pressure-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'atmospheric_pressure',
'friendly_name': 'Living Room Atmospheric pressure',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'atmospheric_pressure',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Living Room Atmospheric pressure',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPressure.HPA: 'hPa'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfPressure.HPA: 'hPa'>,
}),
'context': <ANY>,
'entity_id': 'sensor.living_room_atmospheric_pressure',
@@ -105,10 +105,10 @@
# name: test_all_device_types[view_plus][sensor.living_room_battery-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'Living Room Battery',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'battery',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Living Room Battery',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.living_room_battery',
@@ -163,10 +163,10 @@
# name: test_all_device_types[view_plus][sensor.living_room_carbon_dioxide-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'carbon_dioxide',
'friendly_name': 'Living Room Carbon dioxide',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'carbon_dioxide',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Living Room Carbon dioxide',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'ppm',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'ppm',
}),
'context': <ANY>,
'entity_id': 'sensor.living_room_carbon_dioxide',
@@ -221,10 +221,10 @@
# name: test_all_device_types[view_plus][sensor.living_room_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'humidity',
'friendly_name': 'Living Room Humidity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'humidity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Living Room Humidity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.living_room_humidity',
@@ -279,10 +279,10 @@
# name: test_all_device_types[view_plus][sensor.living_room_pm1-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'pm1',
'friendly_name': 'Living Room PM1',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'pm1',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Living Room PM1',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.living_room_pm1',
@@ -337,10 +337,10 @@
# name: test_all_device_types[view_plus][sensor.living_room_pm2_5-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'pm25',
'friendly_name': 'Living Room PM2.5',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'pm25',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Living Room PM2.5',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.living_room_pm2_5',
@@ -395,9 +395,9 @@
# name: test_all_device_types[view_plus][sensor.living_room_radon-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Living Room Radon',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Living Room Radon',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'Bq/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'Bq/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.living_room_radon',
@@ -452,10 +452,10 @@
# name: test_all_device_types[view_plus][sensor.living_room_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Living Room Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Living Room Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.living_room_temperature',
@@ -510,10 +510,10 @@
# name: test_all_device_types[view_plus][sensor.living_room_volatile_organic_compounds_parts-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'volatile_organic_compounds_parts',
'friendly_name': 'Living Room Volatile organic compounds parts',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'volatile_organic_compounds_parts',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Living Room Volatile organic compounds parts',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'ppb',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'ppb',
}),
'context': <ANY>,
'entity_id': 'sensor.living_room_volatile_organic_compounds_parts',
@@ -571,10 +571,10 @@
# name: test_all_device_types[wave_enhance][sensor.bedroom_atmospheric_pressure-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'atmospheric_pressure',
'friendly_name': 'Bedroom Atmospheric pressure',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'atmospheric_pressure',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Bedroom Atmospheric pressure',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPressure.HPA: 'hPa'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfPressure.HPA: 'hPa'>,
}),
'context': <ANY>,
'entity_id': 'sensor.bedroom_atmospheric_pressure',
@@ -629,10 +629,10 @@
# name: test_all_device_types[wave_enhance][sensor.bedroom_battery-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'Bedroom Battery',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'battery',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Bedroom Battery',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.bedroom_battery',
@@ -687,10 +687,10 @@
# name: test_all_device_types[wave_enhance][sensor.bedroom_carbon_dioxide-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'carbon_dioxide',
'friendly_name': 'Bedroom Carbon dioxide',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'carbon_dioxide',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Bedroom Carbon dioxide',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'ppm',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'ppm',
}),
'context': <ANY>,
'entity_id': 'sensor.bedroom_carbon_dioxide',
@@ -745,10 +745,10 @@
# name: test_all_device_types[wave_enhance][sensor.bedroom_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'humidity',
'friendly_name': 'Bedroom Humidity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'humidity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Bedroom Humidity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.bedroom_humidity',
@@ -803,10 +803,10 @@
# name: test_all_device_types[wave_enhance][sensor.bedroom_illuminance-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'illuminance',
'friendly_name': 'Bedroom Illuminance',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'illuminance',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Bedroom Illuminance',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'lx',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'lx',
}),
'context': <ANY>,
'entity_id': 'sensor.bedroom_illuminance',
@@ -861,10 +861,10 @@
# name: test_all_device_types[wave_enhance][sensor.bedroom_sound_pressure-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'sound_pressure',
'friendly_name': 'Bedroom Sound pressure',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'sound_pressure',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Bedroom Sound pressure',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfSoundPressure.WEIGHTED_DECIBEL_A: 'dBA'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfSoundPressure.WEIGHTED_DECIBEL_A: 'dBA'>,
}),
'context': <ANY>,
'entity_id': 'sensor.bedroom_sound_pressure',
@@ -919,10 +919,10 @@
# name: test_all_device_types[wave_enhance][sensor.bedroom_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Bedroom Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Bedroom Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.bedroom_temperature',
@@ -977,10 +977,10 @@
# name: test_all_device_types[wave_enhance][sensor.bedroom_volatile_organic_compounds_parts-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'volatile_organic_compounds_parts',
'friendly_name': 'Bedroom Volatile organic compounds parts',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'volatile_organic_compounds_parts',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Bedroom Volatile organic compounds parts',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'ppb',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'ppb',
}),
'context': <ANY>,
'entity_id': 'sensor.bedroom_volatile_organic_compounds_parts',
@@ -1038,10 +1038,10 @@
# name: test_all_device_types[wave_plus][sensor.office_atmospheric_pressure-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'atmospheric_pressure',
'friendly_name': 'Office Atmospheric pressure',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'atmospheric_pressure',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Office Atmospheric pressure',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPressure.HPA: 'hPa'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfPressure.HPA: 'hPa'>,
}),
'context': <ANY>,
'entity_id': 'sensor.office_atmospheric_pressure',
@@ -1096,10 +1096,10 @@
# name: test_all_device_types[wave_plus][sensor.office_battery-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'Office Battery',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'battery',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Office Battery',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.office_battery',
@@ -1154,10 +1154,10 @@
# name: test_all_device_types[wave_plus][sensor.office_carbon_dioxide-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'carbon_dioxide',
'friendly_name': 'Office Carbon dioxide',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'carbon_dioxide',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Office Carbon dioxide',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'ppm',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'ppm',
}),
'context': <ANY>,
'entity_id': 'sensor.office_carbon_dioxide',
@@ -1212,10 +1212,10 @@
# name: test_all_device_types[wave_plus][sensor.office_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'humidity',
'friendly_name': 'Office Humidity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'humidity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Office Humidity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.office_humidity',
@@ -1270,9 +1270,9 @@
# name: test_all_device_types[wave_plus][sensor.office_radon-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Office Radon',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Office Radon',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'Bq/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'Bq/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.office_radon',
@@ -1327,10 +1327,10 @@
# name: test_all_device_types[wave_plus][sensor.office_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Office Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Office Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.office_temperature',
@@ -1385,10 +1385,10 @@
# name: test_all_device_types[wave_plus][sensor.office_volatile_organic_compounds_parts-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'volatile_organic_compounds_parts',
'friendly_name': 'Office Volatile organic compounds parts',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'volatile_organic_compounds_parts',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Office Volatile organic compounds parts',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'ppb',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'ppb',
}),
'context': <ANY>,
'entity_id': 'sensor.office_volatile_organic_compounds_parts',
@@ -40,10 +40,10 @@
StateSnapshot({
'attributes': ReadOnlyDict({
<CoverEntityStateAttribute.CURRENT_POSITION: 'current_position'>: 90,
'device_class': 'damper',
'friendly_name': 'Zone 1 Damper',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'damper',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Zone 1 Damper',
<CoverEntityStateAttribute.IS_CLOSED: 'is_closed'>: False,
'supported_features': <CoverEntityFeature: 7>,
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <CoverEntityFeature: 7>,
}),
'context': <ANY>,
'entity_id': 'cover.zone_1_damper',
@@ -94,10 +94,10 @@
StateSnapshot({
'attributes': ReadOnlyDict({
<CoverEntityStateAttribute.CURRENT_POSITION: 'current_position'>: 100,
'device_class': 'damper',
'friendly_name': 'Zone 2 Damper',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'damper',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Zone 2 Damper',
<CoverEntityStateAttribute.IS_CLOSED: 'is_closed'>: False,
'supported_features': <CoverEntityFeature: 7>,
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <CoverEntityFeature: 7>,
}),
'context': <ANY>,
'entity_id': 'cover.zone_2_damper',
@@ -41,10 +41,10 @@
# name: test_airzone_create_sensors[sensor.airzone_2_1_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'humidity',
'friendly_name': 'Airzone 2:1 Humidity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'humidity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airzone 2:1 Humidity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.airzone_2_1_humidity',
@@ -99,10 +99,10 @@
# name: test_airzone_create_sensors[sensor.airzone_2_1_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Airzone 2:1 Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airzone 2:1 Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.airzone_2_1_temperature',
@@ -157,10 +157,10 @@
# name: test_airzone_create_sensors[sensor.airzone_dhw_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Airzone DHW Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airzone DHW Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.airzone_dhw_temperature',
@@ -212,10 +212,10 @@
# name: test_airzone_create_sensors[sensor.airzone_webserver_rssi-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'signal_strength',
'friendly_name': 'Airzone WebServer RSSI',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'signal_strength',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Airzone WebServer RSSI',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'dBm',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'dBm',
}),
'context': <ANY>,
'entity_id': 'sensor.airzone_webserver_rssi',
@@ -270,10 +270,10 @@
# name: test_airzone_create_sensors[sensor.aux_heat_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Aux Heat Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Aux Heat Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.aux_heat_temperature',
@@ -325,10 +325,10 @@
# name: test_airzone_create_sensors[sensor.despacho_battery-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'Despacho Battery',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'battery',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Despacho Battery',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.despacho_battery',
@@ -380,10 +380,10 @@
# name: test_airzone_create_sensors[sensor.despacho_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'humidity',
'friendly_name': 'Despacho Humidity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'humidity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Despacho Humidity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.despacho_humidity',
@@ -435,9 +435,9 @@
# name: test_airzone_create_sensors[sensor.despacho_signal_strength-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Despacho Signal strength',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Despacho Signal strength',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.despacho_signal_strength',
@@ -492,10 +492,10 @@
# name: test_airzone_create_sensors[sensor.despacho_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Despacho Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Despacho Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.despacho_temperature',
@@ -550,10 +550,10 @@
# name: test_airzone_create_sensors[sensor.dkn_plus_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'DKN Plus Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'DKN Plus Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.dkn_plus_temperature',
@@ -605,10 +605,10 @@
# name: test_airzone_create_sensors[sensor.dorm_1_battery-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'Dorm #1 Battery',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'battery',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Dorm #1 Battery',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dorm_1_battery',
@@ -660,10 +660,10 @@
# name: test_airzone_create_sensors[sensor.dorm_1_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'humidity',
'friendly_name': 'Dorm #1 Humidity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'humidity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Dorm #1 Humidity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dorm_1_humidity',
@@ -715,9 +715,9 @@
# name: test_airzone_create_sensors[sensor.dorm_1_signal_strength-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dorm #1 Signal strength',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Dorm #1 Signal strength',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dorm_1_signal_strength',
@@ -772,10 +772,10 @@
# name: test_airzone_create_sensors[sensor.dorm_1_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Dorm #1 Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Dorm #1 Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.dorm_1_temperature',
@@ -827,10 +827,10 @@
# name: test_airzone_create_sensors[sensor.dorm_2_battery-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'Dorm #2 Battery',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'battery',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Dorm #2 Battery',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dorm_2_battery',
@@ -882,10 +882,10 @@
# name: test_airzone_create_sensors[sensor.dorm_2_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'humidity',
'friendly_name': 'Dorm #2 Humidity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'humidity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Dorm #2 Humidity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dorm_2_humidity',
@@ -937,9 +937,9 @@
# name: test_airzone_create_sensors[sensor.dorm_2_signal_strength-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dorm #2 Signal strength',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Dorm #2 Signal strength',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dorm_2_signal_strength',
@@ -994,10 +994,10 @@
# name: test_airzone_create_sensors[sensor.dorm_2_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Dorm #2 Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Dorm #2 Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.dorm_2_temperature',
@@ -1049,10 +1049,10 @@
# name: test_airzone_create_sensors[sensor.dorm_ppal_battery-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'Dorm Ppal Battery',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'battery',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Dorm Ppal Battery',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dorm_ppal_battery',
@@ -1104,10 +1104,10 @@
# name: test_airzone_create_sensors[sensor.dorm_ppal_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'humidity',
'friendly_name': 'Dorm Ppal Humidity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'humidity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Dorm Ppal Humidity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dorm_ppal_humidity',
@@ -1159,9 +1159,9 @@
# name: test_airzone_create_sensors[sensor.dorm_ppal_signal_strength-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dorm Ppal Signal strength',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Dorm Ppal Signal strength',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.dorm_ppal_signal_strength',
@@ -1216,10 +1216,10 @@
# name: test_airzone_create_sensors[sensor.dorm_ppal_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Dorm Ppal Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Dorm Ppal Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.dorm_ppal_temperature',
@@ -1271,10 +1271,10 @@
# name: test_airzone_create_sensors[sensor.salon_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'humidity',
'friendly_name': 'Salon Humidity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'humidity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Salon Humidity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.salon_humidity',
@@ -1329,10 +1329,10 @@
# name: test_airzone_create_sensors[sensor.salon_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Salon Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Salon Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.salon_temperature',
@@ -39,10 +39,10 @@
# name: test_cover_entities[cover.test_door-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'garage',
'friendly_name': 'Test Door',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'garage',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test Door',
<CoverEntityStateAttribute.IS_CLOSED: 'is_closed'>: True,
'supported_features': <CoverEntityFeature: 3>,
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <CoverEntityFeature: 3>,
}),
'context': <ANY>,
'entity_id': 'cover.test_door',
@@ -41,10 +41,10 @@
# name: test_sensor_entities[sensor.test_door_battery-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'Test Door Battery',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'battery',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Test Door Battery',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.test_door_battery',
@@ -39,8 +39,8 @@
# name: test_all_entities[binary_sensor.echo_test_connectivity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'connectivity',
'friendly_name': 'Echo Test Connectivity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'connectivity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Echo Test Connectivity',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.echo_test_connectivity',
@@ -39,7 +39,7 @@
# name: test_all_entities[button.fake_email_gmail_com_test_routine-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'fake_email@gmail.com Test Routine',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'fake_email@gmail.com Test Routine',
}),
'context': <ANY>,
'entity_id': 'button.fake_email_gmail_com_test_routine',
@@ -47,7 +47,7 @@
<EventEntityCapabilityAttribute.EVENT_TYPES: 'event_types'>: list([
'triggered',
]),
'friendly_name': 'Echo Test Voice event',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Echo Test Voice event',
}),
'context': <ANY>,
'entity_id': 'event.echo_test_voice_event',
@@ -63,7 +63,7 @@
<EventEntityCapabilityAttribute.EVENT_TYPES: 'event_types'>: list([
'triggered',
]),
'friendly_name': 'Echo Test Voice event',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Echo Test Voice event',
'intent': 'PlayMusicIntent',
'voice_command': 'Play some music',
'voice_reply': 'Echo Test',
@@ -40,9 +40,9 @@
# name: test_all_entities[media_player.echo_test-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'speaker',
'friendly_name': 'Echo Test',
'supported_features': <MediaPlayerEntityFeature: 5644>,
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'speaker',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Echo Test',
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <MediaPlayerEntityFeature: 5644>,
}),
'context': <ANY>,
'entity_id': 'media_player.echo_test',
@@ -39,8 +39,8 @@
# name: test_all_entities[notify.echo_test_announce-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Echo Test Announce',
'supported_features': <NotifyEntityFeature: 0>,
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Echo Test Announce',
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <NotifyEntityFeature: 0>,
}),
'context': <ANY>,
'entity_id': 'notify.echo_test_announce',
@@ -90,8 +90,8 @@
# name: test_all_entities[notify.echo_test_speak-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Echo Test Speak',
'supported_features': <NotifyEntityFeature: 0>,
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Echo Test Speak',
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <NotifyEntityFeature: 0>,
}),
'context': <ANY>,
'entity_id': 'notify.echo_test_speak',
@@ -45,7 +45,7 @@
# name: test_all_entities[select.echo_test_drop_in-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Echo Test Drop In',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Echo Test Drop In',
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'all',
'home',
@@ -39,8 +39,8 @@
# name: test_all_entities[sensor.echo_test_next_alarm-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'timestamp',
'friendly_name': 'Echo Test Next alarm',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'timestamp',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Echo Test Next alarm',
}),
'context': <ANY>,
'entity_id': 'sensor.echo_test_next_alarm',
@@ -90,8 +90,8 @@
# name: test_all_entities[sensor.echo_test_next_reminder-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'timestamp',
'friendly_name': 'Echo Test Next reminder',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'timestamp',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Echo Test Next reminder',
}),
'context': <ANY>,
'entity_id': 'sensor.echo_test_next_reminder',
@@ -141,8 +141,8 @@
# name: test_all_entities[sensor.echo_test_next_timer-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'timestamp',
'friendly_name': 'Echo Test Next timer',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'timestamp',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Echo Test Next timer',
}),
'context': <ANY>,
'entity_id': 'sensor.echo_test_next_timer',
@@ -197,10 +197,10 @@
# name: test_all_entities[sensor.echo_test_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Echo Test Temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Echo Test Temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.echo_test_temperature',
@@ -39,7 +39,7 @@
# name: test_all_entities[switch.echo_test_announcements-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Echo Test Announcements',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Echo Test Announcements',
}),
'context': <ANY>,
'entity_id': 'switch.echo_test_announcements',
@@ -89,7 +89,7 @@
# name: test_all_entities[switch.echo_test_communications-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Echo Test Communications',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Echo Test Communications',
}),
'context': <ANY>,
'entity_id': 'switch.echo_test_communications',
@@ -139,7 +139,7 @@
# name: test_all_entities[switch.echo_test_do_not_disturb-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Echo Test Do not disturb',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Echo Test Do not disturb',
}),
'context': <ANY>,
'entity_id': 'switch.echo_test_do_not_disturb',
@@ -39,8 +39,8 @@
# name: test_all_entities[todo.fake_email_gmail_com_concerts-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'fake_email@gmail.com Concerts',
'supported_features': <TodoListEntityFeature: 7>,
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'fake_email@gmail.com Concerts',
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <TodoListEntityFeature: 7>,
}),
'context': <ANY>,
'entity_id': 'todo.fake_email_gmail_com_concerts',
@@ -90,8 +90,8 @@
# name: test_all_entities[todo.fake_email_gmail_com_shopping_list-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'fake_email@gmail.com Shopping list',
'supported_features': <TodoListEntityFeature: 7>,
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'fake_email@gmail.com Shopping list',
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <TodoListEntityFeature: 7>,
}),
'context': <ANY>,
'entity_id': 'todo.fake_email_gmail_com_shopping_list',
@@ -141,8 +141,8 @@
# name: test_all_entities[todo.fake_email_gmail_com_to_do_list-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'fake_email@gmail.com To-do list',
'supported_features': <TodoListEntityFeature: 7>,
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'fake_email@gmail.com To-do list',
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <TodoListEntityFeature: 7>,
}),
'context': <ANY>,
'entity_id': 'todo.fake_email_gmail_com_to_do_list',
@@ -44,10 +44,10 @@
# name: test_all_entities[sensor.5366960e8b18_average_noise-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'sound_pressure',
'friendly_name': '5366960e8b18 Average noise',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'sound_pressure',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: '5366960e8b18 Average noise',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfSoundPressure.DECIBEL: 'dB'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfSoundPressure.DECIBEL: 'dB'>,
}),
'context': <ANY>,
'entity_id': 'sensor.5366960e8b18_average_noise',
@@ -102,10 +102,10 @@
# name: test_all_entities[sensor.5366960e8b18_bme280_humidity-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'humidity',
'friendly_name': '5366960e8b18 BME280 humidity',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'humidity',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: '5366960e8b18 BME280 humidity',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: '%',
}),
'context': <ANY>,
'entity_id': 'sensor.5366960e8b18_bme280_humidity',
@@ -163,10 +163,10 @@
# name: test_all_entities[sensor.5366960e8b18_bme280_pressure-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'pressure',
'friendly_name': '5366960e8b18 BME280 pressure',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'pressure',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: '5366960e8b18 BME280 pressure',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPressure.MMHG: 'mmHg'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfPressure.MMHG: 'mmHg'>,
}),
'context': <ANY>,
'entity_id': 'sensor.5366960e8b18_bme280_pressure',
@@ -221,10 +221,10 @@
# name: test_all_entities[sensor.5366960e8b18_bme280_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': '5366960e8b18 BME280 temperature',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: '5366960e8b18 BME280 temperature',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.5366960e8b18_bme280_temperature',
@@ -279,10 +279,10 @@
# name: test_all_entities[sensor.5366960e8b18_maximum_noise-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'sound_pressure',
'friendly_name': '5366960e8b18 Maximum noise',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'sound_pressure',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: '5366960e8b18 Maximum noise',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfSoundPressure.DECIBEL: 'dB'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfSoundPressure.DECIBEL: 'dB'>,
}),
'context': <ANY>,
'entity_id': 'sensor.5366960e8b18_maximum_noise',
@@ -337,10 +337,10 @@
# name: test_all_entities[sensor.5366960e8b18_pm10-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'pm10',
'friendly_name': '5366960e8b18 PM10',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'pm10',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: '5366960e8b18 PM10',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.5366960e8b18_pm10',
@@ -395,10 +395,10 @@
# name: test_all_entities[sensor.5366960e8b18_pm2_5-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'pm25',
'friendly_name': '5366960e8b18 PM2.5',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'pm25',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: '5366960e8b18 PM2.5',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μg/m³',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.5366960e8b18_pm2_5',
@@ -453,9 +453,9 @@
# name: test_all_entities[sensor.5366960e8b18_radiation_level-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': '5366960e8b18 Radiation level',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: '5366960e8b18 Radiation level',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μR/h',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'μR/h',
}),
'context': <ANY>,
'entity_id': 'sensor.5366960e8b18_radiation_level',
@@ -510,10 +510,10 @@
# name: test_all_entities[sensor.5366960e8b18_signal_strength-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'signal_strength',
'friendly_name': '5366960e8b18 Signal strength',
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'signal_strength',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: '5366960e8b18 Signal strength',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'dBm',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'dBm',
}),
'context': <ANY>,
'entity_id': 'sensor.5366960e8b18_signal_strength',

Some files were not shown because too many files have changed in this diff Show More