This commit is contained in:
Franck Nijhof 2025-03-07 18:54:39 +01:00 committed by GitHub
commit 4e89948b5c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
73 changed files with 5989 additions and 1259 deletions

View File

@ -11,6 +11,7 @@ from typing import Any
import evohomeasync as ec1
import evohomeasync2 as ec2
from evohomeasync2.const import (
SZ_DHW,
SZ_GATEWAY_ID,
SZ_GATEWAY_INFO,
SZ_GATEWAYS,
@ -19,8 +20,9 @@ from evohomeasync2.const import (
SZ_TEMPERATURE_CONTROL_SYSTEMS,
SZ_TIME_ZONE,
SZ_USE_DAYLIGHT_SAVE_SWITCHING,
SZ_ZONES,
)
from evohomeasync2.schemas.typedefs import EvoLocStatusResponseT
from evohomeasync2.schemas.typedefs import EvoLocStatusResponseT, EvoTcsConfigResponseT
from homeassistant.const import CONF_SCAN_INTERVAL
from homeassistant.core import HomeAssistant
@ -113,17 +115,19 @@ class EvoDataUpdateCoordinator(DataUpdateCoordinator):
SZ_USE_DAYLIGHT_SAVE_SWITCHING
],
}
tcs_info: EvoTcsConfigResponseT = self.tcs.config # type: ignore[assignment]
tcs_info[SZ_ZONES] = [zone.config for zone in self.tcs.zones]
if self.tcs.hotwater:
tcs_info[SZ_DHW] = self.tcs.hotwater.config
gwy_info = {
SZ_GATEWAY_ID: self.loc.gateways[0].id,
SZ_TEMPERATURE_CONTROL_SYSTEMS: [
self.loc.gateways[0].systems[0].config
],
SZ_TEMPERATURE_CONTROL_SYSTEMS: [tcs_info],
}
config = {
SZ_LOCATION_INFO: loc_info,
SZ_GATEWAYS: [{SZ_GATEWAY_INFO: gwy_info}],
}
self.logger.debug("Config = %s", config)
self.logger.debug("Config = %s", [config])
async def call_client_api(
self,
@ -203,10 +207,18 @@ class EvoDataUpdateCoordinator(DataUpdateCoordinator):
async def _update_v2_schedules(self) -> None:
for zone in self.tcs.zones:
await zone.get_schedule()
try:
await zone.get_schedule()
except ec2.InvalidScheduleError as err:
self.logger.warning(
"Zone '%s' has an invalid/missing schedule: %r", zone.name, err
)
if dhw := self.tcs.hotwater:
await dhw.get_schedule()
try:
await dhw.get_schedule()
except ec2.InvalidScheduleError as err:
self.logger.warning("DHW has an invalid/missing schedule: %r", err)
async def _async_update_data(self) -> EvoLocStatusResponseT: # type: ignore[override]
"""Fetch the latest state of an entire TCC Location.

View File

@ -6,6 +6,7 @@ import logging
from typing import Any
import evohomeasync2 as evo
from evohomeasync2.schemas.typedefs import DayOfWeekDhwT
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
@ -102,7 +103,7 @@ class EvoChild(EvoEntity):
self._evo_tcs = evo_device.tcs
self._schedule: dict[str, Any] | None = None
self._schedule: list[DayOfWeekDhwT] | None = None
self._setpoints: dict[str, Any] = {}
@property
@ -123,6 +124,9 @@ class EvoChild(EvoEntity):
Only Zones & DHW controllers (but not the TCS) can have schedules.
"""
if not self._schedule:
return self._setpoints
this_sp_dtm, this_sp_val = self._evo_device.this_switchpoint
next_sp_dtm, next_sp_val = self._evo_device.next_switchpoint
@ -152,10 +156,10 @@ class EvoChild(EvoEntity):
self._evo_device,
err,
)
self._schedule = {}
self._schedule = []
return
else:
self._schedule = schedule or {} # mypy hint
self._schedule = schedule # type: ignore[assignment]
_LOGGER.debug("Schedule['%s'] = %s", self.name, schedule)

View File

@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20250305.0"]
"requirements": ["home-assistant-frontend==20250306.0"]
}

View File

@ -65,9 +65,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
prompt_parts = [call.data[CONF_PROMPT]]
config_entry: GoogleGenerativeAIConfigEntry = hass.config_entries.async_entries(
DOMAIN
)[0]
config_entry: GoogleGenerativeAIConfigEntry = (
hass.config_entries.async_loaded_entries(DOMAIN)[0]
)
client = config_entry.runtime_data

View File

@ -64,28 +64,18 @@ async def async_setup_entry(
SUPPORTED_SCHEMA_KEYS = {
"min_items",
"example",
"property_ordering",
"pattern",
"minimum",
"default",
"any_of",
"max_length",
"title",
"min_properties",
"min_length",
"max_items",
"maximum",
"nullable",
"max_properties",
# Gemini API does not support all of the OpenAPI schema
# SoT: https://ai.google.dev/api/caching#Schema
"type",
"description",
"enum",
"format",
"items",
"description",
"nullable",
"enum",
"max_items",
"min_items",
"properties",
"required",
"items",
}
@ -109,9 +99,7 @@ def _format_schema(schema: dict[str, Any]) -> Schema:
key = _camel_to_snake(key)
if key not in SUPPORTED_SCHEMA_KEYS:
continue
if key == "any_of":
val = [_format_schema(subschema) for subschema in val]
elif key == "type":
if key == "type":
val = val.upper()
elif key == "format":
# Gemini API does not support all formats, see: https://ai.google.dev/api/caching#Schema

View File

@ -7,6 +7,6 @@
"documentation": "https://www.home-assistant.io/integrations/home_connect",
"iot_class": "cloud_push",
"loggers": ["aiohomeconnect"],
"requirements": ["aiohomeconnect==0.16.2"],
"requirements": ["aiohomeconnect==0.16.3"],
"single_config_entry": true
}

View File

@ -386,6 +386,13 @@ class HomeConnectProgramSensor(HomeConnectSensor):
def update_native_value(self) -> None:
"""Update the program sensor's status."""
self.program_running = (
status := self.appliance.status.get(StatusKey.BSH_COMMON_OPERATION_STATE)
) is not None and status.value in [
BSH_OPERATION_STATE_RUN,
BSH_OPERATION_STATE_PAUSE,
BSH_OPERATION_STATE_FINISHED,
]
event = self.appliance.events.get(cast(EventKey, self.bsh_key))
if event:
self._update_native_value(event.value)

View File

@ -3,6 +3,7 @@
"step": {
"init": {
"title": "Pick Homematic IP access point",
"description": "If you are about to register a **Homematic IP HCU1**, please press the button on top of the device before you continue.\n\nThe registration process must be completed within 5 minutes.",
"data": {
"hapid": "Access point ID (SGTIN)",
"pin": "[%key:common::config_flow::data::pin%]",

View File

@ -11,6 +11,7 @@ import voluptuous as vol
from homeassistant.components import sensor
from homeassistant.components.sensor import (
CONF_STATE_CLASS,
DEVICE_CLASS_UNITS,
DEVICE_CLASSES_SCHEMA,
ENTITY_ID_FORMAT,
STATE_CLASSES_SCHEMA,
@ -107,6 +108,20 @@ def validate_sensor_state_and_device_class_config(config: ConfigType) -> ConfigT
f"got `{CONF_DEVICE_CLASS}` '{device_class}'"
)
if (device_class := config.get(CONF_DEVICE_CLASS)) is None or (
unit_of_measurement := config.get(CONF_UNIT_OF_MEASUREMENT)
) is None:
return config
if (
device_class in DEVICE_CLASS_UNITS
and unit_of_measurement not in DEVICE_CLASS_UNITS[device_class]
):
raise vol.Invalid(
f"The unit of measurement `{unit_of_measurement}` is not valid "
f"together with device class `{device_class}`"
)
return config

View File

@ -12,5 +12,5 @@
"documentation": "https://www.home-assistant.io/integrations/nexia",
"iot_class": "cloud_polling",
"loggers": ["nexia"],
"requirements": ["nexia==2.2.1"]
"requirements": ["nexia==2.2.2"]
}

View File

@ -2,6 +2,7 @@
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
import logging
from typing import TYPE_CHECKING, cast
@ -11,6 +12,7 @@ from pysmartthings import (
Attribute,
Capability,
Device,
DeviceEvent,
Scene,
SmartThings,
SmartThingsAuthenticationFailedError,
@ -28,7 +30,14 @@ from homeassistant.helpers.config_entry_oauth2_flow import (
async_get_config_entry_implementation,
)
from .const import CONF_INSTALLED_APP_ID, CONF_LOCATION_ID, DOMAIN, MAIN, OLD_DATA
from .const import (
CONF_INSTALLED_APP_ID,
CONF_LOCATION_ID,
DOMAIN,
EVENT_BUTTON,
MAIN,
OLD_DATA,
)
_LOGGER = logging.getLogger(__name__)
@ -114,6 +123,28 @@ async def async_setup_entry(hass: HomeAssistant, entry: SmartThingsConfigEntry)
scenes=scenes,
)
def handle_button_press(event: DeviceEvent) -> None:
"""Handle a button press."""
if (
event.capability is Capability.BUTTON
and event.attribute is Attribute.BUTTON
):
hass.bus.async_fire(
EVENT_BUTTON,
{
"component_id": event.component_id,
"device_id": event.device_id,
"location_id": event.location_id,
"value": event.value,
"name": entry.runtime_data.devices[event.device_id].device.label,
"data": event.data,
},
)
entry.async_on_unload(
client.add_unspecified_device_event_listener(handle_button_press)
)
entry.async_create_background_task(
hass,
client.subscribe(
@ -160,26 +191,62 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return True
KEEP_CAPABILITY_QUIRK: dict[
Capability | str, Callable[[dict[Attribute | str, Status]], bool]
] = {
Capability.WASHER_OPERATING_STATE: (
lambda status: status[Attribute.SUPPORTED_MACHINE_STATES].value is not None
),
Capability.DEMAND_RESPONSE_LOAD_CONTROL: lambda _: True,
}
POWER_CONSUMPTION_FIELDS = {
"energy",
"power",
"deltaEnergy",
"powerEnergy",
"energySaved",
}
CAPABILITY_VALIDATION: dict[
Capability | str, Callable[[dict[Attribute | str, Status]], bool]
] = {
Capability.POWER_CONSUMPTION_REPORT: (
lambda status: (
(power_consumption := status[Attribute.POWER_CONSUMPTION].value) is not None
and all(
field in cast(dict, power_consumption)
for field in POWER_CONSUMPTION_FIELDS
)
)
)
}
def process_status(
status: dict[str, dict[Capability | str, dict[Attribute | str, Status]]],
) -> dict[str, dict[Capability | str, dict[Attribute | str, Status]]]:
"""Remove disabled capabilities from status."""
if (main_component := status.get("main")) is None or (
if (main_component := status.get(MAIN)) is None:
return status
if (
disabled_capabilities_capability := main_component.get(
Capability.CUSTOM_DISABLED_CAPABILITIES
)
) is None:
return status
disabled_capabilities = cast(
list[Capability | str],
disabled_capabilities_capability[Attribute.DISABLED_CAPABILITIES].value,
)
if disabled_capabilities is not None:
for capability in disabled_capabilities:
# We still need to make sure the climate entity can work without this capability
if (
capability in main_component
and capability != Capability.DEMAND_RESPONSE_LOAD_CONTROL
):
) is not None:
disabled_capabilities = cast(
list[Capability | str],
disabled_capabilities_capability[Attribute.DISABLED_CAPABILITIES].value,
)
if disabled_capabilities is not None:
for capability in disabled_capabilities:
if capability in main_component and (
capability not in KEEP_CAPABILITY_QUIRK
or not KEEP_CAPABILITY_QUIRK[capability](main_component[capability])
):
del main_component[capability]
for capability in list(main_component):
if capability in CAPABILITY_VALIDATION:
if not CAPABILITY_VALIDATION[capability](main_component[capability]):
del main_component[capability]
return status

View File

@ -161,9 +161,7 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateEntity):
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
if self.get_attribute_value(
Capability.THERMOSTAT_FAN_MODE, Attribute.THERMOSTAT_FAN_MODE
):
if self.supports_capability(Capability.THERMOSTAT_FAN_MODE):
flags |= ClimateEntityFeature.FAN_MODE
return flags
@ -445,12 +443,15 @@ class SmartThingsAirConditioner(SmartThingsEntity, ClimateEntity):
)
@property
def extra_state_attributes(self) -> dict[str, Any]:
def extra_state_attributes(self) -> dict[str, Any] | None:
"""Return device specific state attributes.
Include attributes from the Demand Response Load Control (drlc)
and Power Consumption capabilities.
"""
if not self.supports_capability(Capability.DEMAND_RESPONSE_LOAD_CONTROL):
return None
drlc_status = self.get_attribute_value(
Capability.DEMAND_RESPONSE_LOAD_CONTROL,
Attribute.DEMAND_RESPONSE_LOAD_CONTROL_STATUS,
@ -560,5 +561,6 @@ class SmartThingsAirConditioner(SmartThingsEntity, ClimateEntity):
Capability.AIR_CONDITIONER_MODE, Attribute.SUPPORTED_AC_MODES
)
if (state := AC_MODE_TO_STATE.get(mode)) is not None
if state not in modes
)
return modes

View File

@ -32,3 +32,5 @@ CONF_REFRESH_TOKEN = "refresh_token"
MAIN = "main"
OLD_DATA = "old_data"
EVENT_BUTTON = "smartthings.button"

View File

@ -17,6 +17,15 @@ from .const import DOMAIN
EVENT_WAIT_TIME = 5
async def async_get_config_entry_diagnostics(
hass: HomeAssistant,
entry: SmartThingsConfigEntry,
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
client = entry.runtime_data.client
return await client.get_raw_devices()
async def async_get_device_diagnostics(
hass: HomeAssistant, entry: SmartThingsConfigEntry, device: DeviceEntry
) -> dict[str, Any]:
@ -26,7 +35,8 @@ async def async_get_device_diagnostics(
identifier for identifier in device.identifiers if identifier[0] == DOMAIN
)[1]
device_status = await client.get_device_status(device_id)
device_status = await client.get_raw_device_status(device_id)
device_info = await client.get_raw_device(device_id)
events: list[DeviceEvent] = []
@ -39,11 +49,8 @@ async def async_get_device_diagnostics(
listener()
status: dict[str, Any] = {}
for component, capabilities in device_status.items():
status[component] = {}
for capability, attributes in capabilities.items():
status[component][capability] = {}
for attribute, value in attributes.items():
status[component][capability][attribute] = asdict(value)
return {"events": [asdict(event) for event in events], "status": status}
return {
"events": [asdict(event) for event in events],
"status": device_status,
"info": device_info,
}

View File

@ -48,7 +48,9 @@ class SmartThingsEntity(Entity):
self._attr_device_info.update(
{
"manufacturer": ocf.manufacturer_name,
"model": ocf.model_number.split("|")[0],
"model": (
(ocf.model_number.split("|")[0]) if ocf.model_number else None
),
"hw_version": ocf.hardware_version,
"sw_version": ocf.firmware_version,
}

View File

@ -116,7 +116,7 @@ class SmartThingsFan(SmartThingsEntity, FanEntity):
@property
def is_on(self) -> bool:
"""Return true if fan is on."""
return self.get_attribute_value(Capability.SWITCH, Attribute.SWITCH)
return self.get_attribute_value(Capability.SWITCH, Attribute.SWITCH) == "on"
@property
def percentage(self) -> int | None:
@ -132,6 +132,8 @@ class SmartThingsFan(SmartThingsEntity, FanEntity):
Requires FanEntityFeature.PRESET_MODE.
"""
if not self.supports_capability(Capability.AIR_CONDITIONER_FAN_MODE):
return None
return self.get_attribute_value(
Capability.AIR_CONDITIONER_FAN_MODE, Attribute.FAN_MODE
)
@ -142,6 +144,8 @@ class SmartThingsFan(SmartThingsEntity, FanEntity):
Requires FanEntityFeature.PRESET_MODE.
"""
if not self.supports_capability(Capability.AIR_CONDITIONER_FAN_MODE):
return None
return self.get_attribute_value(
Capability.AIR_CONDITIONER_FAN_MODE, Attribute.SUPPORTED_AC_FAN_MODES
)

View File

@ -29,5 +29,5 @@
"documentation": "https://www.home-assistant.io/integrations/smartthings",
"iot_class": "cloud_push",
"loggers": ["pysmartthings"],
"requirements": ["pysmartthings==2.5.0"]
"requirements": ["pysmartthings==2.7.0"]
}

View File

@ -130,7 +130,6 @@ class SmartThingsSensorEntityDescription(SensorEntityDescription):
unique_id_separator: str = "."
capability_ignore_list: list[set[Capability]] | None = None
options_attribute: Attribute | None = None
except_if_state_none: bool = False
CAPABILITY_TO_SENSORS: dict[
@ -581,7 +580,6 @@ CAPABILITY_TO_SENSORS: dict[
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value_fn=lambda value: value["energy"] / 1000,
suggested_display_precision=2,
except_if_state_none=True,
),
SmartThingsSensorEntityDescription(
key="power_meter",
@ -591,7 +589,6 @@ CAPABILITY_TO_SENSORS: dict[
value_fn=lambda value: value["power"],
extra_state_attributes_fn=power_attributes,
suggested_display_precision=2,
except_if_state_none=True,
),
SmartThingsSensorEntityDescription(
key="deltaEnergy_meter",
@ -601,7 +598,6 @@ CAPABILITY_TO_SENSORS: dict[
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value_fn=lambda value: value["deltaEnergy"] / 1000,
suggested_display_precision=2,
except_if_state_none=True,
),
SmartThingsSensorEntityDescription(
key="powerEnergy_meter",
@ -611,7 +607,6 @@ CAPABILITY_TO_SENSORS: dict[
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value_fn=lambda value: value["powerEnergy"] / 1000,
suggested_display_precision=2,
except_if_state_none=True,
),
SmartThingsSensorEntityDescription(
key="energySaved_meter",
@ -621,7 +616,6 @@ CAPABILITY_TO_SENSORS: dict[
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value_fn=lambda value: value["energySaved"] / 1000,
suggested_display_precision=2,
except_if_state_none=True,
),
]
},
@ -951,6 +945,7 @@ UNITS = {
"F": UnitOfTemperature.FAHRENHEIT,
"lux": LIGHT_LUX,
"mG": None,
"μg/m^3": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
}
@ -975,10 +970,6 @@ async def async_setup_entry(
for capability_list in description.capability_ignore_list
)
)
and (
not description.except_if_state_none
or device.status[MAIN][capability][attribute].value is not None
)
)

View File

@ -7,5 +7,5 @@
"iot_class": "cloud_push",
"loggers": ["snoo"],
"quality_scale": "bronze",
"requirements": ["python-snoo==0.6.0"]
"requirements": ["python-snoo==0.6.1"]
}

View File

@ -43,6 +43,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = (
state_class=SensorStateClass.TOTAL,
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.SECONDS,
suggested_unit_of_measurement=UnitOfTime.HOURS,
),
SensorEntityDescription(
key=STATUS_SENSOR_INFO_TOTAL_GENRES,

View File

@ -7,7 +7,7 @@
"documentation": "https://www.home-assistant.io/integrations/synology_dsm",
"iot_class": "local_polling",
"loggers": ["synology_dsm"],
"requirements": ["py-synologydsm-api==2.7.0"],
"requirements": ["py-synologydsm-api==2.7.1"],
"ssdp": [
{
"manufacturer": "Synology",

View File

@ -466,6 +466,7 @@ async def async_setup_entry(
for energysite in entry.runtime_data.energysites
for description in ENERGY_LIVE_DESCRIPTIONS
if description.key in energysite.live_coordinator.data
or description.key == "percentage_charged"
),
( # Add energy site history
TeslaFleetEnergyHistorySensorEntity(energysite, description)

View File

@ -68,7 +68,7 @@ class TeslemetryVehicleSensorEntityDescription(SensorEntityDescription):
polling: bool = False
polling_value_fn: Callable[[StateType], StateType] = lambda x: x
polling_available_fn: Callable[[StateType], bool] = lambda x: x is not None
nullable: bool = False
streaming_key: Signal | None = None
streaming_value_fn: Callable[[str | int | float], StateType] = lambda x: x
streaming_firmware: str = "2024.26"
@ -210,7 +210,7 @@ VEHICLE_DESCRIPTIONS: tuple[TeslemetryVehicleSensorEntityDescription, ...] = (
TeslemetryVehicleSensorEntityDescription(
key="drive_state_shift_state",
polling=True,
polling_available_fn=lambda x: True,
nullable=True,
polling_value_fn=lambda x: SHIFT_STATES.get(str(x), "p"),
streaming_key=Signal.GEAR,
streaming_value_fn=lambda x: str(ShiftState.get(x, "P")).lower(),
@ -622,10 +622,10 @@ class TeslemetryStreamSensorEntity(TeslemetryVehicleStreamEntity, RestoreSensor)
def _async_value_from_stream(self, value) -> None:
"""Update the value of the entity."""
if value is None:
self._attr_native_value = None
else:
if self.entity_description.nullable or value is not None:
self._attr_native_value = self.entity_description.streaming_value_fn(value)
else:
self._attr_native_value = None
class TeslemetryVehicleSensorEntity(TeslemetryVehicleEntity, SensorEntity):
@ -644,7 +644,7 @@ class TeslemetryVehicleSensorEntity(TeslemetryVehicleEntity, SensorEntity):
def _async_update_attrs(self) -> None:
"""Update the attributes of the sensor."""
if self.entity_description.polling_available_fn(self._value):
if self.entity_description.nullable or self._value is not None:
self._attr_available = True
self._attr_native_value = self.entity_description.polling_value_fn(
self._value

View File

@ -148,7 +148,7 @@ DESCRIPTIONS: tuple[TessieSensorEntityDescription, ...] = (
key="drive_state_shift_state",
options=["p", "d", "r", "n"],
device_class=SensorDeviceClass.ENUM,
value_fn=lambda x: x.lower() if isinstance(x, str) else x,
value_fn=lambda x: x.lower() if isinstance(x, str) else "p",
),
TessieSensorEntityDescription(
key="vehicle_state_odometer",
@ -397,6 +397,7 @@ async def async_setup_entry(
for energysite in entry.runtime_data.energysites
for description in ENERGY_LIVE_DESCRIPTIONS
if description.key in energysite.live_coordinator.data
or description.key == "percentage_charged"
),
( # Add wall connectors
TessieWallConnectorSensorEntity(energysite, din, description)
@ -449,7 +450,6 @@ class TessieEnergyLiveSensorEntity(TessieEnergyEntity, SensorEntity):
def _async_update_attrs(self) -> None:
"""Update the attributes of the sensor."""
self._attr_available = self._value is not None
self._attr_native_value = self.entity_description.value_fn(self._value)

View File

@ -54,5 +54,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/thermobeacon",
"iot_class": "local_push",
"requirements": ["thermobeacon-ble==0.8.0"]
"requirements": ["thermobeacon-ble==0.8.1"]
}

View File

@ -171,6 +171,7 @@ class WebDavBackupAgent(BackupAgent):
await open_stream(),
f"{self._backup_path}/{filename_tar}",
timeout=BACKUP_TIMEOUT,
content_length=backup.size,
)
_LOGGER.debug(

View File

@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aiowebdav2"],
"quality_scale": "bronze",
"requirements": ["aiowebdav2==0.3.1"]
"requirements": ["aiowebdav2==0.4.1"]
}

View File

@ -25,7 +25,7 @@ if TYPE_CHECKING:
APPLICATION_NAME: Final = "HomeAssistant"
MAJOR_VERSION: Final = 2025
MINOR_VERSION: Final = 3
PATCH_VERSION: Final = "0"
PATCH_VERSION: Final = "1"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 13, 0)

View File

@ -37,7 +37,7 @@ habluetooth==3.24.1
hass-nabucasa==0.92.0
hassil==2.2.3
home-assistant-bluetooth==1.13.1
home-assistant-frontend==20250305.0
home-assistant-frontend==20250306.0
home-assistant-intents==2025.3.5
httpx==0.28.1
ifaddr==0.2.0

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "homeassistant"
version = "2025.3.0"
version = "2025.3.1"
license = {text = "Apache-2.0"}
description = "Open-source home automation platform running on Python 3."
readme = "README.rst"

16
requirements_all.txt generated
View File

@ -264,7 +264,7 @@ aioharmony==0.4.1
aiohasupervisor==0.3.0
# homeassistant.components.home_connect
aiohomeconnect==0.16.2
aiohomeconnect==0.16.3
# homeassistant.components.homekit_controller
aiohomekit==3.2.8
@ -422,7 +422,7 @@ aiowaqi==3.1.0
aiowatttime==0.1.1
# homeassistant.components.webdav
aiowebdav2==0.3.1
aiowebdav2==0.4.1
# homeassistant.components.webostv
aiowebostv==0.7.3
@ -1152,7 +1152,7 @@ hole==0.8.0
holidays==0.68
# homeassistant.components.frontend
home-assistant-frontend==20250305.0
home-assistant-frontend==20250306.0
# homeassistant.components.conversation
home-assistant-intents==2025.3.5
@ -1483,7 +1483,7 @@ nettigo-air-monitor==4.0.0
neurio==0.3.1
# homeassistant.components.nexia
nexia==2.2.1
nexia==2.2.2
# homeassistant.components.nextcloud
nextcloudmonitor==1.5.1
@ -1755,7 +1755,7 @@ py-schluter==0.1.7
py-sucks==0.9.10
# homeassistant.components.synology_dsm
py-synologydsm-api==2.7.0
py-synologydsm-api==2.7.1
# homeassistant.components.atome
pyAtome==0.1.1
@ -2310,7 +2310,7 @@ pysma==0.7.5
pysmappee==0.2.29
# homeassistant.components.smartthings
pysmartthings==2.5.0
pysmartthings==2.7.0
# homeassistant.components.smarty
pysmarty2==0.10.2
@ -2467,7 +2467,7 @@ python-roborock==2.11.1
python-smarttub==0.0.39
# homeassistant.components.snoo
python-snoo==0.6.0
python-snoo==0.6.1
# homeassistant.components.songpal
python-songpal==0.16.2
@ -2890,7 +2890,7 @@ tessie-api==0.1.1
# tf-models-official==2.5.0
# homeassistant.components.thermobeacon
thermobeacon-ble==0.8.0
thermobeacon-ble==0.8.1
# homeassistant.components.thermopro
thermopro-ble==0.11.0

View File

@ -249,7 +249,7 @@ aioharmony==0.4.1
aiohasupervisor==0.3.0
# homeassistant.components.home_connect
aiohomeconnect==0.16.2
aiohomeconnect==0.16.3
# homeassistant.components.homekit_controller
aiohomekit==3.2.8
@ -404,7 +404,7 @@ aiowaqi==3.1.0
aiowatttime==0.1.1
# homeassistant.components.webdav
aiowebdav2==0.3.1
aiowebdav2==0.4.1
# homeassistant.components.webostv
aiowebostv==0.7.3
@ -981,7 +981,7 @@ hole==0.8.0
holidays==0.68
# homeassistant.components.frontend
home-assistant-frontend==20250305.0
home-assistant-frontend==20250306.0
# homeassistant.components.conversation
home-assistant-intents==2025.3.5
@ -1246,7 +1246,7 @@ netmap==0.7.0.2
nettigo-air-monitor==4.0.0
# homeassistant.components.nexia
nexia==2.2.1
nexia==2.2.2
# homeassistant.components.nextcloud
nextcloudmonitor==1.5.1
@ -1453,7 +1453,7 @@ py-nightscout==1.2.2
py-sucks==0.9.10
# homeassistant.components.synology_dsm
py-synologydsm-api==2.7.0
py-synologydsm-api==2.7.1
# homeassistant.components.hdmi_cec
pyCEC==0.5.2
@ -1882,7 +1882,7 @@ pysma==0.7.5
pysmappee==0.2.29
# homeassistant.components.smartthings
pysmartthings==2.5.0
pysmartthings==2.7.0
# homeassistant.components.smarty
pysmarty2==0.10.2
@ -2000,7 +2000,7 @@ python-roborock==2.11.1
python-smarttub==0.0.39
# homeassistant.components.snoo
python-snoo==0.6.0
python-snoo==0.6.1
# homeassistant.components.songpal
python-songpal==0.16.2
@ -2327,7 +2327,7 @@ teslemetry-stream==0.6.10
tessie-api==0.1.1
# homeassistant.components.thermobeacon
thermobeacon-ble==0.8.0
thermobeacon-ble==0.8.1
# homeassistant.components.thermopro
thermopro-ble==0.11.0

View File

@ -48,18 +48,18 @@ def location_status_fixture(install: str, loc_id: str | None = None) -> JsonObje
return load_json_object_fixture(f"{install}/status_{loc_id}.json", DOMAIN)
def dhw_schedule_fixture(install: str) -> JsonObjectType:
def dhw_schedule_fixture(install: str, dhw_id: str | None = None) -> JsonObjectType:
"""Load JSON for the schedule of a domesticHotWater zone."""
try:
return load_json_object_fixture(f"{install}/schedule_dhw.json", DOMAIN)
return load_json_object_fixture(f"{install}/schedule_{dhw_id}.json", DOMAIN)
except FileNotFoundError:
return load_json_object_fixture("default/schedule_dhw.json", DOMAIN)
def zone_schedule_fixture(install: str) -> JsonObjectType:
def zone_schedule_fixture(install: str, zon_id: str | None = None) -> JsonObjectType:
"""Load JSON for the schedule of a temperatureZone zone."""
try:
return load_json_object_fixture(f"{install}/schedule_zone.json", DOMAIN)
return load_json_object_fixture(f"{install}/schedule_{zon_id}.json", DOMAIN)
except FileNotFoundError:
return load_json_object_fixture("default/schedule_zone.json", DOMAIN)
@ -120,9 +120,9 @@ def mock_make_request(install: str) -> Callable:
elif "schedule" in url:
if url.startswith("domesticHotWater"): # /v2/domesticHotWater/{id}/schedule
return dhw_schedule_fixture(install)
return dhw_schedule_fixture(install, url[16:23])
if url.startswith("temperatureZone"): # /v2/temperatureZone/{id}/schedule
return zone_schedule_fixture(install)
return zone_schedule_fixture(install, url[16:23])
pytest.fail(f"Unexpected request: {HTTPMethod.GET} {url}")

View File

@ -15,8 +15,9 @@ TEST_INSTALLS: Final = (
"default", # evohome: multi-zone, with DHW
"h032585", # VisionProWifi: no preset modes for TCS, zoneId=systemId
"h099625", # RoundThermostat
"h139906", # zone with null schedule
"sys_004", # RoundModulation
)
# "botched", # as default: but with activeFaults, ghost zones & unknown types
TEST_INSTALLS_WITH_DHW: Final = ("default",)
TEST_INSTALLS_WITH_DHW: Final = ("default", "botched")

View File

@ -0,0 +1,3 @@
{
"dailySchedules": []
}

View File

@ -0,0 +1,3 @@
{
"dailySchedules": []
}

View File

@ -0,0 +1,143 @@
{
"dailySchedules": [
{
"dayOfWeek": "Monday",
"switchpoints": [
{
"heatSetpoint": 22.0,
"timeOfDay": "05:30:00"
},
{
"heatSetpoint": 20.0,
"timeOfDay": "08:00:00"
},
{
"heatSetpoint": 22.5,
"timeOfDay": "16:00:00"
},
{
"heatSetpoint": 15.0,
"timeOfDay": "23:00:00"
}
]
},
{
"dayOfWeek": "Tuesday",
"switchpoints": [
{
"heatSetpoint": 22.0,
"timeOfDay": "05:30:00"
},
{
"heatSetpoint": 20.0,
"timeOfDay": "08:00:00"
},
{
"heatSetpoint": 22.5,
"timeOfDay": "16:00:00"
},
{
"heatSetpoint": 15.0,
"timeOfDay": "23:00:00"
}
]
},
{
"dayOfWeek": "Wednesday",
"switchpoints": [
{
"heatSetpoint": 22.0,
"timeOfDay": "05:30:00"
},
{
"heatSetpoint": 20.0,
"timeOfDay": "08:00:00"
},
{
"heatSetpoint": 22.5,
"timeOfDay": "12:00:00"
},
{
"heatSetpoint": 15.0,
"timeOfDay": "23:00:00"
}
]
},
{
"dayOfWeek": "Thursday",
"switchpoints": [
{
"heatSetpoint": 22.0,
"timeOfDay": "05:30:00"
},
{
"heatSetpoint": 20.0,
"timeOfDay": "08:00:00"
},
{
"heatSetpoint": 22.5,
"timeOfDay": "16:00:00"
},
{
"heatSetpoint": 15.0,
"timeOfDay": "23:00:00"
}
]
},
{
"dayOfWeek": "Friday",
"switchpoints": [
{
"heatSetpoint": 22.0,
"timeOfDay": "05:30:00"
},
{
"heatSetpoint": 20.0,
"timeOfDay": "08:00:00"
},
{
"heatSetpoint": 22.5,
"timeOfDay": "16:00:00"
},
{
"heatSetpoint": 15.0,
"timeOfDay": "23:00:00"
}
]
},
{
"dayOfWeek": "Saturday",
"switchpoints": [
{
"heatSetpoint": 22.0,
"timeOfDay": "07:00:00"
},
{
"heatSetpoint": 22.5,
"timeOfDay": "16:00:00"
},
{
"heatSetpoint": 15.0,
"timeOfDay": "23:00:00"
}
]
},
{
"dayOfWeek": "Sunday",
"switchpoints": [
{
"heatSetpoint": 22.0,
"timeOfDay": "07:30:00"
},
{
"heatSetpoint": 22.5,
"timeOfDay": "16:00:00"
},
{
"heatSetpoint": 15.0,
"timeOfDay": "23:00:00"
}
]
}
]
}

View File

@ -0,0 +1,52 @@
{
"locationId": "2727366",
"gateways": [
{
"gatewayId": "2513794",
"temperatureControlSystems": [
{
"systemId": "3454856",
"zones": [
{
"zoneId": "3454854",
"temperatureStatus": {
"temperature": 22.0,
"isAvailable": true
},
"activeFaults": [
{
"faultType": "TempZoneSensorCommunicationLost",
"since": "2025-02-06T11:20:29"
}
],
"setpointStatus": {
"targetHeatTemperature": 5.0,
"setpointMode": "FollowSchedule"
},
"name": "Thermostat"
},
{
"zoneId": "3454855",
"temperatureStatus": {
"temperature": 22.0,
"isAvailable": true
},
"activeFaults": [],
"setpointStatus": {
"targetHeatTemperature": 20.0,
"setpointMode": "FollowSchedule"
},
"name": "Thermostat 2"
}
],
"activeFaults": [],
"systemModeStatus": {
"mode": "Auto",
"isPermanent": true
}
}
],
"activeFaults": []
}
]
}

View File

@ -0,0 +1,125 @@
[
{
"locationInfo": {
"locationId": "2727366",
"name": "Vr**********",
"streetAddress": "********** *",
"city": "*********",
"country": "Netherlands",
"postcode": "******",
"locationType": "Residential",
"useDaylightSaveSwitching": true,
"timeZone": {
"timeZoneId": "WEuropeStandardTime",
"displayName": "(UTC+01:00) Amsterdam, Berlijn, Bern, Rome, Stockholm, Wenen",
"offsetMinutes": 60,
"currentOffsetMinutes": 60,
"supportsDaylightSaving": true
},
"locationOwner": {
"userId": "2276512",
"username": "nobody@nowhere.com",
"firstname": "Gl***",
"lastname": "de*****"
}
},
"gateways": [
{
"gatewayInfo": {
"gatewayId": "2513794",
"mac": "************",
"crc": "****",
"isWiFi": false
},
"temperatureControlSystems": [
{
"systemId": "3454856",
"modelType": "EvoTouch",
"zones": [
{
"zoneId": "3454854",
"modelType": "HeatingZone",
"setpointCapabilities": {
"maxHeatSetpoint": 35.0,
"minHeatSetpoint": 5.0,
"valueResolution": 0.5,
"canControlHeat": true,
"canControlCool": false,
"allowedSetpointModes": [
"PermanentOverride",
"FollowSchedule",
"TemporaryOverride"
],
"maxDuration": "1.00:00:00",
"timingResolution": "00:10:00"
},
"scheduleCapabilities": {
"maxSwitchpointsPerDay": 6,
"minSwitchpointsPerDay": 1,
"timingResolution": "00:10:00",
"setpointValueResolution": 0.5
},
"name": "Thermostat",
"zoneType": "ZoneTemperatureControl"
},
{
"zoneId": "3454855",
"modelType": "RoundWireless",
"setpointCapabilities": {
"maxHeatSetpoint": 35.0,
"minHeatSetpoint": 5.0,
"valueResolution": 0.5,
"canControlHeat": true,
"canControlCool": false,
"allowedSetpointModes": [
"PermanentOverride",
"FollowSchedule",
"TemporaryOverride"
],
"maxDuration": "1.00:00:00",
"timingResolution": "00:10:00"
},
"scheduleCapabilities": {
"maxSwitchpointsPerDay": 6,
"minSwitchpointsPerDay": 0,
"timingResolution": "00:10:00",
"setpointValueResolution": 0.5
},
"name": "Thermostat 2",
"zoneType": "Thermostat"
}
],
"allowedSystemModes": [
{
"systemMode": "Auto",
"canBePermanent": true,
"canBeTemporary": false
},
{
"systemMode": "AutoWithEco",
"canBePermanent": true,
"canBeTemporary": true,
"maxDuration": "1.00:00:00",
"timingResolution": "01:00:00",
"timingMode": "Duration"
},
{
"systemMode": "Away",
"canBePermanent": true,
"canBeTemporary": true,
"maxDuration": "99.00:00:00",
"timingResolution": "1.00:00:00",
"timingMode": "Period"
},
{
"systemMode": "HeatingOff",
"canBePermanent": true,
"canBeTemporary": false
}
]
}
]
}
]
}
]

View File

@ -29,6 +29,16 @@
),
])
# ---
# name: test_ctl_set_hvac_mode[h139906]
list([
tuple(
<SystemMode.HEATING_OFF: 'HeatingOff'>,
),
tuple(
<SystemMode.AUTO: 'Auto'>,
),
])
# ---
# name: test_ctl_set_hvac_mode[minimal]
list([
tuple(
@ -70,6 +80,13 @@
),
])
# ---
# name: test_ctl_turn_off[h139906]
list([
tuple(
<SystemMode.HEATING_OFF: 'HeatingOff'>,
),
])
# ---
# name: test_ctl_turn_off[minimal]
list([
tuple(
@ -105,6 +122,13 @@
),
])
# ---
# name: test_ctl_turn_on[h139906]
list([
tuple(
<SystemMode.AUTO: 'Auto'>,
),
])
# ---
# name: test_ctl_turn_on[minimal]
list([
tuple(
@ -1118,6 +1142,136 @@
'state': 'heat',
})
# ---
# name: test_setup_platform[h139906][climate.thermostat-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_temperature': 22.0,
'friendly_name': 'Thermostat',
'hvac_modes': list([
<HVACMode.OFF: 'off'>,
<HVACMode.HEAT: 'heat'>,
]),
'max_temp': 35.0,
'min_temp': 5.0,
'preset_mode': 'none',
'preset_modes': list([
'none',
'temporary',
'permanent',
]),
'status': dict({
'activeFaults': tuple(
dict({
'fault_type': 'TempZoneSensorCommunicationLost',
'since': '2025-02-06T11:20:29+01:00',
}),
),
'setpoint_status': dict({
'setpoint_mode': 'FollowSchedule',
'target_heat_temperature': 5.0,
}),
'setpoints': dict({
}),
'temperature_status': dict({
'is_available': True,
'temperature': 22.0,
}),
'zone_id': '3454854',
}),
'supported_features': <ClimateEntityFeature: 401>,
'temperature': 5.0,
}),
'context': <ANY>,
'entity_id': 'climate.thermostat',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_setup_platform[h139906][climate.thermostat_2-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_temperature': 22.0,
'friendly_name': 'Thermostat 2',
'hvac_modes': list([
<HVACMode.OFF: 'off'>,
<HVACMode.HEAT: 'heat'>,
]),
'max_temp': 35.0,
'min_temp': 5.0,
'preset_mode': 'none',
'preset_modes': list([
'none',
'temporary',
'permanent',
]),
'status': dict({
'activeFaults': tuple(
),
'setpoint_status': dict({
'setpoint_mode': 'FollowSchedule',
'target_heat_temperature': 20.0,
}),
'setpoints': dict({
'next_sp_from': HAFakeDatetime(2024, 7, 10, 23, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Berlin')),
'next_sp_temp': 15.0,
'this_sp_from': HAFakeDatetime(2024, 7, 10, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Berlin')),
'this_sp_temp': 22.5,
}),
'temperature_status': dict({
'is_available': True,
'temperature': 22.0,
}),
'zone_id': '3454855',
}),
'supported_features': <ClimateEntityFeature: 401>,
'temperature': 20.0,
}),
'context': <ANY>,
'entity_id': 'climate.thermostat_2',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'heat',
})
# ---
# name: test_setup_platform[h139906][climate.vr-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_temperature': 22.0,
'friendly_name': 'Vr**********',
'hvac_modes': list([
<HVACMode.OFF: 'off'>,
<HVACMode.HEAT: 'heat'>,
]),
'icon': 'mdi:thermostat',
'max_temp': 35,
'min_temp': 7,
'preset_mode': None,
'preset_modes': list([
'eco',
'away',
]),
'status': dict({
'activeSystemFaults': tuple(
),
'system_id': '3454856',
'system_mode_status': dict({
'is_permanent': True,
'mode': 'Auto',
}),
}),
'supported_features': <ClimateEntityFeature: 400>,
}),
'context': <ANY>,
'entity_id': 'climate.vr',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'heat',
})
# ---
# name: test_setup_platform[minimal][climate.main_room-state]
StateSnapshot({
'attributes': ReadOnlyDict({
@ -1312,6 +1466,13 @@
),
])
# ---
# name: test_zone_set_hvac_mode[h139906]
list([
tuple(
5.0,
),
])
# ---
# name: test_zone_set_hvac_mode[minimal]
list([
tuple(
@ -1365,6 +1526,19 @@
}),
])
# ---
# name: test_zone_set_preset_mode[h139906]
list([
tuple(
5.0,
),
tuple(
5.0,
),
dict({
'until': None,
}),
])
# ---
# name: test_zone_set_preset_mode[minimal]
list([
tuple(
@ -1412,6 +1586,13 @@
}),
])
# ---
# name: test_zone_set_temperature[h139906]
list([
dict({
'until': None,
}),
])
# ---
# name: test_zone_set_temperature[minimal]
list([
dict({
@ -1447,6 +1628,13 @@
),
])
# ---
# name: test_zone_turn_off[h139906]
list([
tuple(
5.0,
),
])
# ---
# name: test_zone_turn_off[minimal]
list([
tuple(

View File

@ -11,6 +11,9 @@
# name: test_setup[h099625]
dict_keys(['refresh_system', 'set_system_mode', 'clear_zone_override', 'set_zone_override'])
# ---
# name: test_setup[h139906]
dict_keys(['refresh_system', 'set_system_mode', 'clear_zone_override', 'set_zone_override'])
# ---
# name: test_setup[minimal]
dict_keys(['refresh_system', 'reset_system', 'set_system_mode', 'clear_zone_override', 'set_zone_override'])
# ---

View File

@ -1,4 +1,14 @@
# serializer version: 1
# name: test_set_operation_mode[botched]
list([
dict({
'until': HAFakeDatetime(2024, 7, 10, 12, 0, tzinfo=datetime.timezone.utc),
}),
dict({
'until': HAFakeDatetime(2024, 7, 10, 12, 0, tzinfo=datetime.timezone.utc),
}),
])
# ---
# name: test_set_operation_mode[default]
list([
dict({

View File

@ -33,7 +33,7 @@ from .const import TEST_INSTALLS_WITH_DHW
DHW_ENTITY_ID = "water_heater.domestic_hot_water"
@pytest.mark.parametrize("install", [*TEST_INSTALLS_WITH_DHW, "botched"])
@pytest.mark.parametrize("install", TEST_INSTALLS_WITH_DHW)
async def test_setup_platform(
hass: HomeAssistant,
config: dict[str, str],

View File

@ -6,7 +6,7 @@
tuple(
),
dict({
'config': GenerateContentConfig(http_options=None, system_instruction="Current time is 05:00:00. Today's date is 2024-05-24.\nYou are a voice assistant for Home Assistant.\nAnswer questions about the world truthfully.\nAnswer in plain text. Keep it simple and to the point.\nOnly if the user wants to control a device, tell them to expose entities to their voice assistant in Home Assistant.", temperature=1.0, top_p=0.95, top_k=64.0, candidate_count=None, max_output_tokens=150, stop_sequences=None, response_logprobs=None, logprobs=None, presence_penalty=None, frequency_penalty=None, seed=None, response_mime_type=None, response_schema=None, routing_config=None, safety_settings=[SafetySetting(method=None, category=<HarmCategory.HARM_CATEGORY_HATE_SPEECH: 'HARM_CATEGORY_HATE_SPEECH'>, threshold=<HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE: 'BLOCK_MEDIUM_AND_ABOVE'>), SafetySetting(method=None, category=<HarmCategory.HARM_CATEGORY_HARASSMENT: 'HARM_CATEGORY_HARASSMENT'>, threshold=<HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE: 'BLOCK_MEDIUM_AND_ABOVE'>), SafetySetting(method=None, category=<HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: 'HARM_CATEGORY_DANGEROUS_CONTENT'>, threshold=<HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE: 'BLOCK_MEDIUM_AND_ABOVE'>), SafetySetting(method=None, category=<HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: 'HARM_CATEGORY_SEXUALLY_EXPLICIT'>, threshold=<HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE: 'BLOCK_MEDIUM_AND_ABOVE'>)], tools=[Tool(function_declarations=[FunctionDeclaration(response=None, description='Test function', name='test_tool', parameters=Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=<Type.OBJECT: 'OBJECT'>, description=None, enum=None, format=None, items=None, properties={'param1': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=<Type.ARRAY: 'ARRAY'>, description='Test parameters', enum=None, format=None, items=Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=<Type.STRING: 'STRING'>, description=None, enum=None, format=None, items=None, properties=None, required=None), properties=None, required=None), 'param2': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=[Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=<Type.NUMBER: 'NUMBER'>, description=None, enum=None, format=None, items=None, properties=None, required=None), Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=<Type.INTEGER: 'INTEGER'>, description=None, enum=None, format=None, items=None, properties=None, required=None)], max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=None, description=None, enum=None, format=None, items=None, properties=None, required=None), 'param3': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=<Type.OBJECT: 'OBJECT'>, description=None, enum=None, format=None, items=None, properties={'json': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=<Type.STRING: 'STRING'>, description=None, enum=None, format=None, items=None, properties=None, required=None)}, required=[])}, required=[]))], retrieval=None, google_search=None, google_search_retrieval=None, code_execution=None)], tool_config=None, labels=None, cached_content=None, response_modalities=None, media_resolution=None, speech_config=None, audio_timestamp=None, automatic_function_calling=AutomaticFunctionCallingConfig(disable=True, maximum_remote_calls=None, ignore_call_history=None), thinking_config=None),
'config': GenerateContentConfig(http_options=None, system_instruction="Current time is 05:00:00. Today's date is 2024-05-24.\nYou are a voice assistant for Home Assistant.\nAnswer questions about the world truthfully.\nAnswer in plain text. Keep it simple and to the point.\nOnly if the user wants to control a device, tell them to expose entities to their voice assistant in Home Assistant.", temperature=1.0, top_p=0.95, top_k=64.0, candidate_count=None, max_output_tokens=150, stop_sequences=None, response_logprobs=None, logprobs=None, presence_penalty=None, frequency_penalty=None, seed=None, response_mime_type=None, response_schema=None, routing_config=None, safety_settings=[SafetySetting(method=None, category=<HarmCategory.HARM_CATEGORY_HATE_SPEECH: 'HARM_CATEGORY_HATE_SPEECH'>, threshold=<HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE: 'BLOCK_MEDIUM_AND_ABOVE'>), SafetySetting(method=None, category=<HarmCategory.HARM_CATEGORY_HARASSMENT: 'HARM_CATEGORY_HARASSMENT'>, threshold=<HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE: 'BLOCK_MEDIUM_AND_ABOVE'>), SafetySetting(method=None, category=<HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: 'HARM_CATEGORY_DANGEROUS_CONTENT'>, threshold=<HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE: 'BLOCK_MEDIUM_AND_ABOVE'>), SafetySetting(method=None, category=<HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: 'HARM_CATEGORY_SEXUALLY_EXPLICIT'>, threshold=<HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE: 'BLOCK_MEDIUM_AND_ABOVE'>)], tools=[Tool(function_declarations=[FunctionDeclaration(response=None, description='Test function', name='test_tool', parameters=Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=<Type.OBJECT: 'OBJECT'>, description=None, enum=None, format=None, items=None, properties={'param1': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=<Type.ARRAY: 'ARRAY'>, description='Test parameters', enum=None, format=None, items=Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=<Type.STRING: 'STRING'>, description=None, enum=None, format=None, items=None, properties=None, required=None), properties=None, required=None), 'param2': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=None, description=None, enum=None, format=None, items=None, properties=None, required=None), 'param3': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=<Type.OBJECT: 'OBJECT'>, description=None, enum=None, format=None, items=None, properties={'json': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=<Type.STRING: 'STRING'>, description=None, enum=None, format=None, items=None, properties=None, required=None)}, required=[])}, required=[]))], retrieval=None, google_search=None, google_search_retrieval=None, code_execution=None)], tool_config=None, labels=None, cached_content=None, response_modalities=None, media_resolution=None, speech_config=None, audio_timestamp=None, automatic_function_calling=AutomaticFunctionCallingConfig(disable=True, maximum_remote_calls=None, ignore_call_history=None), thinking_config=None),
'history': list([
]),
'model': 'models/gemini-2.0-flash',

View File

@ -31,3 +31,18 @@
),
])
# ---
# name: test_load_entry_with_unloaded_entries
list([
tuple(
'',
tuple(
),
dict({
'contents': list([
'Write an opening speech for a Home Assistant release party',
]),
'model': 'models/gemini-2.0-flash',
}),
),
])
# ---

View File

@ -493,6 +493,26 @@ async def test_escape_decode() -> None:
{"type": "string", "enum": ["a", "b", "c"]},
{"type": "STRING", "enum": ["a", "b", "c"]},
),
(
{"type": "string", "default": "default"},
{"type": "STRING"},
),
(
{"type": "string", "pattern": "default"},
{"type": "STRING"},
),
(
{"type": "string", "maxLength": 10},
{"type": "STRING"},
),
(
{"type": "string", "minLength": 10},
{"type": "STRING"},
),
(
{"type": "string", "title": "title"},
{"type": "STRING"},
),
(
{"type": "string", "format": "enum", "enum": ["a", "b", "c"]},
{"type": "STRING", "format": "enum", "enum": ["a", "b", "c"]},
@ -517,6 +537,10 @@ async def test_escape_decode() -> None:
{"type": "number", "format": "hex"},
{"type": "NUMBER"},
),
(
{"type": "number", "minimum": 1},
{"type": "NUMBER"},
),
(
{"type": "integer", "format": "int32"},
{"type": "INTEGER", "format": "int32"},
@ -535,21 +559,7 @@ async def test_escape_decode() -> None:
),
(
{"anyOf": [{"type": "integer"}, {"type": "number"}]},
{"any_of": [{"type": "INTEGER"}, {"type": "NUMBER"}]},
),
(
{
"any_of": [
{"any_of": [{"type": "integer"}, {"type": "number"}]},
{"any_of": [{"type": "integer"}, {"type": "number"}]},
]
},
{
"any_of": [
{"any_of": [{"type": "INTEGER"}, {"type": "NUMBER"}]},
{"any_of": [{"type": "INTEGER"}, {"type": "NUMBER"}]},
]
},
{},
),
({"type": "string", "format": "lower"}, {"type": "STRING"}),
({"type": "boolean", "format": "bool"}, {"type": "BOOLEAN"}),
@ -570,7 +580,15 @@ async def test_escape_decode() -> None:
},
),
(
{"type": "object", "additionalProperties": True},
{"type": "object", "additionalProperties": True, "minProperties": 1},
{
"type": "OBJECT",
"properties": {"json": {"type": "STRING"}},
"required": [],
},
),
(
{"type": "object", "additionalProperties": True, "maxProperties": 1},
{
"type": "OBJECT",
"properties": {"json": {"type": "STRING"}},
@ -581,6 +599,20 @@ async def test_escape_decode() -> None:
{"type": "array", "items": {"type": "string"}},
{"type": "ARRAY", "items": {"type": "STRING"}},
),
(
{
"type": "array",
"items": {"type": "string"},
"minItems": 1,
"maxItems": 2,
},
{
"type": "ARRAY",
"items": {"type": "STRING"},
"min_items": 1,
"max_items": 2,
},
),
],
)
async def test_format_schema(openapi, genai_schema) -> None:

View File

@ -224,3 +224,52 @@ async def test_config_entry_error(
await hass.async_block_till_done()
assert mock_config_entry.state == state
assert any(mock_config_entry.async_get_active_flows(hass, {"reauth"})) == reauth
@pytest.mark.usefixtures("mock_init_component")
async def test_load_entry_with_unloaded_entries(
hass: HomeAssistant, snapshot: SnapshotAssertion
) -> None:
"""Test loading an entry with unloaded entries."""
config_entries = hass.config_entries.async_entries(
"google_generative_ai_conversation"
)
runtime_data = config_entries[0].runtime_data
await hass.config_entries.async_unload(config_entries[0].entry_id)
entry = MockConfigEntry(
domain="google_generative_ai_conversation",
title="Google Generative AI Conversation",
data={
"api_key": "bla",
},
state=ConfigEntryState.LOADED,
)
entry.runtime_data = runtime_data
entry.add_to_hass(hass)
stubbed_generated_content = (
"I'm thrilled to welcome you all to the release "
"party for the latest version of Home Assistant!"
)
with patch(
"google.genai.models.AsyncModels.generate_content",
return_value=Mock(
text=stubbed_generated_content,
prompt_feedback=None,
candidates=[Mock()],
),
) as mock_generate:
response = await hass.services.async_call(
"google_generative_ai_conversation",
"generate_content",
{"prompt": "Write an opening speech for a Home Assistant release party"},
blocking=True,
return_response=True,
)
assert response == {
"text": stubbed_generated_content,
}
assert [tuple(mock_call) for mock_call in mock_generate.mock_calls] == snapshot

View File

@ -27,7 +27,7 @@ from homeassistant.components.home_connect.const import (
DOMAIN,
)
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import STATE_UNAVAILABLE, Platform
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
@ -302,7 +302,7 @@ ENTITY_ID_STATES = {
)
),
)
async def test_event_sensors(
async def test_program_sensors(
client: MagicMock,
appliance_ha_id: str,
states: tuple,
@ -313,7 +313,7 @@ async def test_event_sensors(
integration_setup: Callable[[MagicMock], Awaitable[bool]],
setup_credentials: None,
) -> None:
"""Test sequence for sensors that are only available after an event happens."""
"""Test sequence for sensors that expose information about a program."""
entity_ids = ENTITY_ID_STATES.keys()
time_to_freeze = "2021-01-09 12:00:00+00:00"
@ -358,6 +358,82 @@ async def test_event_sensors(
assert hass.states.is_state(entity_id, state)
@pytest.mark.parametrize("appliance_ha_id", [TEST_HC_APP], indirect=True)
@pytest.mark.parametrize(
("initial_operation_state", "initial_state", "event_order", "entity_states"),
[
(
"BSH.Common.EnumType.OperationState.Ready",
STATE_UNAVAILABLE,
(EventType.STATUS, EventType.EVENT),
(STATE_UNKNOWN, "60"),
),
(
"BSH.Common.EnumType.OperationState.Run",
STATE_UNKNOWN,
(EventType.EVENT, EventType.STATUS),
("60", "60"),
),
],
)
async def test_program_sensor_edge_case(
initial_operation_state: str,
initial_state: str,
event_order: tuple[EventType, EventType],
entity_states: tuple[str, str],
appliance_ha_id: str,
hass: HomeAssistant,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
setup_credentials: None,
client: MagicMock,
) -> None:
"""Test edge case for the program related entities."""
entity_id = "sensor.dishwasher_program_progress"
client.get_status = AsyncMock(
return_value=ArrayOfStatus(
[
Status(
StatusKey.BSH_COMMON_OPERATION_STATE,
StatusKey.BSH_COMMON_OPERATION_STATE.value,
initial_operation_state,
)
]
)
)
assert config_entry.state == ConfigEntryState.NOT_LOADED
assert await integration_setup(client)
assert config_entry.state == ConfigEntryState.LOADED
assert hass.states.is_state(entity_id, initial_state)
for event_type, state in zip(event_order, entity_states, strict=True):
await client.add_events(
[
EventMessage(
appliance_ha_id,
event_type,
ArrayOfEvents(
[
Event(
key=event_key,
raw_key=event_key.value,
timestamp=0,
level="",
handling="",
value=value,
)
],
),
)
for event_key, value in EVENT_PROG_RUN[event_type].items()
]
)
await hass.async_block_till_done()
assert hass.states.is_state(entity_id, state)
# Program sequence for SensorDeviceClass.TIMESTAMP edge cases.
PROGRAM_SEQUENCE_EDGE_CASE = [
EVENT_PROG_DELAYED_START,

View File

@ -870,6 +870,32 @@ async def test_invalid_device_class(
assert "expected SensorDeviceClass or one of" in caplog.text
@pytest.mark.parametrize(
"hass_config",
[
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"name": "test",
"state_topic": "test-topic",
"device_class": "energy",
"unit_of_measurement": "ppm",
}
}
}
],
)
async def test_invalid_unit_of_measurement(
mqtt_mock_entry: MqttMockHAClientGenerator, caplog: pytest.LogCaptureFixture
) -> None:
"""Test device_class with invalid unit of measurement."""
assert await mqtt_mock_entry()
assert (
"The unit of measurement `ppm` is not valid together with device class `energy`"
in caplog.text
)
@pytest.mark.parametrize(
"hass_config",
[

View File

@ -68,6 +68,8 @@ async def trigger_update(
value,
data,
)
for call in mock.add_unspecified_device_event_listener.call_args_list:
call[0][0](event)
for call in mock.add_device_event_listener.call_args_list:
if call[0][0] == device_id:
call[0][3](event)

View File

@ -84,6 +84,7 @@ def mock_smartthings() -> Generator[AsyncMock]:
@pytest.fixture(
params=[
"da_ac_rac_000001",
"da_ac_rac_100001",
"da_ac_rac_01001",
"multipurpose_sensor",
"contact_sensor",
@ -100,6 +101,7 @@ def mock_smartthings() -> Generator[AsyncMock]:
"da_wm_dw_000001",
"da_wm_wd_000001",
"da_wm_wm_000001",
"da_wm_wm_000001_1",
"da_rvc_normal_000001",
"da_ks_microwave_0101x",
"hue_color_temperature_bulb",
@ -114,6 +116,8 @@ def mock_smartthings() -> Generator[AsyncMock]:
"ecobee_sensor",
"ecobee_thermostat",
"fake_fan",
"generic_fan_3_speed",
"heatit_ztrm3_thermostat",
]
)
def device_fixture(

View File

@ -0,0 +1,21 @@
{
"components": {
"main": {
"button": {
"button": {
"value": "pushed",
"timestamp": "2025-03-07T12:20:43.363Z"
},
"numberOfButtons": {
"value": 1,
"timestamp": "2025-03-07T12:20:43.363Z"
},
"supportedButtonValues": {
"value": ["pushed", "held", "pushed_2x"],
"timestamp": "2025-03-07T12:20:43.363Z"
}
},
"refresh": {}
}
}
}

View File

@ -58,6 +58,15 @@
"timestamp": "2025-02-08T21:56:09.761Z"
}
},
"powerConsumptionReport": {
"powerConsumption": {
"value": {
"start": "2025-02-09T15:45:29Z",
"end": "2025-02-09T16:15:33Z"
},
"timestamp": "2025-02-09T16:15:33.639Z"
}
},
"battery": {
"quantity": {
"value": null

View File

@ -32,7 +32,7 @@
"timestamp": "2025-02-09T14:35:56.800Z"
},
"supportedAcModes": {
"value": ["auto", "cool", "dry", "wind", "heat"],
"value": ["auto", "cool", "dry", "wind", "heat", "dryClean"],
"timestamp": "2025-02-09T15:42:13.444Z"
},
"airConditionerMode": {

View File

@ -0,0 +1,171 @@
{
"components": {
"main": {
"refresh": {},
"custom.thermostatSetpointControl": {
"minimumSetpoint": {
"value": 16,
"timestamp": "2024-11-25T22:17:38.251Z"
},
"maximumSetpoint": {
"value": 30,
"timestamp": "2024-11-25T22:17:38.251Z"
}
},
"airConditionerMode": {
"availableAcModes": {
"value": null
},
"supportedAcModes": {
"value": ["cool", "dry", "wind", "auto"],
"timestamp": "2025-03-02T10:16:19.519Z"
},
"airConditionerMode": {
"value": "cool",
"timestamp": "2025-03-02T10:16:19.519Z"
}
},
"execute": {
"data": {
"value": null
}
},
"airQualitySensor": {
"airQuality": {
"value": null
}
},
"switch": {
"switch": {
"value": "off",
"timestamp": "2025-03-02T06:54:52.852Z"
}
},
"ocf": {
"st": {
"value": null
},
"mndt": {
"value": null
},
"mnfv": {
"value": null
},
"mnhw": {
"value": null
},
"di": {
"value": "F8042E25-0E53-0000-0000-000000000000",
"timestamp": "2025-02-28T21:15:28.920Z"
},
"mnsl": {
"value": null
},
"dmv": {
"value": "res.1.1.0,sh.1.1.0",
"timestamp": "2025-02-28T21:15:28.920Z"
},
"n": {
"value": "Room A/C",
"timestamp": "2025-02-28T21:15:28.920Z"
},
"mnmo": {
"value": "TP6X_RAC_15K",
"timestamp": "2025-02-28T21:15:28.920Z"
},
"vid": {
"value": "DA-AC-RAC-100001",
"timestamp": "2025-02-28T21:15:28.920Z"
},
"mnmn": {
"value": "Samsung Electronics",
"timestamp": "2025-02-28T21:15:28.920Z"
},
"mnml": {
"value": null
},
"mnpv": {
"value": null
},
"mnos": {
"value": null
},
"pi": {
"value": "shp",
"timestamp": "2025-02-28T21:15:28.920Z"
},
"icv": {
"value": "core.1.1.0",
"timestamp": "2025-02-28T21:15:28.920Z"
}
},
"odorSensor": {
"odorLevel": {
"value": null
}
},
"airConditionerFanMode": {
"fanMode": {
"value": "auto",
"timestamp": "2025-02-28T21:15:28.941Z"
},
"supportedAcFanModes": {
"value": ["auto", "low", "medium", "high", "turbo"],
"timestamp": "2025-02-28T21:15:28.941Z"
},
"availableAcFanModes": {
"value": null
}
},
"samsungce.driverState": {
"driverState": {
"value": null
}
},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": ["odorSensor"],
"timestamp": "2024-11-25T22:17:38.251Z"
}
},
"samsungce.driverVersion": {
"versionNumber": {
"value": 22090101,
"timestamp": "2024-11-25T22:17:38.251Z"
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": null
},
"temperature": {
"value": 27,
"unit": "C",
"timestamp": "2025-03-02T08:28:39.409Z"
}
},
"dustSensor": {
"dustLevel": {
"value": 46,
"unit": "\u03bcg/m^3",
"timestamp": "2025-03-06T16:01:49.656000+00:00"
},
"fineDustLevel": {
"value": 10,
"unit": "\u03bcg/m^3",
"timestamp": "2025-03-06T16:01:49.656000+00:00"
}
},
"thermostatCoolingSetpoint": {
"coolingSetpointRange": {
"value": null
},
"coolingSetpoint": {
"value": 18,
"unit": "C",
"timestamp": "2025-03-02T06:54:23.887Z"
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
{
"components": {
"main": {
"refresh": {},
"fanSpeed": {
"fanSpeed": {
"value": 0,
"timestamp": "2025-03-06T11:47:32.683Z"
}
},
"switch": {
"switch": {
"value": "off",
"timestamp": "2025-03-06T11:47:32.697Z"
}
}
}
}
}

View File

@ -0,0 +1,60 @@
{
"components": {
"main": {
"powerMeter": {
"power": {
"value": 368.17,
"unit": "W",
"timestamp": "2025-03-07T12:52:08.997Z"
}
},
"thermostatOperatingState": {
"thermostatOperatingState": {
"value": "heating",
"timestamp": "2025-03-07T12:49:53.638Z"
}
},
"energyMeter": {
"energy": {
"value": 2339.5,
"unit": "kWh",
"timestamp": "2025-03-07T12:26:37.133Z"
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": null
},
"temperature": {
"value": 19.0,
"unit": "C",
"timestamp": "2025-03-07T12:52:39.210Z"
}
},
"thermostatHeatingSetpoint": {
"heatingSetpoint": {
"value": 19.0,
"unit": "C",
"timestamp": "2025-03-06T21:38:22.856Z"
},
"heatingSetpointRange": {
"value": null
}
},
"refresh": {},
"thermostatMode": {
"thermostatMode": {
"value": "heat",
"data": {
"supportedThermostatModes": ["off", "heat"]
},
"timestamp": "2025-03-06T21:38:23.046Z"
},
"supportedThermostatModes": {
"value": ["off", "heat"],
"timestamp": "2023-09-22T15:41:01.268Z"
}
}
}
}
}

View File

@ -0,0 +1,49 @@
{
"items": [
{
"deviceId": "c4bdd19f-85d1-4d58-8f9c-e75ac3cf113b",
"name": "button",
"label": "button",
"manufacturerName": "SmartThingsCommunity",
"presentationId": "238c483a-10e8-359b-b032-1be2b2fcdee7",
"locationId": "88a3a314-f0c8-40b4-bb44-44ba06c9c42f",
"ownerId": "12d4af93-cb68-b108-87f5-625437d7371f",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "button",
"version": 1
},
{
"id": "refresh",
"version": 1
}
],
"categories": [
{
"name": "Other",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2025-03-07T12:20:43.273Z",
"profile": {
"id": "b045d731-4d01-35bc-8018-b3da711d8904"
},
"virtual": {
"name": "button",
"executingLocally": false
},
"type": "VIRTUAL",
"restrictionTier": 0,
"allowed": null,
"executionContext": "CLOUD",
"relationships": []
}
],
"_links": {}
}

View File

@ -286,18 +286,10 @@
"id": "60fbc713-8da5-315d-b31a-6d6dcde4be7b"
},
"ocf": {
"ocfDeviceType": "oic.d.airconditioner",
"name": "[room a/c] Samsung",
"specVersion": "core.1.1.0",
"verticalDomainSpecVersion": "res.1.1.0,sh.1.1.0",
"ocfDeviceType": "x.com.st.d.sensor.light",
"manufacturerName": "Samsung Electronics",
"modelNumber": "ARTIK051_KRAC_18K|10193441|60010132001111110200000000000000",
"platformVersion": "0G3MPDCKA00010E",
"platformOS": "TizenRT2.0",
"hwVersion": "1.0",
"firmwareVersion": "0.1.0",
"vendorId": "DA-AC-RAC-000001",
"lastSignupTime": "2021-04-06T16:43:27.889445Z",
"vendorId": "VD-Sensor.Light-2023",
"lastSignupTime": "2025-01-08T02:32:04.631093137Z",
"transferCandidate": false,
"additionalAuthCodeRequired": false
},

View File

@ -0,0 +1,112 @@
{
"items": [
{
"deviceId": "F8042E25-0E53-0000-0000-000000000000",
"name": "Room A/C",
"label": "Corridor A/C",
"manufacturerName": "Samsung Electronics",
"presentationId": "DA-AC-RAC-100001",
"deviceManufacturerCode": "Samsung Electronics",
"locationId": "5df0730b-38ed-43e4-b291-ec14feb3224c",
"ownerId": "63b9c79b-90fe-5262-9a6a-5e24db90915e",
"roomId": "7715151d-0314-457a-a82c-5ce48900e065",
"deviceTypeName": "Samsung OCF Air Conditioner",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "ocf",
"version": 1
},
{
"id": "switch",
"version": 1
},
{
"id": "airConditionerMode",
"version": 1
},
{
"id": "airConditionerFanMode",
"version": 1
},
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "thermostatCoolingSetpoint",
"version": 1
},
{
"id": "airQualitySensor",
"version": 1
},
{
"id": "dustSensor",
"version": 1
},
{
"id": "odorSensor",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "execute",
"version": 1
},
{
"id": "samsungce.driverVersion",
"version": 1
},
{
"id": "samsungce.driverState",
"version": 1
},
{
"id": "custom.thermostatSetpointControl",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
}
],
"categories": [
{
"name": "AirConditioner",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2024-11-25T22:17:38.129Z",
"profile": {
"id": "9e3e03b1-7f8c-3ea2-8568-6902b79b99dd"
},
"ocf": {
"ocfDeviceType": "oic.d.airconditioner",
"name": "Room A/C",
"specVersion": "core.1.1.0",
"verticalDomainSpecVersion": "res.1.1.0,sh.1.1.0",
"manufacturerName": "Samsung Electronics",
"modelNumber": "TP6X_RAC_15K",
"vendorId": "DA-AC-RAC-100001",
"lastSignupTime": "2024-11-25T22:17:37.928118320Z",
"transferCandidate": false,
"additionalAuthCodeRequired": false
},
"type": "OCF",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD",
"relationships": []
}
],
"_links": {}
}

View File

@ -0,0 +1,261 @@
{
"items": [
{
"deviceId": "63803fae-cbed-f356-a063-2cf148ae3ca7",
"name": "[washer] Samsung",
"label": "Washing Machine",
"manufacturerName": "Samsung Electronics",
"presentationId": "DA-WM-WM-000001",
"deviceManufacturerCode": "Samsung Electronics",
"locationId": "ca23214d-d9ae-41e5-9d26-f1a604c864d8",
"ownerId": "9b53a4ba-4422-b04d-f436-33c0490e7c37",
"roomId": "e226f1ae-1112-4794-bd3a-0beddf811645",
"deviceTypeName": "Samsung OCF Washer",
"components": [
{
"id": "main",
"label": "Washing Machine",
"capabilities": [
{
"id": "execute",
"version": 1
},
{
"id": "ocf",
"version": 1
},
{
"id": "powerConsumptionReport",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "remoteControlStatus",
"version": 1
},
{
"id": "demandResponseLoadControl",
"version": 1
},
{
"id": "switch",
"version": 1
},
{
"id": "washerOperatingState",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
},
{
"id": "custom.dryerDryLevel",
"version": 1
},
{
"id": "custom.energyType",
"version": 1
},
{
"id": "custom.jobBeginningStatus",
"version": 1
},
{
"id": "custom.supportedOptions",
"version": 1
},
{
"id": "custom.washerAutoDetergent",
"version": 1
},
{
"id": "custom.washerAutoSoftener",
"version": 1
},
{
"id": "custom.washerRinseCycles",
"version": 1
},
{
"id": "custom.washerSoilLevel",
"version": 1
},
{
"id": "custom.washerSpinLevel",
"version": 1
},
{
"id": "custom.washerWaterTemperature",
"version": 1
},
{
"id": "samsungce.autoDispenseDetergent",
"version": 1
},
{
"id": "samsungce.autoDispenseSoftener",
"version": 1
},
{
"id": "samsungce.detergentOrder",
"version": 1
},
{
"id": "samsungce.detergentState",
"version": 1
},
{
"id": "samsungce.deviceIdentification",
"version": 1
},
{
"id": "samsungce.dongleSoftwareInstallation",
"version": 1
},
{
"id": "samsungce.detergentAutoReplenishment",
"version": 1
},
{
"id": "samsungce.softenerAutoReplenishment",
"version": 1
},
{
"id": "samsungce.driverVersion",
"version": 1
},
{
"id": "samsungce.softwareUpdate",
"version": 1
},
{
"id": "samsungce.kidsLock",
"version": 1
},
{
"id": "samsungce.softenerOrder",
"version": 1
},
{
"id": "samsungce.softenerState",
"version": 1
},
{
"id": "samsungce.washerBubbleSoak",
"version": 1
},
{
"id": "samsungce.washerCycle",
"version": 1
},
{
"id": "samsungce.washerCyclePreset",
"version": 1
},
{
"id": "samsungce.washerDelayEnd",
"version": 1
},
{
"id": "samsungce.washerFreezePrevent",
"version": 1
},
{
"id": "samsungce.washerOperatingState",
"version": 1
},
{
"id": "samsungce.washerWashingTime",
"version": 1
},
{
"id": "samsungce.washerWaterLevel",
"version": 1
},
{
"id": "samsungce.washerWaterValve",
"version": 1
},
{
"id": "samsungce.welcomeMessage",
"version": 1
},
{
"id": "samsungce.waterConsumptionReport",
"version": 1
},
{
"id": "samsungce.quickControl",
"version": 1
},
{
"id": "samsungce.energyPlanner",
"version": 1
},
{
"id": "sec.diagnosticsInformation",
"version": 1
},
{
"id": "sec.wifiConfiguration",
"version": 1
}
],
"categories": [
{
"name": "Washer",
"categoryType": "manufacturer"
}
]
},
{
"id": "hca.main",
"label": "hca.main",
"capabilities": [
{
"id": "hca.washerMode",
"version": 1
}
],
"categories": [
{
"name": "Other",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2020-03-04T03:03:19Z",
"profile": {
"id": "3f221c79-d81c-315f-8e8b-b5742802a1e3"
},
"ocf": {
"ocfDeviceType": "oic.d.washer",
"name": "[washer] Samsung",
"specVersion": "core.1.1.0",
"verticalDomainSpecVersion": "1.2.1",
"manufacturerName": "Samsung Electronics",
"modelNumber": "DA_WM_A51_20_COMMON|20224941|20010102011211030203000000000000",
"platformVersion": "DAWIT 2.0",
"platformOS": "TizenRT 1.0 + IPv6",
"hwVersion": "ARTIK051",
"firmwareVersion": "DA_WM_A51_20_COMMON_30230708",
"vendorId": "DA-WM-WM-000001",
"vendorResourceClientServerVersion": "ARTIK051 Release 2.210224.1",
"lastSignupTime": "2024-12-27T04:47:59.763899737Z",
"transferCandidate": false,
"additionalAuthCodeRequired": false
},
"type": "OCF",
"restrictionTier": 0,
"allowed": null,
"executionContext": "CLOUD",
"relationships": []
}
],
"_links": {}
}

View File

@ -0,0 +1,63 @@
{
"items": [
{
"deviceId": "6d95a8b7-4ee3-429a-a13a-00ec9354170c",
"name": "GE In-Wall Smart Dimmer",
"label": "Bedroom Fan",
"manufacturerName": "SmartThingsEdge",
"presentationId": "generic-fan-3-speed",
"deviceManufacturerCode": "0063-4944-3131",
"locationId": "f1313f27-6732-481d-a2a9-c7bbf900f867",
"ownerId": "e5216062-ac82-79b8-20db-ea65fa3d3fdd",
"roomId": "5f77f7cf-ece8-485e-a409-98f7b128a41a",
"components": [
{
"id": "main",
"label": "Bedroom Fan",
"capabilities": [
{
"id": "fanSpeed",
"version": 1
},
{
"id": "switch",
"version": 1
},
{
"id": "refresh",
"version": 1
}
],
"categories": [
{
"name": "Fan",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2018-01-12T22:12:15Z",
"parentDeviceId": "4ceb9b86-2f0d-4e98-ba4e-3fbe705f7805",
"profile": {
"id": "9bd81754-fc81-3ed1-86c2-d1094d6cbf6d"
},
"zwave": {
"networkId": "02",
"driverId": "e7947a05-947d-4bb5-92c4-2aafaff6d69c",
"executingLocally": true,
"hubId": "4ceb9b86-2f0d-4e98-ba4e-3fbe705f7805",
"networkSecurityLevel": "ZWAVE_LEGACY_NON_SECURE",
"provisioningState": "PROVISIONED",
"manufacturerId": 99,
"productType": 18756,
"productId": 12593
},
"type": "ZWAVE",
"restrictionTier": 0,
"allowed": null,
"executionContext": "LOCAL",
"relationships": []
}
],
"_links": {}
}

View File

@ -0,0 +1,79 @@
{
"items": [
{
"deviceId": "69a271f6-6537-4982-8cd9-979866872692",
"name": "heatit-ztrm3-thermostat",
"label": "Hall thermostat",
"manufacturerName": "SmartThingsCommunity",
"presentationId": "8c5c0adc-73d6-33db-a1bd-67d746ab0e00",
"deviceManufacturerCode": "019B-0003-0203",
"locationId": "6cf6637b-9bc5-4e52-bc99-7497e322fb0d",
"ownerId": "7b68139b-d068-45d8-bf27-961320350024",
"roomId": "746b4d54-8026-44f1-b50f-8833dafdeea3",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "thermostatHeatingSetpoint",
"version": 1
},
{
"id": "thermostatOperatingState",
"version": 1
},
{
"id": "thermostatMode",
"version": 1
},
{
"id": "powerMeter",
"version": 1
},
{
"id": "energyMeter",
"version": 1
},
{
"id": "refresh",
"version": 1
}
],
"categories": [
{
"name": "Thermostat",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2023-09-22T15:40:58.942Z",
"parentDeviceId": "d04f5ba0-1430-4826-9aa4-fba4efb57c24",
"profile": {
"id": "2677e0e8-9241-3163-815e-6b1d6743f280"
},
"zwave": {
"networkId": "28",
"driverId": "28198799-de20-4cfd-a9f3-67860a0877d5",
"executingLocally": true,
"hubId": "d04f5ba0-1430-4826-9aa4-fba4efb57c24",
"networkSecurityLevel": "ZWAVE_S2_AUTHENTICATED",
"provisioningState": "PROVISIONED",
"manufacturerId": 411,
"productType": 3,
"productId": 515
},
"type": "ZWAVE",
"restrictionTier": 0,
"allowed": null,
"executionContext": "LOCAL",
"relationships": []
}
],
"_links": {}
}

View File

@ -209,6 +209,90 @@
'state': 'off',
})
# ---
# name: test_all_entities[da_ac_rac_100001][climate.corridor_a_c-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'fan_modes': list([
'auto',
'low',
'medium',
'high',
'turbo',
]),
'hvac_modes': list([
<HVACMode.OFF: 'off'>,
<HVACMode.COOL: 'cool'>,
<HVACMode.DRY: 'dry'>,
<HVACMode.FAN_ONLY: 'fan_only'>,
<HVACMode.HEAT_COOL: 'heat_cool'>,
]),
'max_temp': 35,
'min_temp': 7,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'climate',
'entity_category': None,
'entity_id': 'climate.corridor_a_c',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 393>,
'translation_key': None,
'unique_id': 'F8042E25-0E53-0000-0000-000000000000',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[da_ac_rac_100001][climate.corridor_a_c-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_temperature': 27,
'fan_mode': 'auto',
'fan_modes': list([
'auto',
'low',
'medium',
'high',
'turbo',
]),
'friendly_name': 'Corridor A/C',
'hvac_modes': list([
<HVACMode.OFF: 'off'>,
<HVACMode.COOL: 'cool'>,
<HVACMode.DRY: 'dry'>,
<HVACMode.FAN_ONLY: 'fan_only'>,
<HVACMode.HEAT_COOL: 'heat_cool'>,
]),
'max_temp': 35,
'min_temp': 7,
'supported_features': <ClimateEntityFeature: 393>,
'temperature': 18,
}),
'context': <ANY>,
'entity_id': 'climate.corridor_a_c',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[ecobee_thermostat][climate.main_floor-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
@ -285,6 +369,70 @@
'state': 'heat',
})
# ---
# name: test_all_entities[heatit_ztrm3_thermostat][climate.hall_thermostat-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'hvac_modes': list([
<HVACMode.HEAT: 'heat'>,
]),
'max_temp': 35,
'min_temp': 7,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'climate',
'entity_category': None,
'entity_id': 'climate.hall_thermostat',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 387>,
'translation_key': None,
'unique_id': '69a271f6-6537-4982-8cd9-979866872692',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[heatit_ztrm3_thermostat][climate.hall_thermostat-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_temperature': 19.0,
'friendly_name': 'Hall thermostat',
'hvac_action': <HVACAction.HEATING: 'heating'>,
'hvac_modes': list([
<HVACMode.HEAT: 'heat'>,
]),
'max_temp': 35,
'min_temp': 7,
'supported_features': <ClimateEntityFeature: 387>,
'target_temp_high': None,
'target_temp_low': None,
'temperature': 19.0,
}),
'context': <ANY>,
'entity_id': 'climate.hall_thermostat',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'heat',
})
# ---
# name: test_all_entities[virtual_thermostat][climate.asd-entry]
EntityRegistryEntrySnapshot({
'aliases': set({

File diff suppressed because it is too large Load Diff

View File

@ -62,6 +62,60 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
'state': 'off',
})
# ---
# name: test_all_entities[generic_fan_3_speed][fan.bedroom_fan-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'preset_modes': None,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'fan',
'entity_category': None,
'entity_id': 'fan.bedroom_fan',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': <FanEntityFeature: 49>,
'translation_key': None,
'unique_id': '6d95a8b7-4ee3-429a-a13a-00ec9354170c',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[generic_fan_3_speed][fan.bedroom_fan-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Bedroom Fan',
'percentage': 0,
'percentage_step': 33.333333333333336,
'preset_mode': None,
'preset_modes': None,
'supported_features': <FanEntityFeature: 49>,
}),
'context': <ANY>,
'entity_id': 'fan.bedroom_fan',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---

View File

@ -1,4 +1,7 @@
# serializer version: 1
# name: test_button_event[button]
<Event smartthings.button[L]: component_id=main, device_id=c4bdd19f-85d1-4d58-8f9c-e75ac3cf113b, location_id=abc, value=pushed, name=button, data=None>
# ---
# name: test_devices[aeotec_home_energy_meter_gen5]
DeviceRegistryEntrySnapshot({
'area_id': None,
@ -207,7 +210,7 @@
}),
'disabled_by': None,
'entry_type': None,
'hw_version': '1.0',
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
@ -219,14 +222,14 @@
'labels': set({
}),
'manufacturer': 'Samsung Electronics',
'model': 'ARTIK051_KRAC_18K',
'model': None,
'model_id': None,
'name': 'AC Office Granit',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'suggested_area': None,
'sw_version': '0.1.0',
'sw_version': None,
'via_device_id': None,
})
# ---
@ -263,6 +266,39 @@
'via_device_id': None,
})
# ---
# name: test_devices[da_ac_rac_100001]
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'https://account.smartthings.com',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'smartthings',
'F8042E25-0E53-0000-0000-000000000000',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'Samsung Electronics',
'model': 'TP6X_RAC_15K',
'model_id': None,
'name': 'Corridor A/C',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'suggested_area': None,
'sw_version': None,
'via_device_id': None,
})
# ---
# name: test_devices[da_ks_microwave_0101x]
DeviceRegistryEntrySnapshot({
'area_id': None,
@ -461,6 +497,39 @@
'via_device_id': None,
})
# ---
# name: test_devices[da_wm_wm_000001_1]
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'https://account.smartthings.com',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': 'ARTIK051',
'id': <ANY>,
'identifiers': set({
tuple(
'smartthings',
'63803fae-cbed-f356-a063-2cf148ae3ca7',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'Samsung Electronics',
'model': 'DA_WM_A51_20_COMMON',
'model_id': None,
'name': 'Washing Machine',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'suggested_area': None,
'sw_version': 'DA_WM_A51_20_COMMON_30230708',
'via_device_id': None,
})
# ---
# name: test_devices[ecobee_sensor]
DeviceRegistryEntrySnapshot({
'area_id': None,
@ -593,6 +662,72 @@
'via_device_id': None,
})
# ---
# name: test_devices[generic_fan_3_speed]
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'https://account.smartthings.com',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'smartthings',
'6d95a8b7-4ee3-429a-a13a-00ec9354170c',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': None,
'model': None,
'model_id': None,
'name': 'Bedroom Fan',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'suggested_area': None,
'sw_version': None,
'via_device_id': None,
})
# ---
# name: test_devices[heatit_ztrm3_thermostat]
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': 'https://account.smartthings.com',
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'smartthings',
'69a271f6-6537-4982-8cd9-979866872692',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': None,
'model': None,
'model_id': None,
'name': 'Hall thermostat',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'suggested_area': None,
'sw_version': None,
'via_device_id': None,
})
# ---
# name: test_devices[hue_color_temperature_bulb]
DeviceRegistryEntrySnapshot({
'area_id': None,

View File

@ -1379,6 +1379,213 @@
'state': '0',
})
# ---
# name: test_all_entities[da_ac_rac_100001][sensor.corridor_a_c_air_quality-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.corridor_a_c_air_quality',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Air quality',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'air_quality',
'unique_id': 'F8042E25-0E53-0000-0000-000000000000.airQuality',
'unit_of_measurement': 'CAQI',
})
# ---
# name: test_all_entities[da_ac_rac_100001][sensor.corridor_a_c_air_quality-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Corridor A/C Air quality',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'CAQI',
}),
'context': <ANY>,
'entity_id': 'sensor.corridor_a_c_air_quality',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_all_entities[da_ac_rac_100001][sensor.corridor_a_c_pm10-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.corridor_a_c_pm10',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.PM10: 'pm10'>,
'original_icon': None,
'original_name': 'PM10',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'F8042E25-0E53-0000-0000-000000000000.dustLevel',
'unit_of_measurement': 'µg/m³',
})
# ---
# name: test_all_entities[da_ac_rac_100001][sensor.corridor_a_c_pm10-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'pm10',
'friendly_name': 'Corridor A/C PM10',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'µg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.corridor_a_c_pm10',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '46',
})
# ---
# name: test_all_entities[da_ac_rac_100001][sensor.corridor_a_c_pm2_5-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.corridor_a_c_pm2_5',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.PM25: 'pm25'>,
'original_icon': None,
'original_name': 'PM2.5',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'F8042E25-0E53-0000-0000-000000000000.fineDustLevel',
'unit_of_measurement': 'µg/m³',
})
# ---
# name: test_all_entities[da_ac_rac_100001][sensor.corridor_a_c_pm2_5-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'pm25',
'friendly_name': 'Corridor A/C PM2.5',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'µg/m³',
}),
'context': <ANY>,
'entity_id': 'sensor.corridor_a_c_pm2_5',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '10',
})
# ---
# name: test_all_entities[da_ac_rac_100001][sensor.corridor_a_c_temperature-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.corridor_a_c_temperature',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
'original_icon': None,
'original_name': 'Temperature',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'F8042E25-0E53-0000-0000-000000000000.temperature',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
# name: test_all_entities[da_ac_rac_100001][sensor.corridor_a_c_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Corridor A/C Temperature',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.corridor_a_c_temperature',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '27',
})
# ---
# name: test_all_entities[da_ks_microwave_0101x][sensor.microwave_completion_time-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
@ -3692,6 +3899,475 @@
'state': '0.0',
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_completion_time-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.washing_machine_completion_time',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
'original_icon': None,
'original_name': 'Completion time',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'completion_time',
'unique_id': '63803fae-cbed-f356-a063-2cf148ae3ca7.completionTime',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_completion_time-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'timestamp',
'friendly_name': 'Washing Machine Completion time',
}),
'context': <ANY>,
'entity_id': 'sensor.washing_machine_completion_time',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2025-03-07T07:01:12+00:00',
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_energy-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.washing_machine_energy',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Energy',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '63803fae-cbed-f356-a063-2cf148ae3ca7.energy_meter',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_energy-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Washing Machine Energy',
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.washing_machine_energy',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '1323.6',
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_energy_difference-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.washing_machine_energy_difference',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Energy difference',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'energy_difference',
'unique_id': '63803fae-cbed-f356-a063-2cf148ae3ca7.deltaEnergy_meter',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_energy_difference-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Washing Machine Energy difference',
'state_class': <SensorStateClass.TOTAL: 'total'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.washing_machine_energy_difference',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0.1',
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_energy_saved-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.washing_machine_energy_saved',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Energy saved',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'energy_saved',
'unique_id': '63803fae-cbed-f356-a063-2cf148ae3ca7.energySaved_meter',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_energy_saved-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Washing Machine Energy saved',
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.washing_machine_energy_saved',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0.0',
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_job_state-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'air_wash',
'ai_rinse',
'ai_spin',
'ai_wash',
'cooling',
'delay_wash',
'drying',
'finish',
'none',
'pre_wash',
'rinse',
'spin',
'wash',
'weight_sensing',
'wrinkle_prevent',
'freeze_protection',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.washing_machine_job_state',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Job state',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'washer_job_state',
'unique_id': '63803fae-cbed-f356-a063-2cf148ae3ca7.washerJobState',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_job_state-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Washing Machine Job state',
'options': list([
'air_wash',
'ai_rinse',
'ai_spin',
'ai_wash',
'cooling',
'delay_wash',
'drying',
'finish',
'none',
'pre_wash',
'rinse',
'spin',
'wash',
'weight_sensing',
'wrinkle_prevent',
'freeze_protection',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.washing_machine_job_state',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'wash',
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_machine_state-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'pause',
'run',
'stop',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.washing_machine_machine_state',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Machine state',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'washer_machine_state',
'unique_id': '63803fae-cbed-f356-a063-2cf148ae3ca7.machineState',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_machine_state-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'Washing Machine Machine state',
'options': list([
'pause',
'run',
'stop',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.washing_machine_machine_state',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'run',
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_power-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.washing_machine_power',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
}),
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
'original_icon': None,
'original_name': 'Power',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '63803fae-cbed-f356-a063-2cf148ae3ca7.power_meter',
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_power-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'power',
'friendly_name': 'Washing Machine Power',
'power_consumption_end': '2025-03-07T06:23:21Z',
'power_consumption_start': '2025-03-07T06:21:09Z',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
}),
'context': <ANY>,
'entity_id': 'sensor.washing_machine_power',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_power_energy-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.washing_machine_power_energy',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Power energy',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'power_energy',
'unique_id': '63803fae-cbed-f356-a063-2cf148ae3ca7.powerEnergy_meter',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][sensor.washing_machine_power_energy-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Washing Machine Power energy',
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.washing_machine_power_energy',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0.0',
})
# ---
# name: test_all_entities[ecobee_sensor][sensor.child_bedroom_temperature-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
@ -3848,6 +4524,162 @@
'state': '22',
})
# ---
# name: test_all_entities[heatit_ztrm3_thermostat][sensor.hall_thermostat_energy-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.hall_thermostat_energy',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Energy',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '69a271f6-6537-4982-8cd9-979866872692.energy',
'unit_of_measurement': 'kWh',
})
# ---
# name: test_all_entities[heatit_ztrm3_thermostat][sensor.hall_thermostat_energy-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Hall thermostat Energy',
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': 'kWh',
}),
'context': <ANY>,
'entity_id': 'sensor.hall_thermostat_energy',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2339.5',
})
# ---
# name: test_all_entities[heatit_ztrm3_thermostat][sensor.hall_thermostat_power-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.hall_thermostat_power',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
'original_icon': None,
'original_name': 'Power',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '69a271f6-6537-4982-8cd9-979866872692.power',
'unit_of_measurement': 'W',
})
# ---
# name: test_all_entities[heatit_ztrm3_thermostat][sensor.hall_thermostat_power-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'power',
'friendly_name': 'Hall thermostat Power',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'W',
}),
'context': <ANY>,
'entity_id': 'sensor.hall_thermostat_power',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '368.17',
})
# ---
# name: test_all_entities[heatit_ztrm3_thermostat][sensor.hall_thermostat_temperature-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.hall_thermostat_temperature',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
'original_icon': None,
'original_name': 'Temperature',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '69a271f6-6537-4982-8cd9-979866872692.temperature',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
# name: test_all_entities[heatit_ztrm3_thermostat][sensor.hall_thermostat_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Hall thermostat Temperature',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.hall_thermostat_temperature',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '19.0',
})
# ---
# name: test_all_entities[multipurpose_sensor][sensor.deck_door_battery-entry]
EntityRegistryEntrySnapshot({
'aliases': set({

View File

@ -281,6 +281,53 @@
'state': 'off',
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][switch.washing_machine-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.washing_machine',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '63803fae-cbed-f356-a063-2cf148ae3ca7',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[da_wm_wm_000001_1][switch.washing_machine-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Washing Machine',
}),
'context': <ANY>,
'entity_id': 'switch.washing_machine',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_all_entities[sensibo_airconditioner_1][switch.office-entry]
EntityRegistryEntrySnapshot({
'aliases': set({

View File

@ -12,13 +12,36 @@ from homeassistant.helpers import device_registry as dr
from . import setup_integration
from tests.common import MockConfigEntry
from tests.components.diagnostics import get_diagnostics_for_device
from tests.common import MockConfigEntry, load_json_object_fixture
from tests.components.diagnostics import (
get_diagnostics_for_config_entry,
get_diagnostics_for_device,
)
from tests.typing import ClientSessionGenerator
@pytest.mark.parametrize("device_fixture", ["da_ac_rac_000001"])
async def test_device(
async def test_config_entry_diagnostics(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
devices: AsyncMock,
mock_smartthings: AsyncMock,
mock_config_entry: MockConfigEntry,
snapshot: SnapshotAssertion,
) -> None:
"""Test generating diagnostics for a device entry."""
mock_smartthings.get_raw_devices.return_value = load_json_object_fixture(
"devices/da_ac_rac_000001.json", DOMAIN
)
await setup_integration(hass, mock_config_entry)
assert (
await get_diagnostics_for_config_entry(hass, hass_client, mock_config_entry)
== snapshot
)
@pytest.mark.parametrize("device_fixture", ["da_ac_rac_000001"])
async def test_device_diagnostics(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
device_registry: dr.DeviceRegistry,
@ -28,13 +51,19 @@ async def test_device(
snapshot: SnapshotAssertion,
) -> None:
"""Test generating diagnostics for a device entry."""
mock_smartthings.get_raw_device_status.return_value = load_json_object_fixture(
"device_status/da_ac_rac_000001.json", DOMAIN
)
mock_smartthings.get_raw_device.return_value = load_json_object_fixture(
"devices/da_ac_rac_000001.json", DOMAIN
)["items"][0]
await setup_integration(hass, mock_config_entry)
device = device_registry.async_get_device(
identifiers={(DOMAIN, "96a5ef74-5832-a84b-f1f7-ca799957065d")}
)
mock_smartthings.get_device_status.reset_mock()
mock_smartthings.get_raw_device_status.reset_mock()
with patch("homeassistant.components.smartthings.diagnostics.EVENT_WAIT_TIME", 0.1):
diag = await get_diagnostics_for_device(
@ -44,6 +73,6 @@ async def test_device(
assert diag == snapshot(
exclude=props("last_changed", "last_reported", "last_updated")
)
mock_smartthings.get_device_status.assert_called_once_with(
mock_smartthings.get_raw_device_status.assert_called_once_with(
"96a5ef74-5832-a84b-f1f7-ca799957065d"
)

View File

@ -2,14 +2,16 @@
from unittest.mock import AsyncMock
from pysmartthings import Attribute, Capability
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.smartthings import EVENT_BUTTON
from homeassistant.components.smartthings.const import DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.core import Event, HomeAssistant
from homeassistant.helpers import device_registry as dr
from . import setup_integration
from . import setup_integration, trigger_update
from tests.common import MockConfigEntry
@ -32,6 +34,35 @@ async def test_devices(
assert device == snapshot
@pytest.mark.parametrize("device_fixture", ["button"])
async def test_button_event(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
snapshot: SnapshotAssertion,
) -> None:
"""Test button event."""
await setup_integration(hass, mock_config_entry)
events = []
def capture_event(event: Event) -> None:
events.append(event)
hass.bus.async_listen_once(EVENT_BUTTON, capture_event)
await trigger_update(
hass,
devices,
"c4bdd19f-85d1-4d58-8f9c-e75ac3cf113b",
Capability.BUTTON,
Attribute.BUTTON,
"pushed",
)
assert len(events) == 1
assert events[0] == snapshot
@pytest.mark.parametrize("device_fixture", ["da_ac_rac_000001"])
async def test_removing_stale_devices(
hass: HomeAssistant,

View File

@ -1614,7 +1614,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
'state': 'p',
})
# ---
# name: test_sensors[sensor.test_speed-entry]