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 evohomeasync as ec1
import evohomeasync2 as ec2 import evohomeasync2 as ec2
from evohomeasync2.const import ( from evohomeasync2.const import (
SZ_DHW,
SZ_GATEWAY_ID, SZ_GATEWAY_ID,
SZ_GATEWAY_INFO, SZ_GATEWAY_INFO,
SZ_GATEWAYS, SZ_GATEWAYS,
@ -19,8 +20,9 @@ from evohomeasync2.const import (
SZ_TEMPERATURE_CONTROL_SYSTEMS, SZ_TEMPERATURE_CONTROL_SYSTEMS,
SZ_TIME_ZONE, SZ_TIME_ZONE,
SZ_USE_DAYLIGHT_SAVE_SWITCHING, 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.const import CONF_SCAN_INTERVAL
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -113,17 +115,19 @@ class EvoDataUpdateCoordinator(DataUpdateCoordinator):
SZ_USE_DAYLIGHT_SAVE_SWITCHING 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 = { gwy_info = {
SZ_GATEWAY_ID: self.loc.gateways[0].id, SZ_GATEWAY_ID: self.loc.gateways[0].id,
SZ_TEMPERATURE_CONTROL_SYSTEMS: [ SZ_TEMPERATURE_CONTROL_SYSTEMS: [tcs_info],
self.loc.gateways[0].systems[0].config
],
} }
config = { config = {
SZ_LOCATION_INFO: loc_info, SZ_LOCATION_INFO: loc_info,
SZ_GATEWAYS: [{SZ_GATEWAY_INFO: gwy_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( async def call_client_api(
self, self,
@ -203,10 +207,18 @@ class EvoDataUpdateCoordinator(DataUpdateCoordinator):
async def _update_v2_schedules(self) -> None: async def _update_v2_schedules(self) -> None:
for zone in self.tcs.zones: 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: 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] async def _async_update_data(self) -> EvoLocStatusResponseT: # type: ignore[override]
"""Fetch the latest state of an entire TCC Location. """Fetch the latest state of an entire TCC Location.

View File

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

View File

@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend", "documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system", "integration_type": "system",
"quality_scale": "internal", "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]] prompt_parts = [call.data[CONF_PROMPT]]
config_entry: GoogleGenerativeAIConfigEntry = hass.config_entries.async_entries( config_entry: GoogleGenerativeAIConfigEntry = (
DOMAIN hass.config_entries.async_loaded_entries(DOMAIN)[0]
)[0] )
client = config_entry.runtime_data client = config_entry.runtime_data

View File

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

View File

@ -386,6 +386,13 @@ class HomeConnectProgramSensor(HomeConnectSensor):
def update_native_value(self) -> None: def update_native_value(self) -> None:
"""Update the program sensor's status.""" """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)) event = self.appliance.events.get(cast(EventKey, self.bsh_key))
if event: if event:
self._update_native_value(event.value) self._update_native_value(event.value)

View File

@ -3,6 +3,7 @@
"step": { "step": {
"init": { "init": {
"title": "Pick Homematic IP access point", "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": { "data": {
"hapid": "Access point ID (SGTIN)", "hapid": "Access point ID (SGTIN)",
"pin": "[%key:common::config_flow::data::pin%]", "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 import sensor
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
CONF_STATE_CLASS, CONF_STATE_CLASS,
DEVICE_CLASS_UNITS,
DEVICE_CLASSES_SCHEMA, DEVICE_CLASSES_SCHEMA,
ENTITY_ID_FORMAT, ENTITY_ID_FORMAT,
STATE_CLASSES_SCHEMA, 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}'" 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 return config

View File

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

View File

@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
import logging import logging
from typing import TYPE_CHECKING, cast from typing import TYPE_CHECKING, cast
@ -11,6 +12,7 @@ from pysmartthings import (
Attribute, Attribute,
Capability, Capability,
Device, Device,
DeviceEvent,
Scene, Scene,
SmartThings, SmartThings,
SmartThingsAuthenticationFailedError, SmartThingsAuthenticationFailedError,
@ -28,7 +30,14 @@ from homeassistant.helpers.config_entry_oauth2_flow import (
async_get_config_entry_implementation, 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__) _LOGGER = logging.getLogger(__name__)
@ -114,6 +123,28 @@ async def async_setup_entry(hass: HomeAssistant, entry: SmartThingsConfigEntry)
scenes=scenes, 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( entry.async_create_background_task(
hass, hass,
client.subscribe( client.subscribe(
@ -160,26 +191,62 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return True 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( def process_status(
status: dict[str, dict[Capability | str, dict[Attribute | str, Status]]], status: dict[str, dict[Capability | str, dict[Attribute | str, Status]]],
) -> dict[str, dict[Capability | str, dict[Attribute | str, Status]]]: ) -> dict[str, dict[Capability | str, dict[Attribute | str, Status]]]:
"""Remove disabled capabilities from 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( disabled_capabilities_capability := main_component.get(
Capability.CUSTOM_DISABLED_CAPABILITIES Capability.CUSTOM_DISABLED_CAPABILITIES
) )
) is None: ) is not None:
return status disabled_capabilities = cast(
disabled_capabilities = cast( list[Capability | str],
list[Capability | str], disabled_capabilities_capability[Attribute.DISABLED_CAPABILITIES].value,
disabled_capabilities_capability[Attribute.DISABLED_CAPABILITIES].value, )
) if disabled_capabilities is not None:
if disabled_capabilities is not None: for capability in disabled_capabilities:
for capability in disabled_capabilities: if capability in main_component and (
# We still need to make sure the climate entity can work without this capability capability not in KEEP_CAPABILITY_QUIRK
if ( or not KEEP_CAPABILITY_QUIRK[capability](main_component[capability])
capability in main_component ):
and capability != Capability.DEMAND_RESPONSE_LOAD_CONTROL 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] del main_component[capability]
return status return status

View File

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

View File

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

View File

@ -17,6 +17,15 @@ from .const import DOMAIN
EVENT_WAIT_TIME = 5 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( async def async_get_device_diagnostics(
hass: HomeAssistant, entry: SmartThingsConfigEntry, device: DeviceEntry hass: HomeAssistant, entry: SmartThingsConfigEntry, device: DeviceEntry
) -> dict[str, Any]: ) -> dict[str, Any]:
@ -26,7 +35,8 @@ async def async_get_device_diagnostics(
identifier for identifier in device.identifiers if identifier[0] == DOMAIN identifier for identifier in device.identifiers if identifier[0] == DOMAIN
)[1] )[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] = [] events: list[DeviceEvent] = []
@ -39,11 +49,8 @@ async def async_get_device_diagnostics(
listener() listener()
status: dict[str, Any] = {} return {
for component, capabilities in device_status.items(): "events": [asdict(event) for event in events],
status[component] = {} "status": device_status,
for capability, attributes in capabilities.items(): "info": device_info,
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}

View File

@ -48,7 +48,9 @@ class SmartThingsEntity(Entity):
self._attr_device_info.update( self._attr_device_info.update(
{ {
"manufacturer": ocf.manufacturer_name, "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, "hw_version": ocf.hardware_version,
"sw_version": ocf.firmware_version, "sw_version": ocf.firmware_version,
} }

View File

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

View File

@ -29,5 +29,5 @@
"documentation": "https://www.home-assistant.io/integrations/smartthings", "documentation": "https://www.home-assistant.io/integrations/smartthings",
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["pysmartthings"], "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 = "." unique_id_separator: str = "."
capability_ignore_list: list[set[Capability]] | None = None capability_ignore_list: list[set[Capability]] | None = None
options_attribute: Attribute | None = None options_attribute: Attribute | None = None
except_if_state_none: bool = False
CAPABILITY_TO_SENSORS: dict[ CAPABILITY_TO_SENSORS: dict[
@ -581,7 +580,6 @@ CAPABILITY_TO_SENSORS: dict[
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value_fn=lambda value: value["energy"] / 1000, value_fn=lambda value: value["energy"] / 1000,
suggested_display_precision=2, suggested_display_precision=2,
except_if_state_none=True,
), ),
SmartThingsSensorEntityDescription( SmartThingsSensorEntityDescription(
key="power_meter", key="power_meter",
@ -591,7 +589,6 @@ CAPABILITY_TO_SENSORS: dict[
value_fn=lambda value: value["power"], value_fn=lambda value: value["power"],
extra_state_attributes_fn=power_attributes, extra_state_attributes_fn=power_attributes,
suggested_display_precision=2, suggested_display_precision=2,
except_if_state_none=True,
), ),
SmartThingsSensorEntityDescription( SmartThingsSensorEntityDescription(
key="deltaEnergy_meter", key="deltaEnergy_meter",
@ -601,7 +598,6 @@ CAPABILITY_TO_SENSORS: dict[
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value_fn=lambda value: value["deltaEnergy"] / 1000, value_fn=lambda value: value["deltaEnergy"] / 1000,
suggested_display_precision=2, suggested_display_precision=2,
except_if_state_none=True,
), ),
SmartThingsSensorEntityDescription( SmartThingsSensorEntityDescription(
key="powerEnergy_meter", key="powerEnergy_meter",
@ -611,7 +607,6 @@ CAPABILITY_TO_SENSORS: dict[
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value_fn=lambda value: value["powerEnergy"] / 1000, value_fn=lambda value: value["powerEnergy"] / 1000,
suggested_display_precision=2, suggested_display_precision=2,
except_if_state_none=True,
), ),
SmartThingsSensorEntityDescription( SmartThingsSensorEntityDescription(
key="energySaved_meter", key="energySaved_meter",
@ -621,7 +616,6 @@ CAPABILITY_TO_SENSORS: dict[
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
value_fn=lambda value: value["energySaved"] / 1000, value_fn=lambda value: value["energySaved"] / 1000,
suggested_display_precision=2, suggested_display_precision=2,
except_if_state_none=True,
), ),
] ]
}, },
@ -951,6 +945,7 @@ UNITS = {
"F": UnitOfTemperature.FAHRENHEIT, "F": UnitOfTemperature.FAHRENHEIT,
"lux": LIGHT_LUX, "lux": LIGHT_LUX,
"mG": None, "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 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", "iot_class": "cloud_push",
"loggers": ["snoo"], "loggers": ["snoo"],
"quality_scale": "bronze", "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, state_class=SensorStateClass.TOTAL,
device_class=SensorDeviceClass.DURATION, device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.SECONDS, native_unit_of_measurement=UnitOfTime.SECONDS,
suggested_unit_of_measurement=UnitOfTime.HOURS,
), ),
SensorEntityDescription( SensorEntityDescription(
key=STATUS_SENSOR_INFO_TOTAL_GENRES, key=STATUS_SENSOR_INFO_TOTAL_GENRES,

View File

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

View File

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

View File

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

View File

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

View File

@ -54,5 +54,5 @@
"dependencies": ["bluetooth_adapters"], "dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/thermobeacon", "documentation": "https://www.home-assistant.io/integrations/thermobeacon",
"iot_class": "local_push", "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(), await open_stream(),
f"{self._backup_path}/{filename_tar}", f"{self._backup_path}/{filename_tar}",
timeout=BACKUP_TIMEOUT, timeout=BACKUP_TIMEOUT,
content_length=backup.size,
) )
_LOGGER.debug( _LOGGER.debug(

View File

@ -8,5 +8,5 @@
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["aiowebdav2"], "loggers": ["aiowebdav2"],
"quality_scale": "bronze", "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" APPLICATION_NAME: Final = "HomeAssistant"
MAJOR_VERSION: Final = 2025 MAJOR_VERSION: Final = 2025
MINOR_VERSION: Final = 3 MINOR_VERSION: Final = 3
PATCH_VERSION: Final = "0" PATCH_VERSION: Final = "1"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 13, 0) 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 hass-nabucasa==0.92.0
hassil==2.2.3 hassil==2.2.3
home-assistant-bluetooth==1.13.1 home-assistant-bluetooth==1.13.1
home-assistant-frontend==20250305.0 home-assistant-frontend==20250306.0
home-assistant-intents==2025.3.5 home-assistant-intents==2025.3.5
httpx==0.28.1 httpx==0.28.1
ifaddr==0.2.0 ifaddr==0.2.0

View File

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

16
requirements_all.txt generated
View File

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

View File

@ -249,7 +249,7 @@ aioharmony==0.4.1
aiohasupervisor==0.3.0 aiohasupervisor==0.3.0
# homeassistant.components.home_connect # homeassistant.components.home_connect
aiohomeconnect==0.16.2 aiohomeconnect==0.16.3
# homeassistant.components.homekit_controller # homeassistant.components.homekit_controller
aiohomekit==3.2.8 aiohomekit==3.2.8
@ -404,7 +404,7 @@ aiowaqi==3.1.0
aiowatttime==0.1.1 aiowatttime==0.1.1
# homeassistant.components.webdav # homeassistant.components.webdav
aiowebdav2==0.3.1 aiowebdav2==0.4.1
# homeassistant.components.webostv # homeassistant.components.webostv
aiowebostv==0.7.3 aiowebostv==0.7.3
@ -981,7 +981,7 @@ hole==0.8.0
holidays==0.68 holidays==0.68
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20250305.0 home-assistant-frontend==20250306.0
# homeassistant.components.conversation # homeassistant.components.conversation
home-assistant-intents==2025.3.5 home-assistant-intents==2025.3.5
@ -1246,7 +1246,7 @@ netmap==0.7.0.2
nettigo-air-monitor==4.0.0 nettigo-air-monitor==4.0.0
# homeassistant.components.nexia # homeassistant.components.nexia
nexia==2.2.1 nexia==2.2.2
# homeassistant.components.nextcloud # homeassistant.components.nextcloud
nextcloudmonitor==1.5.1 nextcloudmonitor==1.5.1
@ -1453,7 +1453,7 @@ py-nightscout==1.2.2
py-sucks==0.9.10 py-sucks==0.9.10
# homeassistant.components.synology_dsm # homeassistant.components.synology_dsm
py-synologydsm-api==2.7.0 py-synologydsm-api==2.7.1
# homeassistant.components.hdmi_cec # homeassistant.components.hdmi_cec
pyCEC==0.5.2 pyCEC==0.5.2
@ -1882,7 +1882,7 @@ pysma==0.7.5
pysmappee==0.2.29 pysmappee==0.2.29
# homeassistant.components.smartthings # homeassistant.components.smartthings
pysmartthings==2.5.0 pysmartthings==2.7.0
# homeassistant.components.smarty # homeassistant.components.smarty
pysmarty2==0.10.2 pysmarty2==0.10.2
@ -2000,7 +2000,7 @@ python-roborock==2.11.1
python-smarttub==0.0.39 python-smarttub==0.0.39
# homeassistant.components.snoo # homeassistant.components.snoo
python-snoo==0.6.0 python-snoo==0.6.1
# homeassistant.components.songpal # homeassistant.components.songpal
python-songpal==0.16.2 python-songpal==0.16.2
@ -2327,7 +2327,7 @@ teslemetry-stream==0.6.10
tessie-api==0.1.1 tessie-api==0.1.1
# homeassistant.components.thermobeacon # homeassistant.components.thermobeacon
thermobeacon-ble==0.8.0 thermobeacon-ble==0.8.1
# homeassistant.components.thermopro # homeassistant.components.thermopro
thermopro-ble==0.11.0 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) 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.""" """Load JSON for the schedule of a domesticHotWater zone."""
try: 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: except FileNotFoundError:
return load_json_object_fixture("default/schedule_dhw.json", DOMAIN) 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.""" """Load JSON for the schedule of a temperatureZone zone."""
try: 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: except FileNotFoundError:
return load_json_object_fixture("default/schedule_zone.json", DOMAIN) 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: elif "schedule" in url:
if url.startswith("domesticHotWater"): # /v2/domesticHotWater/{id}/schedule 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 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}") pytest.fail(f"Unexpected request: {HTTPMethod.GET} {url}")

View File

@ -15,8 +15,9 @@ TEST_INSTALLS: Final = (
"default", # evohome: multi-zone, with DHW "default", # evohome: multi-zone, with DHW
"h032585", # VisionProWifi: no preset modes for TCS, zoneId=systemId "h032585", # VisionProWifi: no preset modes for TCS, zoneId=systemId
"h099625", # RoundThermostat "h099625", # RoundThermostat
"h139906", # zone with null schedule
"sys_004", # RoundModulation "sys_004", # RoundModulation
) )
# "botched", # as default: but with activeFaults, ghost zones & unknown types # "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] # name: test_ctl_set_hvac_mode[minimal]
list([ list([
tuple( tuple(
@ -70,6 +80,13 @@
), ),
]) ])
# --- # ---
# name: test_ctl_turn_off[h139906]
list([
tuple(
<SystemMode.HEATING_OFF: 'HeatingOff'>,
),
])
# ---
# name: test_ctl_turn_off[minimal] # name: test_ctl_turn_off[minimal]
list([ list([
tuple( tuple(
@ -105,6 +122,13 @@
), ),
]) ])
# --- # ---
# name: test_ctl_turn_on[h139906]
list([
tuple(
<SystemMode.AUTO: 'Auto'>,
),
])
# ---
# name: test_ctl_turn_on[minimal] # name: test_ctl_turn_on[minimal]
list([ list([
tuple( tuple(
@ -1118,6 +1142,136 @@
'state': 'heat', '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] # name: test_setup_platform[minimal][climate.main_room-state]
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
@ -1312,6 +1466,13 @@
), ),
]) ])
# --- # ---
# name: test_zone_set_hvac_mode[h139906]
list([
tuple(
5.0,
),
])
# ---
# name: test_zone_set_hvac_mode[minimal] # name: test_zone_set_hvac_mode[minimal]
list([ list([
tuple( 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] # name: test_zone_set_preset_mode[minimal]
list([ list([
tuple( tuple(
@ -1412,6 +1586,13 @@
}), }),
]) ])
# --- # ---
# name: test_zone_set_temperature[h139906]
list([
dict({
'until': None,
}),
])
# ---
# name: test_zone_set_temperature[minimal] # name: test_zone_set_temperature[minimal]
list([ list([
dict({ dict({
@ -1447,6 +1628,13 @@
), ),
]) ])
# --- # ---
# name: test_zone_turn_off[h139906]
list([
tuple(
5.0,
),
])
# ---
# name: test_zone_turn_off[minimal] # name: test_zone_turn_off[minimal]
list([ list([
tuple( tuple(

View File

@ -11,6 +11,9 @@
# name: test_setup[h099625] # name: test_setup[h099625]
dict_keys(['refresh_system', 'set_system_mode', 'clear_zone_override', 'set_zone_override']) 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] # name: test_setup[minimal]
dict_keys(['refresh_system', 'reset_system', 'set_system_mode', 'clear_zone_override', 'set_zone_override']) dict_keys(['refresh_system', 'reset_system', 'set_system_mode', 'clear_zone_override', 'set_zone_override'])
# --- # ---

View File

@ -1,4 +1,14 @@
# serializer version: 1 # 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] # name: test_set_operation_mode[default]
list([ list([
dict({ dict({

View File

@ -33,7 +33,7 @@ from .const import TEST_INSTALLS_WITH_DHW
DHW_ENTITY_ID = "water_heater.domestic_hot_water" 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( async def test_setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
config: dict[str, str], config: dict[str, str],

View File

@ -6,7 +6,7 @@
tuple( tuple(
), ),
dict({ 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([ 'history': list([
]), ]),
'model': 'models/gemini-2.0-flash', '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", "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"]},
{"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", "format": "hex"},
{"type": "NUMBER"}, {"type": "NUMBER"},
), ),
(
{"type": "number", "minimum": 1},
{"type": "NUMBER"},
),
( (
{"type": "integer", "format": "int32"}, {"type": "integer", "format": "int32"},
{"type": "INTEGER", "format": "int32"}, {"type": "INTEGER", "format": "int32"},
@ -535,21 +559,7 @@ async def test_escape_decode() -> None:
), ),
( (
{"anyOf": [{"type": "integer"}, {"type": "number"}]}, {"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": "string", "format": "lower"}, {"type": "STRING"}),
({"type": "boolean", "format": "bool"}, {"type": "BOOLEAN"}), ({"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", "type": "OBJECT",
"properties": {"json": {"type": "STRING"}}, "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"}}, {"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: 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() await hass.async_block_till_done()
assert mock_config_entry.state == state assert mock_config_entry.state == state
assert any(mock_config_entry.async_get_active_flows(hass, {"reauth"})) == reauth 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, DOMAIN,
) )
from homeassistant.config_entries import ConfigEntryState 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.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er 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, client: MagicMock,
appliance_ha_id: str, appliance_ha_id: str,
states: tuple, states: tuple,
@ -313,7 +313,7 @@ async def test_event_sensors(
integration_setup: Callable[[MagicMock], Awaitable[bool]], integration_setup: Callable[[MagicMock], Awaitable[bool]],
setup_credentials: None, setup_credentials: None,
) -> 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() entity_ids = ENTITY_ID_STATES.keys()
time_to_freeze = "2021-01-09 12:00:00+00:00" 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) 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 for SensorDeviceClass.TIMESTAMP edge cases.
PROGRAM_SEQUENCE_EDGE_CASE = [ PROGRAM_SEQUENCE_EDGE_CASE = [
EVENT_PROG_DELAYED_START, 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 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( @pytest.mark.parametrize(
"hass_config", "hass_config",
[ [

View File

@ -68,6 +68,8 @@ async def trigger_update(
value, value,
data, 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: for call in mock.add_device_event_listener.call_args_list:
if call[0][0] == device_id: if call[0][0] == device_id:
call[0][3](event) call[0][3](event)

View File

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

View File

@ -32,7 +32,7 @@
"timestamp": "2025-02-09T14:35:56.800Z" "timestamp": "2025-02-09T14:35:56.800Z"
}, },
"supportedAcModes": { "supportedAcModes": {
"value": ["auto", "cool", "dry", "wind", "heat"], "value": ["auto", "cool", "dry", "wind", "heat", "dryClean"],
"timestamp": "2025-02-09T15:42:13.444Z" "timestamp": "2025-02-09T15:42:13.444Z"
}, },
"airConditionerMode": { "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" "id": "60fbc713-8da5-315d-b31a-6d6dcde4be7b"
}, },
"ocf": { "ocf": {
"ocfDeviceType": "oic.d.airconditioner", "ocfDeviceType": "x.com.st.d.sensor.light",
"name": "[room a/c] Samsung",
"specVersion": "core.1.1.0",
"verticalDomainSpecVersion": "res.1.1.0,sh.1.1.0",
"manufacturerName": "Samsung Electronics", "manufacturerName": "Samsung Electronics",
"modelNumber": "ARTIK051_KRAC_18K|10193441|60010132001111110200000000000000", "vendorId": "VD-Sensor.Light-2023",
"platformVersion": "0G3MPDCKA00010E", "lastSignupTime": "2025-01-08T02:32:04.631093137Z",
"platformOS": "TizenRT2.0",
"hwVersion": "1.0",
"firmwareVersion": "0.1.0",
"vendorId": "DA-AC-RAC-000001",
"lastSignupTime": "2021-04-06T16:43:27.889445Z",
"transferCandidate": false, "transferCandidate": false,
"additionalAuthCodeRequired": 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', '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] # name: test_all_entities[ecobee_thermostat][climate.main_floor-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
@ -285,6 +369,70 @@
'state': 'heat', '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] # name: test_all_entities[virtual_thermostat][climate.asd-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({

File diff suppressed because it is too large Load Diff

View File

@ -62,6 +62,60 @@
'last_changed': <ANY>, 'last_changed': <ANY>,
'last_reported': <ANY>, 'last_reported': <ANY>,
'last_updated': <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 # 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] # name: test_devices[aeotec_home_energy_meter_gen5]
DeviceRegistryEntrySnapshot({ DeviceRegistryEntrySnapshot({
'area_id': None, 'area_id': None,
@ -207,7 +210,7 @@
}), }),
'disabled_by': None, 'disabled_by': None,
'entry_type': None, 'entry_type': None,
'hw_version': '1.0', 'hw_version': None,
'id': <ANY>, 'id': <ANY>,
'identifiers': set({ 'identifiers': set({
tuple( tuple(
@ -219,14 +222,14 @@
'labels': set({ 'labels': set({
}), }),
'manufacturer': 'Samsung Electronics', 'manufacturer': 'Samsung Electronics',
'model': 'ARTIK051_KRAC_18K', 'model': None,
'model_id': None, 'model_id': None,
'name': 'AC Office Granit', 'name': 'AC Office Granit',
'name_by_user': None, 'name_by_user': None,
'primary_config_entry': <ANY>, 'primary_config_entry': <ANY>,
'serial_number': None, 'serial_number': None,
'suggested_area': None, 'suggested_area': None,
'sw_version': '0.1.0', 'sw_version': None,
'via_device_id': None, 'via_device_id': None,
}) })
# --- # ---
@ -263,6 +266,39 @@
'via_device_id': None, '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] # name: test_devices[da_ks_microwave_0101x]
DeviceRegistryEntrySnapshot({ DeviceRegistryEntrySnapshot({
'area_id': None, 'area_id': None,
@ -461,6 +497,39 @@
'via_device_id': None, '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] # name: test_devices[ecobee_sensor]
DeviceRegistryEntrySnapshot({ DeviceRegistryEntrySnapshot({
'area_id': None, 'area_id': None,
@ -593,6 +662,72 @@
'via_device_id': None, '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] # name: test_devices[hue_color_temperature_bulb]
DeviceRegistryEntrySnapshot({ DeviceRegistryEntrySnapshot({
'area_id': None, 'area_id': None,

View File

@ -1379,6 +1379,213 @@
'state': '0', '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] # name: test_all_entities[da_ks_microwave_0101x][sensor.microwave_completion_time-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
@ -3692,6 +3899,475 @@
'state': '0.0', '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] # name: test_all_entities[ecobee_sensor][sensor.child_bedroom_temperature-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({
@ -3848,6 +4524,162 @@
'state': '22', '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] # name: test_all_entities[multipurpose_sensor][sensor.deck_door_battery-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({

View File

@ -281,6 +281,53 @@
'state': 'off', '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] # name: test_all_entities[sensibo_airconditioner_1][switch.office-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({

View File

@ -12,13 +12,36 @@ from homeassistant.helpers import device_registry as dr
from . import setup_integration from . import setup_integration
from tests.common import MockConfigEntry from tests.common import MockConfigEntry, load_json_object_fixture
from tests.components.diagnostics import get_diagnostics_for_device from tests.components.diagnostics import (
get_diagnostics_for_config_entry,
get_diagnostics_for_device,
)
from tests.typing import ClientSessionGenerator from tests.typing import ClientSessionGenerator
@pytest.mark.parametrize("device_fixture", ["da_ac_rac_000001"]) @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: HomeAssistant,
hass_client: ClientSessionGenerator, hass_client: ClientSessionGenerator,
device_registry: dr.DeviceRegistry, device_registry: dr.DeviceRegistry,
@ -28,13 +51,19 @@ async def test_device(
snapshot: SnapshotAssertion, snapshot: SnapshotAssertion,
) -> None: ) -> None:
"""Test generating diagnostics for a device entry.""" """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) await setup_integration(hass, mock_config_entry)
device = device_registry.async_get_device( device = device_registry.async_get_device(
identifiers={(DOMAIN, "96a5ef74-5832-a84b-f1f7-ca799957065d")} 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): with patch("homeassistant.components.smartthings.diagnostics.EVENT_WAIT_TIME", 0.1):
diag = await get_diagnostics_for_device( diag = await get_diagnostics_for_device(
@ -44,6 +73,6 @@ async def test_device(
assert diag == snapshot( assert diag == snapshot(
exclude=props("last_changed", "last_reported", "last_updated") 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" "96a5ef74-5832-a84b-f1f7-ca799957065d"
) )

View File

@ -2,14 +2,16 @@
from unittest.mock import AsyncMock from unittest.mock import AsyncMock
from pysmartthings import Attribute, Capability
import pytest import pytest
from syrupy import SnapshotAssertion from syrupy import SnapshotAssertion
from homeassistant.components.smartthings import EVENT_BUTTON
from homeassistant.components.smartthings.const import DOMAIN 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 homeassistant.helpers import device_registry as dr
from . import setup_integration from . import setup_integration, trigger_update
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -32,6 +34,35 @@ async def test_devices(
assert device == snapshot 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"]) @pytest.mark.parametrize("device_fixture", ["da_ac_rac_000001"])
async def test_removing_stale_devices( async def test_removing_stale_devices(
hass: HomeAssistant, hass: HomeAssistant,

View File

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