mirror of
https://github.com/home-assistant/core.git
synced 2026-03-22 02:35:12 +01:00
Compare commits
16 Commits
esphome-no
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dcbc1d5d9 | ||
|
|
3068653cc7 | ||
|
|
61b1a45889 | ||
|
|
573d4eba02 | ||
|
|
09895aa601 | ||
|
|
aa6a4c7eab | ||
|
|
662c44b125 | ||
|
|
5a80087cf4 | ||
|
|
c28dc32168 | ||
|
|
eef3472c43 | ||
|
|
f9bd9f4982 | ||
|
|
e4620a208d | ||
|
|
c6c5661b4b | ||
|
|
d0154e5019 | ||
|
|
16fb7ed21e | ||
|
|
d0a751abe4 |
@@ -28,7 +28,6 @@ from homeassistant.config_entries import (
|
||||
SOURCE_REAUTH,
|
||||
SOURCE_RECONFIGURE,
|
||||
ConfigEntry,
|
||||
ConfigEntryState,
|
||||
ConfigFlow,
|
||||
ConfigFlowResult,
|
||||
FlowType,
|
||||
@@ -364,11 +363,6 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
# Don't probe to verify the mac is correct since
|
||||
# the host matches (and port matches if provided).
|
||||
raise AbortFlow("already_configured")
|
||||
# If the entry is loaded and the device is currently connected,
|
||||
# don't update the host. This prevents transient mDNS announcements
|
||||
# (e.g., during WiFi mesh roaming) from overwriting a working connection.
|
||||
if entry.state is ConfigEntryState.LOADED and entry.runtime_data.available:
|
||||
raise AbortFlow("already_configured")
|
||||
configured_psk: str | None = entry.data.get(CONF_NOISE_PSK)
|
||||
await self._fetch_device_info(host, port or configured_port, configured_psk)
|
||||
updates: dict[str, Any] = {}
|
||||
|
||||
@@ -13,6 +13,7 @@ from fritzconnection.core.exceptions import (
|
||||
FritzSecurityError,
|
||||
FritzServiceError,
|
||||
)
|
||||
from requests.exceptions import ConnectionError
|
||||
|
||||
from homeassistant.const import Platform
|
||||
|
||||
@@ -68,6 +69,7 @@ BUTTON_TYPE_WOL = "WakeOnLan"
|
||||
UPTIME_DEVIATION = 5
|
||||
|
||||
FRITZ_EXCEPTIONS = (
|
||||
ConnectionError,
|
||||
FritzActionError,
|
||||
FritzActionFailedError,
|
||||
FritzConnectionException,
|
||||
|
||||
44
homeassistant/components/google_weather/diagnostics.py
Normal file
44
homeassistant/components/google_weather/diagnostics.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""Diagnostics support for Google Weather."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import CONF_REFERRER
|
||||
from .coordinator import GoogleWeatherConfigEntry
|
||||
|
||||
TO_REDACT = {
|
||||
CONF_API_KEY,
|
||||
CONF_REFERRER,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
}
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: GoogleWeatherConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
diag_data: dict[str, Any] = {
|
||||
"entry": entry.as_dict(),
|
||||
"subentries": {},
|
||||
}
|
||||
|
||||
for subentry_id, subentry_rt in entry.runtime_data.subentries_runtime_data.items():
|
||||
diag_data["subentries"][subentry_id] = {
|
||||
"observation_data": subentry_rt.coordinator_observation.data.to_dict()
|
||||
if subentry_rt.coordinator_observation.data
|
||||
else None,
|
||||
"daily_forecast_data": subentry_rt.coordinator_daily_forecast.data.to_dict()
|
||||
if subentry_rt.coordinator_daily_forecast.data
|
||||
else None,
|
||||
"hourly_forecast_data": subentry_rt.coordinator_hourly_forecast.data.to_dict()
|
||||
if subentry_rt.coordinator_hourly_forecast.data
|
||||
else None,
|
||||
}
|
||||
|
||||
return async_redact_data(diag_data, TO_REDACT)
|
||||
@@ -8,5 +8,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["google_weather_api"],
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["python-google-weather-api==0.0.4"]
|
||||
"requirements": ["python-google-weather-api==0.0.6"]
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ rules:
|
||||
|
||||
# Gold
|
||||
devices: done
|
||||
diagnostics: todo
|
||||
diagnostics: done
|
||||
discovery-update-info:
|
||||
status: exempt
|
||||
comment: No discovery.
|
||||
|
||||
@@ -114,24 +114,26 @@ class KnxYamlBinarySensor(_KnxBinarySensor, KnxYamlEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize of KNX binary sensor."""
|
||||
self._device = XknxBinarySensor(
|
||||
xknx=knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address_state=config[CONF_STATE_ADDRESS],
|
||||
invert=config[CONF_INVERT],
|
||||
sync_state=config[CONF_SYNC_STATE],
|
||||
ignore_internal_state=config[CONF_IGNORE_INTERNAL_STATE],
|
||||
context_timeout=config.get(CONF_CONTEXT_TIMEOUT),
|
||||
reset_after=config.get(CONF_RESET_AFTER),
|
||||
always_callback=True,
|
||||
)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=XknxBinarySensor(
|
||||
xknx=knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address_state=config[CONF_STATE_ADDRESS],
|
||||
invert=config[CONF_INVERT],
|
||||
sync_state=config[CONF_SYNC_STATE],
|
||||
ignore_internal_state=config[CONF_IGNORE_INTERNAL_STATE],
|
||||
context_timeout=config.get(CONF_CONTEXT_TIMEOUT),
|
||||
reset_after=config.get(CONF_RESET_AFTER),
|
||||
always_callback=True,
|
||||
),
|
||||
unique_id=str(self._device.remote_value.group_address_state),
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
|
||||
self._attr_device_class = config.get(CONF_DEVICE_CLASS)
|
||||
self._attr_force_update = self._device.ignore_internal_state
|
||||
self._attr_unique_id = str(self._device.remote_value.group_address_state)
|
||||
|
||||
|
||||
class KnxUiBinarySensor(_KnxBinarySensor, KnxUiEntity):
|
||||
|
||||
@@ -35,19 +35,18 @@ class KNXButton(KnxYamlEntity, ButtonEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize a KNX button."""
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=XknxRawValue(
|
||||
xknx=knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
payload_length=config[CONF_PAYLOAD_LENGTH],
|
||||
group_address=config[KNX_ADDRESS],
|
||||
),
|
||||
self._device = XknxRawValue(
|
||||
xknx=knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
payload_length=config[CONF_PAYLOAD_LENGTH],
|
||||
group_address=config[KNX_ADDRESS],
|
||||
)
|
||||
self._payload = config[CONF_PAYLOAD]
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_unique_id = (
|
||||
f"{self._device.remote_value.group_address}_{self._payload}"
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
unique_id=f"{self._device.remote_value.group_address}_{self._payload}",
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
|
||||
async def async_press(self) -> None:
|
||||
|
||||
@@ -119,7 +119,7 @@ async def async_setup_entry(
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
def _create_climate(xknx: XKNX, config: ConfigType) -> XknxClimate:
|
||||
def _create_climate_yaml(xknx: XKNX, config: ConfigType) -> XknxClimate:
|
||||
"""Return a KNX Climate device to be used within XKNX."""
|
||||
climate_mode = XknxClimateMode(
|
||||
xknx,
|
||||
@@ -646,9 +646,17 @@ class KnxYamlClimate(_KnxClimate, KnxYamlEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize of a KNX climate device."""
|
||||
self._device = _create_climate_yaml(knx_module.xknx, config)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=_create_climate(knx_module.xknx, config),
|
||||
unique_id=(
|
||||
f"{self._device.temperature.group_address_state}_"
|
||||
f"{self._device.target_temperature.group_address_state}_"
|
||||
f"{self._device.target_temperature.group_address}_"
|
||||
f"{self._device._setpoint_shift.group_address}" # noqa: SLF001
|
||||
),
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
default_hvac_mode: HVACMode = config[ClimateConf.DEFAULT_CONTROLLER_MODE]
|
||||
fan_max_step = config[ClimateConf.FAN_MAX_STEP]
|
||||
@@ -660,14 +668,6 @@ class KnxYamlClimate(_KnxClimate, KnxYamlEntity):
|
||||
fan_zero_mode=fan_zero_mode,
|
||||
)
|
||||
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_unique_id = (
|
||||
f"{self._device.temperature.group_address_state}_"
|
||||
f"{self._device.target_temperature.group_address_state}_"
|
||||
f"{self._device.target_temperature.group_address}_"
|
||||
f"{self._device._setpoint_shift.group_address}" # noqa: SLF001
|
||||
)
|
||||
|
||||
|
||||
class KnxUiClimate(_KnxClimate, KnxUiEntity):
|
||||
"""Representation of a KNX climate device configured from the UI."""
|
||||
|
||||
@@ -191,36 +191,34 @@ class KnxYamlCover(_KnxCover, KnxYamlEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize the cover."""
|
||||
self._device = XknxCover(
|
||||
xknx=knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address_long=config.get(CoverSchema.CONF_MOVE_LONG_ADDRESS),
|
||||
group_address_short=config.get(CoverSchema.CONF_MOVE_SHORT_ADDRESS),
|
||||
group_address_stop=config.get(CoverSchema.CONF_STOP_ADDRESS),
|
||||
group_address_position_state=config.get(
|
||||
CoverSchema.CONF_POSITION_STATE_ADDRESS
|
||||
),
|
||||
group_address_angle=config.get(CoverSchema.CONF_ANGLE_ADDRESS),
|
||||
group_address_angle_state=config.get(CoverSchema.CONF_ANGLE_STATE_ADDRESS),
|
||||
group_address_position=config.get(CoverSchema.CONF_POSITION_ADDRESS),
|
||||
travel_time_down=config[CoverConf.TRAVELLING_TIME_DOWN],
|
||||
travel_time_up=config[CoverConf.TRAVELLING_TIME_UP],
|
||||
invert_updown=config[CoverConf.INVERT_UPDOWN],
|
||||
invert_position=config[CoverConf.INVERT_POSITION],
|
||||
invert_angle=config[CoverConf.INVERT_ANGLE],
|
||||
)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=XknxCover(
|
||||
xknx=knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address_long=config.get(CoverSchema.CONF_MOVE_LONG_ADDRESS),
|
||||
group_address_short=config.get(CoverSchema.CONF_MOVE_SHORT_ADDRESS),
|
||||
group_address_stop=config.get(CoverSchema.CONF_STOP_ADDRESS),
|
||||
group_address_position_state=config.get(
|
||||
CoverSchema.CONF_POSITION_STATE_ADDRESS
|
||||
),
|
||||
group_address_angle=config.get(CoverSchema.CONF_ANGLE_ADDRESS),
|
||||
group_address_angle_state=config.get(
|
||||
CoverSchema.CONF_ANGLE_STATE_ADDRESS
|
||||
),
|
||||
group_address_position=config.get(CoverSchema.CONF_POSITION_ADDRESS),
|
||||
travel_time_down=config[CoverConf.TRAVELLING_TIME_DOWN],
|
||||
travel_time_up=config[CoverConf.TRAVELLING_TIME_UP],
|
||||
invert_updown=config[CoverConf.INVERT_UPDOWN],
|
||||
invert_position=config[CoverConf.INVERT_POSITION],
|
||||
invert_angle=config[CoverConf.INVERT_ANGLE],
|
||||
unique_id=(
|
||||
f"{self._device.updown.group_address}_"
|
||||
f"{self._device.position_target.group_address}"
|
||||
),
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
self.init_base()
|
||||
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_unique_id = (
|
||||
f"{self._device.updown.group_address}_"
|
||||
f"{self._device.position_target.group_address}"
|
||||
)
|
||||
if custom_device_class := config.get(CONF_DEVICE_CLASS):
|
||||
self._attr_device_class = custom_device_class
|
||||
|
||||
|
||||
@@ -105,20 +105,21 @@ class KnxYamlDate(_KNXDate, KnxYamlEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize a KNX date."""
|
||||
self._device = XknxDateDevice(
|
||||
knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
localtime=False,
|
||||
group_address=config[KNX_ADDRESS],
|
||||
group_address_state=config.get(CONF_STATE_ADDRESS),
|
||||
respond_to_read=config[CONF_RESPOND_TO_READ],
|
||||
sync_state=config[CONF_SYNC_STATE],
|
||||
)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=XknxDateDevice(
|
||||
knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
localtime=False,
|
||||
group_address=config[KNX_ADDRESS],
|
||||
group_address_state=config.get(CONF_STATE_ADDRESS),
|
||||
respond_to_read=config[CONF_RESPOND_TO_READ],
|
||||
sync_state=config[CONF_SYNC_STATE],
|
||||
),
|
||||
unique_id=str(self._device.remote_value.group_address),
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_unique_id = str(self._device.remote_value.group_address)
|
||||
|
||||
|
||||
class KnxUiDate(_KNXDate, KnxUiEntity):
|
||||
|
||||
@@ -110,20 +110,21 @@ class KnxYamlDateTime(_KNXDateTime, KnxYamlEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize a KNX datetime."""
|
||||
self._device = XknxDateTimeDevice(
|
||||
knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
localtime=False,
|
||||
group_address=config[KNX_ADDRESS],
|
||||
group_address_state=config.get(CONF_STATE_ADDRESS),
|
||||
respond_to_read=config[CONF_RESPOND_TO_READ],
|
||||
sync_state=config[CONF_SYNC_STATE],
|
||||
)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=XknxDateTimeDevice(
|
||||
knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
localtime=False,
|
||||
group_address=config[KNX_ADDRESS],
|
||||
group_address_state=config.get(CONF_STATE_ADDRESS),
|
||||
respond_to_read=config[CONF_RESPOND_TO_READ],
|
||||
sync_state=config[CONF_SYNC_STATE],
|
||||
),
|
||||
unique_id=str(self._device.remote_value.group_address),
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_unique_id = str(self._device.remote_value.group_address)
|
||||
|
||||
|
||||
class KnxUiDateTime(_KNXDateTime, KnxUiEntity):
|
||||
|
||||
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any
|
||||
|
||||
from xknx.devices import Device as XknxDevice
|
||||
|
||||
from homeassistant.const import CONF_ENTITY_CATEGORY, EntityCategory
|
||||
from homeassistant.const import CONF_ENTITY_CATEGORY, CONF_NAME, EntityCategory
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_platform import EntityPlatform
|
||||
@@ -52,14 +52,11 @@ class _KnxEntityBase(Entity):
|
||||
"""Representation of a KNX entity."""
|
||||
|
||||
_attr_should_poll = False
|
||||
|
||||
_attr_unique_id: str
|
||||
_knx_module: KNXModule
|
||||
_device: XknxDevice
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name of the KNX device."""
|
||||
return self._device.name
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if entity is available."""
|
||||
@@ -100,16 +97,23 @@ class _KnxEntityBase(Entity):
|
||||
class KnxYamlEntity(_KnxEntityBase):
|
||||
"""Representation of a KNX entity configured from YAML."""
|
||||
|
||||
def __init__(self, knx_module: KNXModule, device: XknxDevice) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
knx_module: KNXModule,
|
||||
unique_id: str,
|
||||
name: str,
|
||||
entity_category: EntityCategory | None,
|
||||
) -> None:
|
||||
"""Initialize the YAML entity."""
|
||||
self._knx_module = knx_module
|
||||
self._device = device
|
||||
self._attr_name = name or None
|
||||
self._attr_unique_id = unique_id
|
||||
self._attr_entity_category = entity_category
|
||||
|
||||
|
||||
class KnxUiEntity(_KnxEntityBase):
|
||||
"""Representation of a KNX UI entity."""
|
||||
|
||||
_attr_unique_id: str
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
@@ -117,6 +121,8 @@ class KnxUiEntity(_KnxEntityBase):
|
||||
) -> None:
|
||||
"""Initialize the UI entity."""
|
||||
self._knx_module = knx_module
|
||||
|
||||
self._attr_name = entity_config[CONF_NAME]
|
||||
self._attr_unique_id = unique_id
|
||||
if entity_category := entity_config.get(CONF_ENTITY_CATEGORY):
|
||||
self._attr_entity_category = EntityCategory(entity_category)
|
||||
|
||||
@@ -208,35 +208,32 @@ class KnxYamlFan(_KnxFan, KnxYamlEntity):
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize of KNX fan."""
|
||||
max_step = config.get(FanConf.MAX_STEP)
|
||||
self._device = XknxFan(
|
||||
xknx=knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address_speed=config.get(KNX_ADDRESS),
|
||||
group_address_speed_state=config.get(FanSchema.CONF_STATE_ADDRESS),
|
||||
group_address_oscillation=config.get(FanSchema.CONF_OSCILLATION_ADDRESS),
|
||||
group_address_oscillation_state=config.get(
|
||||
FanSchema.CONF_OSCILLATION_STATE_ADDRESS
|
||||
),
|
||||
group_address_switch=config.get(FanSchema.CONF_SWITCH_ADDRESS),
|
||||
group_address_switch_state=config.get(FanSchema.CONF_SWITCH_STATE_ADDRESS),
|
||||
max_step=max_step,
|
||||
sync_state=config.get(CONF_SYNC_STATE, True),
|
||||
)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=XknxFan(
|
||||
xknx=knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address_speed=config.get(KNX_ADDRESS),
|
||||
group_address_speed_state=config.get(FanSchema.CONF_STATE_ADDRESS),
|
||||
group_address_oscillation=config.get(
|
||||
FanSchema.CONF_OSCILLATION_ADDRESS
|
||||
),
|
||||
group_address_oscillation_state=config.get(
|
||||
FanSchema.CONF_OSCILLATION_STATE_ADDRESS
|
||||
),
|
||||
group_address_switch=config.get(FanSchema.CONF_SWITCH_ADDRESS),
|
||||
group_address_switch_state=config.get(
|
||||
FanSchema.CONF_SWITCH_STATE_ADDRESS
|
||||
),
|
||||
max_step=max_step,
|
||||
sync_state=config.get(CONF_SYNC_STATE, True),
|
||||
unique_id=(
|
||||
str(self._device.speed.group_address)
|
||||
if self._device.speed.group_address
|
||||
else str(self._device.switch.group_address)
|
||||
),
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
# FanSpeedMode.STEP if max_step is set
|
||||
self._step_range: tuple[int, int] | None = (1, max_step) if max_step else None
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
|
||||
if self._device.speed.group_address:
|
||||
self._attr_unique_id = str(self._device.speed.group_address)
|
||||
else:
|
||||
self._attr_unique_id = str(self._device.switch.group_address)
|
||||
|
||||
|
||||
class KnxUiFan(_KnxFan, KnxUiEntity):
|
||||
|
||||
@@ -558,15 +558,16 @@ class KnxYamlLight(_KnxLight, KnxYamlEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize of KNX light."""
|
||||
self._device = _create_yaml_light(knx_module.xknx, config)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=_create_yaml_light(knx_module.xknx, config),
|
||||
unique_id=self._device_unique_id(),
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
self._attr_color_mode = next(iter(self.supported_color_modes))
|
||||
self._attr_max_color_temp_kelvin: int = config[LightSchema.CONF_MAX_KELVIN]
|
||||
self._attr_min_color_temp_kelvin: int = config[LightSchema.CONF_MIN_KELVIN]
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_unique_id = self._device_unique_id()
|
||||
|
||||
def _device_unique_id(self) -> str:
|
||||
"""Return unique id for this device."""
|
||||
|
||||
@@ -46,12 +46,13 @@ class KNXNotify(KnxYamlEntity, NotifyEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize a KNX notification."""
|
||||
self._device = _create_notification_instance(knx_module.xknx, config)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=_create_notification_instance(knx_module.xknx, config),
|
||||
unique_id=str(self._device.remote_value.group_address),
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_unique_id = str(self._device.remote_value.group_address)
|
||||
|
||||
async def async_send_message(self, message: str, title: str | None = None) -> None:
|
||||
"""Send a notification to knx bus."""
|
||||
|
||||
@@ -109,16 +109,19 @@ class KnxYamlNumber(_KnxNumber, KnxYamlEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize a KNX number."""
|
||||
self._device = NumericValue(
|
||||
knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address=config[KNX_ADDRESS],
|
||||
group_address_state=config.get(CONF_STATE_ADDRESS),
|
||||
respond_to_read=config[CONF_RESPOND_TO_READ],
|
||||
value_type=config[CONF_TYPE],
|
||||
)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=NumericValue(
|
||||
knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address=config[KNX_ADDRESS],
|
||||
group_address_state=config.get(CONF_STATE_ADDRESS),
|
||||
respond_to_read=config[CONF_RESPOND_TO_READ],
|
||||
value_type=config[CONF_TYPE],
|
||||
),
|
||||
unique_id=str(self._device.sensor_value.group_address),
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
dpt_string = self._device.sensor_value.dpt_class.dpt_number_str()
|
||||
dpt_info = get_supported_dpts()[dpt_string]
|
||||
@@ -131,7 +134,6 @@ class KnxYamlNumber(_KnxNumber, KnxYamlEntity):
|
||||
dpt_info["sensor_device_class"],
|
||||
),
|
||||
)
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_mode = config[CONF_MODE]
|
||||
self._attr_native_max_value = config.get(
|
||||
NumberConf.MAX,
|
||||
@@ -149,7 +151,6 @@ class KnxYamlNumber(_KnxNumber, KnxYamlEntity):
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
dpt_info["unit"],
|
||||
)
|
||||
self._attr_unique_id = str(self._device.sensor_value.group_address)
|
||||
|
||||
self._device.sensor_value.value = max(0, self._attr_native_min_value)
|
||||
|
||||
|
||||
@@ -83,18 +83,19 @@ class KnxYamlScene(_KnxScene, KnxYamlEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize KNX scene."""
|
||||
self._device = XknxScene(
|
||||
xknx=knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address=config[KNX_ADDRESS],
|
||||
scene_number=config[SceneSchema.CONF_SCENE_NUMBER],
|
||||
)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=XknxScene(
|
||||
xknx=knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address=config[KNX_ADDRESS],
|
||||
scene_number=config[SceneSchema.CONF_SCENE_NUMBER],
|
||||
unique_id=(
|
||||
f"{self._device.scene_value.group_address}_{self._device.scene_number}"
|
||||
),
|
||||
)
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_unique_id = (
|
||||
f"{self._device.scene_value.group_address}_{self._device.scene_number}"
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -199,16 +199,22 @@ class KNXPlatformSchema(ABC):
|
||||
}
|
||||
|
||||
|
||||
COMMON_ENTITY_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=""): cv.string,
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class BinarySensorSchema(KNXPlatformSchema):
|
||||
"""Voluptuous schema for KNX binary sensors."""
|
||||
|
||||
PLATFORM = Platform.BINARY_SENSOR
|
||||
DEFAULT_NAME = "KNX Binary Sensor"
|
||||
|
||||
ENTITY_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
|
||||
vol.Optional(CONF_IGNORE_INTERNAL_STATE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_INVERT, default=False): cv.boolean,
|
||||
@@ -218,7 +224,6 @@ class BinarySensorSchema(KNXPlatformSchema):
|
||||
),
|
||||
vol.Optional(CONF_DEVICE_CLASS): BINARY_SENSOR_DEVICE_CLASSES_SCHEMA,
|
||||
vol.Optional(CONF_RESET_AFTER): cv.positive_float,
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
),
|
||||
)
|
||||
@@ -230,7 +235,6 @@ class ButtonSchema(KNXPlatformSchema):
|
||||
PLATFORM = Platform.BUTTON
|
||||
|
||||
CONF_VALUE = "value"
|
||||
DEFAULT_NAME = "KNX Button"
|
||||
|
||||
payload_or_value_msg = f"Please use only one of `{CONF_PAYLOAD}` or `{CONF_VALUE}`"
|
||||
length_or_type_msg = (
|
||||
@@ -238,9 +242,8 @@ class ButtonSchema(KNXPlatformSchema):
|
||||
)
|
||||
|
||||
ENTITY_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Required(KNX_ADDRESS): ga_validator,
|
||||
vol.Exclusive(
|
||||
CONF_PAYLOAD, "payload_or_value", msg=payload_or_value_msg
|
||||
@@ -254,7 +257,6 @@ class ButtonSchema(KNXPlatformSchema):
|
||||
vol.Exclusive(
|
||||
CONF_TYPE, "length_or_type", msg=length_or_type_msg
|
||||
): object,
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
),
|
||||
vol.Any(
|
||||
@@ -322,7 +324,6 @@ class ClimateSchema(KNXPlatformSchema):
|
||||
CONF_SWING_HORIZONTAL_ADDRESS = "swing_horizontal_address"
|
||||
CONF_SWING_HORIZONTAL_STATE_ADDRESS = "swing_horizontal_state_address"
|
||||
|
||||
DEFAULT_NAME = "KNX Climate"
|
||||
DEFAULT_SETPOINT_SHIFT_MODE = "DPT6010"
|
||||
DEFAULT_SETPOINT_SHIFT_MAX = 6
|
||||
DEFAULT_SETPOINT_SHIFT_MIN = -6
|
||||
@@ -331,9 +332,8 @@ class ClimateSchema(KNXPlatformSchema):
|
||||
DEFAULT_FAN_SPEED_MODE = "percent"
|
||||
|
||||
ENTITY_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(
|
||||
ClimateConf.SETPOINT_SHIFT_MAX, default=DEFAULT_SETPOINT_SHIFT_MAX
|
||||
): vol.All(int, vol.Range(min=0, max=32)),
|
||||
@@ -399,7 +399,6 @@ class ClimateSchema(KNXPlatformSchema):
|
||||
): vol.Coerce(HVACMode),
|
||||
vol.Optional(ClimateConf.MIN_TEMP): vol.Coerce(float),
|
||||
vol.Optional(ClimateConf.MAX_TEMP): vol.Coerce(float),
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
vol.Optional(CONF_FAN_SPEED_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_FAN_SPEED_STATE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(ClimateConf.FAN_MAX_STEP, default=3): cv.byte,
|
||||
@@ -433,12 +432,10 @@ class CoverSchema(KNXPlatformSchema):
|
||||
CONF_ANGLE_STATE_ADDRESS = "angle_state_address"
|
||||
|
||||
DEFAULT_TRAVEL_TIME = 25
|
||||
DEFAULT_NAME = "KNX Cover"
|
||||
|
||||
ENTITY_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_MOVE_LONG_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_MOVE_SHORT_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_STOP_ADDRESS): ga_list_validator,
|
||||
@@ -456,7 +453,6 @@ class CoverSchema(KNXPlatformSchema):
|
||||
vol.Optional(CoverConf.INVERT_POSITION, default=False): cv.boolean,
|
||||
vol.Optional(CoverConf.INVERT_ANGLE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_DEVICE_CLASS): COVER_DEVICE_CLASSES_SCHEMA,
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
),
|
||||
vol.Any(
|
||||
@@ -481,16 +477,12 @@ class DateSchema(KNXPlatformSchema):
|
||||
|
||||
PLATFORM = Platform.DATE
|
||||
|
||||
DEFAULT_NAME = "KNX Date"
|
||||
|
||||
ENTITY_SCHEMA = vol.Schema(
|
||||
ENTITY_SCHEMA = COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean,
|
||||
vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
|
||||
vol.Required(KNX_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -500,16 +492,12 @@ class DateTimeSchema(KNXPlatformSchema):
|
||||
|
||||
PLATFORM = Platform.DATETIME
|
||||
|
||||
DEFAULT_NAME = "KNX DateTime"
|
||||
|
||||
ENTITY_SCHEMA = vol.Schema(
|
||||
ENTITY_SCHEMA = COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean,
|
||||
vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
|
||||
vol.Required(KNX_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -571,12 +559,9 @@ class FanSchema(KNXPlatformSchema):
|
||||
CONF_SWITCH_ADDRESS = "switch_address"
|
||||
CONF_SWITCH_STATE_ADDRESS = "switch_state_address"
|
||||
|
||||
DEFAULT_NAME = "KNX Fan"
|
||||
|
||||
ENTITY_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(KNX_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_SWITCH_ADDRESS): ga_list_validator,
|
||||
@@ -584,7 +569,6 @@ class FanSchema(KNXPlatformSchema):
|
||||
vol.Optional(CONF_OSCILLATION_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_OSCILLATION_STATE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(FanConf.MAX_STEP): cv.byte,
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
|
||||
}
|
||||
),
|
||||
@@ -629,7 +613,6 @@ class LightSchema(KNXPlatformSchema):
|
||||
CONF_MIN_KELVIN = "min_kelvin"
|
||||
CONF_MAX_KELVIN = "max_kelvin"
|
||||
|
||||
DEFAULT_NAME = "KNX Light"
|
||||
DEFAULT_COLOR_TEMP_MODE = "absolute"
|
||||
DEFAULT_MIN_KELVIN = 2700 # 370 mireds
|
||||
DEFAULT_MAX_KELVIN = 6000 # 166 mireds
|
||||
@@ -661,9 +644,8 @@ class LightSchema(KNXPlatformSchema):
|
||||
)
|
||||
|
||||
ENTITY_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(KNX_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_BRIGHTNESS_ADDRESS): ga_list_validator,
|
||||
@@ -713,7 +695,6 @@ class LightSchema(KNXPlatformSchema):
|
||||
vol.Optional(CONF_MAX_KELVIN, default=DEFAULT_MAX_KELVIN): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=1)
|
||||
),
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
),
|
||||
vol.Any(
|
||||
@@ -759,14 +740,10 @@ class NotifySchema(KNXPlatformSchema):
|
||||
|
||||
PLATFORM = Platform.NOTIFY
|
||||
|
||||
DEFAULT_NAME = "KNX Notify"
|
||||
|
||||
ENTITY_SCHEMA = vol.Schema(
|
||||
ENTITY_SCHEMA = COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_TYPE, default="latin_1"): string_type_validator,
|
||||
vol.Required(KNX_ADDRESS): ga_validator,
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -775,12 +752,10 @@ class NumberSchema(KNXPlatformSchema):
|
||||
"""Voluptuous schema for KNX numbers."""
|
||||
|
||||
PLATFORM = Platform.NUMBER
|
||||
DEFAULT_NAME = "KNX Number"
|
||||
|
||||
ENTITY_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean,
|
||||
vol.Optional(CONF_MODE, default=NumberMode.AUTO): vol.Coerce(
|
||||
NumberMode
|
||||
@@ -793,7 +768,6 @@ class NumberSchema(KNXPlatformSchema):
|
||||
vol.Optional(NumberConf.STEP): cv.positive_float,
|
||||
vol.Optional(CONF_DEVICE_CLASS): NUMBER_DEVICE_CLASSES_SCHEMA,
|
||||
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
),
|
||||
_number_limit_sub_validator,
|
||||
@@ -807,15 +781,12 @@ class SceneSchema(KNXPlatformSchema):
|
||||
|
||||
CONF_SCENE_NUMBER = "scene_number"
|
||||
|
||||
DEFAULT_NAME = "KNX SCENE"
|
||||
ENTITY_SCHEMA = vol.Schema(
|
||||
ENTITY_SCHEMA = COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Required(KNX_ADDRESS): ga_list_validator,
|
||||
vol.Required(SceneConf.SCENE_NUMBER): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=1, max=64)
|
||||
),
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -827,12 +798,10 @@ class SelectSchema(KNXPlatformSchema):
|
||||
|
||||
CONF_OPTION = "option"
|
||||
CONF_OPTIONS = "options"
|
||||
DEFAULT_NAME = "KNX Select"
|
||||
|
||||
ENTITY_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
|
||||
vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean,
|
||||
vol.Required(CONF_PAYLOAD_LENGTH): vol.All(
|
||||
@@ -846,7 +815,6 @@ class SelectSchema(KNXPlatformSchema):
|
||||
],
|
||||
vol.Required(KNX_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
),
|
||||
select_options_sub_validator,
|
||||
@@ -861,12 +829,10 @@ class SensorSchema(KNXPlatformSchema):
|
||||
CONF_ALWAYS_CALLBACK = "always_callback"
|
||||
CONF_STATE_ADDRESS = CONF_STATE_ADDRESS
|
||||
CONF_SYNC_STATE = CONF_SYNC_STATE
|
||||
DEFAULT_NAME = "KNX Sensor"
|
||||
|
||||
ENTITY_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
|
||||
vol.Optional(CONF_ALWAYS_CALLBACK, default=False): cv.boolean,
|
||||
vol.Optional(CONF_SENSOR_STATE_CLASS): STATE_CLASSES_SCHEMA,
|
||||
@@ -874,7 +840,6 @@ class SensorSchema(KNXPlatformSchema):
|
||||
vol.Required(CONF_STATE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_DEVICE_CLASS): SENSOR_DEVICE_CLASSES_SCHEMA,
|
||||
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
),
|
||||
_sensor_attribute_sub_validator,
|
||||
@@ -889,16 +854,13 @@ class SwitchSchema(KNXPlatformSchema):
|
||||
CONF_INVERT = CONF_INVERT
|
||||
CONF_STATE_ADDRESS = CONF_STATE_ADDRESS
|
||||
|
||||
DEFAULT_NAME = "KNX Switch"
|
||||
ENTITY_SCHEMA = vol.Schema(
|
||||
ENTITY_SCHEMA = COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_INVERT, default=False): cv.boolean,
|
||||
vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean,
|
||||
vol.Required(KNX_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_DEVICE_CLASS): SWITCH_DEVICE_CLASSES_SCHEMA,
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -908,17 +870,13 @@ class TextSchema(KNXPlatformSchema):
|
||||
|
||||
PLATFORM = Platform.TEXT
|
||||
|
||||
DEFAULT_NAME = "KNX Text"
|
||||
|
||||
ENTITY_SCHEMA = vol.Schema(
|
||||
ENTITY_SCHEMA = COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean,
|
||||
vol.Optional(CONF_TYPE, default="latin_1"): string_type_validator,
|
||||
vol.Optional(CONF_MODE, default=TextMode.TEXT): vol.Coerce(TextMode),
|
||||
vol.Required(KNX_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -928,16 +886,12 @@ class TimeSchema(KNXPlatformSchema):
|
||||
|
||||
PLATFORM = Platform.TIME
|
||||
|
||||
DEFAULT_NAME = "KNX Time"
|
||||
|
||||
ENTITY_SCHEMA = vol.Schema(
|
||||
ENTITY_SCHEMA = COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean,
|
||||
vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
|
||||
vol.Required(KNX_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -962,27 +916,21 @@ class WeatherSchema(KNXPlatformSchema):
|
||||
CONF_KNX_AIR_PRESSURE_ADDRESS = "address_air_pressure"
|
||||
CONF_KNX_HUMIDITY_ADDRESS = "address_humidity"
|
||||
|
||||
DEFAULT_NAME = "KNX Weather Station"
|
||||
|
||||
ENTITY_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
|
||||
vol.Required(CONF_KNX_TEMPERATURE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_BRIGHTNESS_SOUTH_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_BRIGHTNESS_EAST_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_BRIGHTNESS_WEST_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_BRIGHTNESS_NORTH_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_WIND_SPEED_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_WIND_BEARING_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_RAIN_ALARM_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_FROST_ALARM_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_WIND_ALARM_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_DAY_NIGHT_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_AIR_PRESSURE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_HUMIDITY_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
}
|
||||
),
|
||||
ENTITY_SCHEMA = COMMON_ENTITY_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
|
||||
vol.Required(CONF_KNX_TEMPERATURE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_BRIGHTNESS_SOUTH_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_BRIGHTNESS_EAST_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_BRIGHTNESS_WEST_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_BRIGHTNESS_NORTH_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_WIND_SPEED_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_WIND_BEARING_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_RAIN_ALARM_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_FROST_ALARM_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_WIND_ALARM_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_DAY_NIGHT_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_AIR_PRESSURE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_KNX_HUMIDITY_ADDRESS): ga_list_validator,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -65,9 +65,12 @@ class KNXSelect(KnxYamlEntity, SelectEntity, RestoreEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize a KNX select."""
|
||||
self._device = _create_raw_value(knx_module.xknx, config)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=_create_raw_value(knx_module.xknx, config),
|
||||
unique_id=str(self._device.remote_value.group_address),
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
self._option_payloads: dict[str, int] = {
|
||||
option[SelectSchema.CONF_OPTION]: option[CONF_PAYLOAD]
|
||||
@@ -75,8 +78,6 @@ class KNXSelect(KnxYamlEntity, SelectEntity, RestoreEntity):
|
||||
}
|
||||
self._attr_options = list(self._option_payloads)
|
||||
self._attr_current_option = None
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_unique_id = str(self._device.remote_value.group_address)
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Restore last state."""
|
||||
|
||||
@@ -202,16 +202,19 @@ class KnxYamlSensor(_KnxSensor, KnxYamlEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize of a KNX sensor."""
|
||||
self._device = XknxSensor(
|
||||
knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address_state=config[SensorSchema.CONF_STATE_ADDRESS],
|
||||
sync_state=config[CONF_SYNC_STATE],
|
||||
always_callback=True,
|
||||
value_type=config[CONF_TYPE],
|
||||
)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=XknxSensor(
|
||||
knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address_state=config[SensorSchema.CONF_STATE_ADDRESS],
|
||||
sync_state=config[CONF_SYNC_STATE],
|
||||
always_callback=True,
|
||||
value_type=config[CONF_TYPE],
|
||||
),
|
||||
unique_id=str(self._device.sensor_value.group_address_state),
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
dpt_string = self._device.sensor_value.dpt_class.dpt_number_str()
|
||||
dpt_info = get_supported_dpts()[dpt_string]
|
||||
@@ -220,7 +223,6 @@ class KnxYamlSensor(_KnxSensor, KnxYamlEntity):
|
||||
CONF_DEVICE_CLASS,
|
||||
dpt_info["sensor_device_class"],
|
||||
)
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_extra_state_attributes = {}
|
||||
self._attr_force_update = config[SensorSchema.CONF_ALWAYS_CALLBACK]
|
||||
self._attr_native_unit_of_measurement = config.get(
|
||||
@@ -231,7 +233,6 @@ class KnxYamlSensor(_KnxSensor, KnxYamlEntity):
|
||||
CONF_STATE_CLASS,
|
||||
dpt_info["sensor_state_class"],
|
||||
)
|
||||
self._attr_unique_id = str(self._device.sensor_value.group_address_state)
|
||||
|
||||
|
||||
class KnxUiSensor(_KnxSensor, KnxUiEntity):
|
||||
|
||||
@@ -107,20 +107,21 @@ class KnxYamlSwitch(_KnxSwitch, KnxYamlEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize of KNX switch."""
|
||||
self._device = XknxSwitch(
|
||||
xknx=knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address=config[KNX_ADDRESS],
|
||||
group_address_state=config.get(SwitchSchema.CONF_STATE_ADDRESS),
|
||||
respond_to_read=config[CONF_RESPOND_TO_READ],
|
||||
invert=config[SwitchSchema.CONF_INVERT],
|
||||
)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=XknxSwitch(
|
||||
xknx=knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address=config[KNX_ADDRESS],
|
||||
group_address_state=config.get(SwitchSchema.CONF_STATE_ADDRESS),
|
||||
respond_to_read=config[CONF_RESPOND_TO_READ],
|
||||
invert=config[SwitchSchema.CONF_INVERT],
|
||||
),
|
||||
unique_id=str(self._device.switch.group_address),
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_device_class = config.get(CONF_DEVICE_CLASS)
|
||||
self._attr_unique_id = str(self._device.switch.group_address)
|
||||
|
||||
|
||||
class KnxUiSwitch(_KnxSwitch, KnxUiEntity):
|
||||
|
||||
@@ -112,20 +112,21 @@ class KnxYamlText(_KnxText, KnxYamlEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize a KNX text."""
|
||||
self._device = XknxNotification(
|
||||
knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address=config[KNX_ADDRESS],
|
||||
group_address_state=config.get(CONF_STATE_ADDRESS),
|
||||
respond_to_read=config[CONF_RESPOND_TO_READ],
|
||||
value_type=config[CONF_TYPE],
|
||||
)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=XknxNotification(
|
||||
knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address=config[KNX_ADDRESS],
|
||||
group_address_state=config.get(CONF_STATE_ADDRESS),
|
||||
respond_to_read=config[CONF_RESPOND_TO_READ],
|
||||
value_type=config[CONF_TYPE],
|
||||
),
|
||||
unique_id=str(self._device.remote_value.group_address),
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
self._attr_mode = config[CONF_MODE]
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_unique_id = str(self._device.remote_value.group_address)
|
||||
|
||||
|
||||
class KnxUiText(_KnxText, KnxUiEntity):
|
||||
|
||||
@@ -105,20 +105,21 @@ class KnxYamlTime(_KNXTime, KnxYamlEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize a KNX time."""
|
||||
self._device = XknxTimeDevice(
|
||||
knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
localtime=False,
|
||||
group_address=config[KNX_ADDRESS],
|
||||
group_address_state=config.get(CONF_STATE_ADDRESS),
|
||||
respond_to_read=config[CONF_RESPOND_TO_READ],
|
||||
sync_state=config[CONF_SYNC_STATE],
|
||||
)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=XknxTimeDevice(
|
||||
knx_module.xknx,
|
||||
name=config[CONF_NAME],
|
||||
localtime=False,
|
||||
group_address=config[KNX_ADDRESS],
|
||||
group_address_state=config.get(CONF_STATE_ADDRESS),
|
||||
respond_to_read=config[CONF_RESPOND_TO_READ],
|
||||
sync_state=config[CONF_SYNC_STATE],
|
||||
),
|
||||
unique_id=str(self._device.remote_value.group_address),
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_unique_id = str(self._device.remote_value.group_address)
|
||||
|
||||
|
||||
class KnxUiTime(_KNXTime, KnxUiEntity):
|
||||
|
||||
@@ -85,12 +85,13 @@ class KNXWeather(KnxYamlEntity, WeatherEntity):
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize of a KNX sensor."""
|
||||
self._device = _create_weather(knx_module.xknx, config)
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=_create_weather(knx_module.xknx, config),
|
||||
unique_id=str(self._device._temperature.group_address_state), # noqa: SLF001
|
||||
name=config[CONF_NAME],
|
||||
entity_category=config.get(CONF_ENTITY_CATEGORY),
|
||||
)
|
||||
self._attr_unique_id = str(self._device._temperature.group_address_state) # noqa: SLF001
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
|
||||
@property
|
||||
def native_temperature(self) -> float | None:
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/opendisplay",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["opendisplay"],
|
||||
"quality_scale": "silver",
|
||||
"requirements": ["py-opendisplay==5.5.0"]
|
||||
}
|
||||
|
||||
@@ -20,12 +20,18 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import (
|
||||
RoborockB01Q10UpdateCoordinator,
|
||||
RoborockConfigEntry,
|
||||
RoborockDataUpdateCoordinator,
|
||||
RoborockDataUpdateCoordinatorA01,
|
||||
RoborockWashingMachineUpdateCoordinator,
|
||||
)
|
||||
from .entity import RoborockCoordinatedEntityA01, RoborockEntity, RoborockEntityV1
|
||||
from .entity import (
|
||||
RoborockCoordinatedEntityA01,
|
||||
RoborockCoordinatedEntityB01Q10,
|
||||
RoborockEntity,
|
||||
RoborockEntityV1,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -97,6 +103,14 @@ ZEO_BUTTON_DESCRIPTIONS = [
|
||||
]
|
||||
|
||||
|
||||
Q10_BUTTON_DESCRIPTIONS = [
|
||||
ButtonEntityDescription(
|
||||
key="empty_dustbin",
|
||||
translation_key="empty_dustbin",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: RoborockConfigEntry,
|
||||
@@ -139,6 +153,15 @@ async def async_setup_entry(
|
||||
if isinstance(coordinator, RoborockWashingMachineUpdateCoordinator)
|
||||
for description in ZEO_BUTTON_DESCRIPTIONS
|
||||
),
|
||||
(
|
||||
RoborockQ10EmptyDustbinButtonEntity(
|
||||
coordinator,
|
||||
description,
|
||||
)
|
||||
for coordinator in config_entry.runtime_data.b01_q10
|
||||
if isinstance(coordinator, RoborockB01Q10UpdateCoordinator)
|
||||
for description in Q10_BUTTON_DESCRIPTIONS
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -233,3 +256,37 @@ class RoborockButtonEntityA01(RoborockCoordinatedEntityA01, ButtonEntity):
|
||||
) from err
|
||||
finally:
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
|
||||
class RoborockQ10EmptyDustbinButtonEntity(
|
||||
RoborockCoordinatedEntityB01Q10, ButtonEntity
|
||||
):
|
||||
"""A class to define Q10 empty dustbin button entity."""
|
||||
|
||||
entity_description: ButtonEntityDescription
|
||||
coordinator: RoborockB01Q10UpdateCoordinator
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: RoborockB01Q10UpdateCoordinator,
|
||||
entity_description: ButtonEntityDescription,
|
||||
) -> None:
|
||||
"""Create a Q10 empty dustbin button entity."""
|
||||
self.entity_description = entity_description
|
||||
super().__init__(
|
||||
f"{entity_description.key}_{coordinator.duid_slug}",
|
||||
coordinator,
|
||||
)
|
||||
|
||||
async def async_press(self, **kwargs: Any) -> None:
|
||||
"""Press the button to empty dustbin."""
|
||||
try:
|
||||
await self.coordinator.api.vacuum.empty_dustbin()
|
||||
except RoborockException as err:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="command_failed",
|
||||
translation_placeholders={
|
||||
"command": "empty_dustbin",
|
||||
},
|
||||
) from err
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"loggers": ["roborock"],
|
||||
"quality_scale": "silver",
|
||||
"requirements": [
|
||||
"python-roborock==4.25.0",
|
||||
"python-roborock==4.26.2",
|
||||
"vacuum-map-parser-roborock==0.1.4"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -84,6 +84,9 @@
|
||||
}
|
||||
},
|
||||
"button": {
|
||||
"empty_dustbin": {
|
||||
"name": "Empty dustbin"
|
||||
},
|
||||
"pause": {
|
||||
"name": "Pause"
|
||||
},
|
||||
|
||||
@@ -208,6 +208,16 @@ CAPABILITY_TO_SENSORS: dict[
|
||||
supported_states_attributes=Attribute.SUPPORTED_COOKTOP_OPERATING_STATE,
|
||||
)
|
||||
},
|
||||
Capability.SAMSUNG_CE_CLEAN_STATION_STICK_STATUS: {
|
||||
Attribute.STATUS: SmartThingsBinarySensorEntityDescription(
|
||||
key=Attribute.STATUS,
|
||||
component_translation_key={
|
||||
"station": "stick_cleaner_status",
|
||||
},
|
||||
exists_fn=lambda component, _: component == "station",
|
||||
is_on_key="attached",
|
||||
)
|
||||
},
|
||||
Capability.SAMSUNG_CE_MICROFIBER_FILTER_STATUS: {
|
||||
Attribute.STATUS: SmartThingsBinarySensorEntityDescription(
|
||||
key=Attribute.STATUS,
|
||||
|
||||
@@ -85,6 +85,9 @@
|
||||
"robot_cleaner_dust_bag": {
|
||||
"name": "Dust bag full"
|
||||
},
|
||||
"stick_cleaner_status": {
|
||||
"name": "Stick cleaner in station"
|
||||
},
|
||||
"sub_remote_control": {
|
||||
"name": "Upper washer remote control"
|
||||
},
|
||||
|
||||
@@ -105,6 +105,7 @@ SENSORS: list[SmSensorEntityDescription] = [
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
EXTRA_SENSOR = SmSensorEntityDescription(
|
||||
key="zigbee_temperature_2",
|
||||
translation_key="zigbee_temperature",
|
||||
@@ -115,6 +116,15 @@ EXTRA_SENSOR = SmSensorEntityDescription(
|
||||
value_fn=lambda x: x.zb_temp2,
|
||||
)
|
||||
|
||||
PSRAM_SENSOR = SmSensorEntityDescription(
|
||||
key="psram_usage",
|
||||
translation_key="psram_usage",
|
||||
device_class=SensorDeviceClass.DATA_SIZE,
|
||||
native_unit_of_measurement=UnitOfInformation.KILOBYTES,
|
||||
entity_registry_enabled_default=False,
|
||||
value_fn=lambda x: x.psram_usage,
|
||||
)
|
||||
|
||||
UPTIME: list[SmSensorEntityDescription] = [
|
||||
SmSensorEntityDescription(
|
||||
key="core_uptime",
|
||||
@@ -156,6 +166,9 @@ async def async_setup_entry(
|
||||
if coordinator.data.sensors.zb_temp2 is not None:
|
||||
entities.append(SmSensorEntity(coordinator, EXTRA_SENSOR))
|
||||
|
||||
if coordinator.data.info.u_device:
|
||||
entities.append(SmSensorEntity(coordinator, PSRAM_SENSOR))
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
|
||||
@@ -104,6 +104,9 @@
|
||||
"fs_usage": {
|
||||
"name": "Filesystem usage"
|
||||
},
|
||||
"psram_usage": {
|
||||
"name": "PSRAM usage"
|
||||
},
|
||||
"ram_usage": {
|
||||
"name": "RAM usage"
|
||||
},
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/starlink",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["starlink-grpc-core==1.2.3"]
|
||||
"requirements": ["starlink-grpc-core==1.2.4"]
|
||||
}
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aiotedee"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["aiotedee==0.2.25"]
|
||||
"requirements": ["aiotedee==0.2.27"]
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/trmnl",
|
||||
"integration_type": "hub",
|
||||
"iot_class": "cloud_polling",
|
||||
"quality_scale": "bronze",
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["trmnl==0.1.1"]
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pyvlx import PyVLX, PyVLXException
|
||||
from pyvlx import Node, PyVLX, PyVLXException
|
||||
|
||||
from homeassistant.components.button import ButtonDeviceClass, ButtonEntity
|
||||
from homeassistant.const import EntityCategory
|
||||
@@ -13,6 +13,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import VeluxConfigEntry
|
||||
from .const import DOMAIN
|
||||
from .entity import VeluxEntity, wrap_pyvlx_call_exceptions
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
|
||||
@@ -23,9 +24,32 @@ async def async_setup_entry(
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up button entities for the Velux integration."""
|
||||
async_add_entities(
|
||||
[VeluxGatewayRebootButton(config_entry.entry_id, config_entry.runtime_data)]
|
||||
entities: list[ButtonEntity] = [
|
||||
VeluxGatewayRebootButton(config_entry.entry_id, config_entry.runtime_data)
|
||||
]
|
||||
entities.extend(
|
||||
VeluxIdentifyButton(node, config_entry.entry_id)
|
||||
for node in config_entry.runtime_data.nodes
|
||||
if isinstance(node, Node)
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class VeluxIdentifyButton(VeluxEntity, ButtonEntity):
|
||||
"""Representation of a Velux identify button."""
|
||||
|
||||
_attr_device_class = ButtonDeviceClass.IDENTIFY
|
||||
_attr_entity_category = EntityCategory.DIAGNOSTIC
|
||||
|
||||
def __init__(self, node: Node, config_entry_id: str) -> None:
|
||||
"""Initialize the Velux identify button."""
|
||||
super().__init__(node, config_entry_id)
|
||||
self._attr_unique_id = f"{self._attr_unique_id}_identify"
|
||||
|
||||
@wrap_pyvlx_call_exceptions
|
||||
async def async_press(self) -> None:
|
||||
"""Identify the physical device."""
|
||||
await self.node.wink()
|
||||
|
||||
|
||||
class VeluxGatewayRebootButton(ButtonEntity):
|
||||
|
||||
@@ -25,5 +25,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/xiaomi_ble",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["xiaomi-ble==1.6.0"]
|
||||
"requirements": ["xiaomi-ble==1.10.0"]
|
||||
}
|
||||
|
||||
10
requirements_all.txt
generated
10
requirements_all.txt
generated
@@ -422,7 +422,7 @@ aiosyncthing==0.7.1
|
||||
aiotankerkoenig==0.5.1
|
||||
|
||||
# homeassistant.components.tedee
|
||||
aiotedee==0.2.25
|
||||
aiotedee==0.2.27
|
||||
|
||||
# homeassistant.components.tractive
|
||||
aiotractive==1.0.0
|
||||
@@ -2593,7 +2593,7 @@ python-gitlab==1.6.0
|
||||
python-google-drive-api==0.1.0
|
||||
|
||||
# homeassistant.components.google_weather
|
||||
python-google-weather-api==0.0.4
|
||||
python-google-weather-api==0.0.6
|
||||
|
||||
# homeassistant.components.analytics_insights
|
||||
python-homeassistant-analytics==0.9.0
|
||||
@@ -2660,7 +2660,7 @@ python-rabbitair==0.0.8
|
||||
python-ripple-api==0.0.3
|
||||
|
||||
# homeassistant.components.roborock
|
||||
python-roborock==4.25.0
|
||||
python-roborock==4.26.2
|
||||
|
||||
# homeassistant.components.smarttub
|
||||
python-smarttub==0.0.47
|
||||
@@ -3014,7 +3014,7 @@ starline==0.1.5
|
||||
starlingbank==3.2
|
||||
|
||||
# homeassistant.components.starlink
|
||||
starlink-grpc-core==1.2.3
|
||||
starlink-grpc-core==1.2.4
|
||||
|
||||
# homeassistant.components.statsd
|
||||
statsd==3.2.1
|
||||
@@ -3319,7 +3319,7 @@ wsdot==0.0.1
|
||||
wyoming==1.7.2
|
||||
|
||||
# homeassistant.components.xiaomi_ble
|
||||
xiaomi-ble==1.6.0
|
||||
xiaomi-ble==1.10.0
|
||||
|
||||
# homeassistant.components.knx
|
||||
xknx==3.15.0
|
||||
|
||||
10
requirements_test_all.txt
generated
10
requirements_test_all.txt
generated
@@ -407,7 +407,7 @@ aiosyncthing==0.7.1
|
||||
aiotankerkoenig==0.5.1
|
||||
|
||||
# homeassistant.components.tedee
|
||||
aiotedee==0.2.25
|
||||
aiotedee==0.2.27
|
||||
|
||||
# homeassistant.components.tractive
|
||||
aiotractive==1.0.0
|
||||
@@ -2198,7 +2198,7 @@ python-fullykiosk==0.0.15
|
||||
python-google-drive-api==0.1.0
|
||||
|
||||
# homeassistant.components.google_weather
|
||||
python-google-weather-api==0.0.4
|
||||
python-google-weather-api==0.0.6
|
||||
|
||||
# homeassistant.components.analytics_insights
|
||||
python-homeassistant-analytics==0.9.0
|
||||
@@ -2256,7 +2256,7 @@ python-pooldose==0.8.6
|
||||
python-rabbitair==0.0.8
|
||||
|
||||
# homeassistant.components.roborock
|
||||
python-roborock==4.25.0
|
||||
python-roborock==4.26.2
|
||||
|
||||
# homeassistant.components.smarttub
|
||||
python-smarttub==0.0.47
|
||||
@@ -2547,7 +2547,7 @@ srpenergy==1.3.6
|
||||
starline==0.1.5
|
||||
|
||||
# homeassistant.components.starlink
|
||||
starlink-grpc-core==1.2.3
|
||||
starlink-grpc-core==1.2.4
|
||||
|
||||
# homeassistant.components.statsd
|
||||
statsd==3.2.1
|
||||
@@ -2801,7 +2801,7 @@ wsdot==0.0.1
|
||||
wyoming==1.7.2
|
||||
|
||||
# homeassistant.components.xiaomi_ble
|
||||
xiaomi-ble==1.6.0
|
||||
xiaomi-ble==1.10.0
|
||||
|
||||
# homeassistant.components.knx
|
||||
xknx==3.15.0
|
||||
|
||||
@@ -43,7 +43,7 @@ from homeassistant.helpers.service_info.mqtt import MqttServiceInfo
|
||||
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
|
||||
|
||||
from . import VALID_NOISE_PSK
|
||||
from .conftest import MockESPHomeDeviceType, MockGenericDeviceEntryType
|
||||
from .conftest import MockGenericDeviceEntryType
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
@@ -865,79 +865,6 @@ async def test_discovery_already_configured(hass: HomeAssistant) -> None:
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_zeroconf")
|
||||
async def test_discovery_does_not_update_host_when_device_is_connected(
|
||||
hass: HomeAssistant,
|
||||
mock_client: APIClient,
|
||||
mock_esphome_device: MockESPHomeDeviceType,
|
||||
) -> None:
|
||||
"""Test zeroconf discovery does not update host when device is connected."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.2",
|
||||
CONF_PORT: 6053,
|
||||
CONF_PASSWORD: "",
|
||||
},
|
||||
unique_id="11:22:33:44:55:aa",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await mock_esphome_device(mock_client=mock_client, entry=entry)
|
||||
|
||||
service_info = ZeroconfServiceInfo(
|
||||
ip_address=ip_address("192.168.1.99"),
|
||||
ip_addresses=[ip_address("192.168.1.99")],
|
||||
hostname="test.local.",
|
||||
name="mock_name",
|
||||
port=6053,
|
||||
properties={"mac": "1122334455aa"},
|
||||
type="mock_type",
|
||||
)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_ZEROCONF}, data=service_info
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
# Host should NOT be updated since the device is currently connected
|
||||
assert entry.data[CONF_HOST] == "192.168.1.2"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_zeroconf")
|
||||
async def test_discovery_does_not_update_host_when_device_is_connected_dhcp(
|
||||
hass: HomeAssistant,
|
||||
mock_client: APIClient,
|
||||
mock_esphome_device: MockESPHomeDeviceType,
|
||||
) -> None:
|
||||
"""Test DHCP discovery does not update host when device is connected."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.2",
|
||||
CONF_PORT: 6053,
|
||||
CONF_PASSWORD: "",
|
||||
},
|
||||
unique_id="11:22:33:44:55:aa",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await mock_esphome_device(mock_client=mock_client, entry=entry)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_DHCP},
|
||||
data=DhcpServiceInfo(
|
||||
ip="192.168.1.99",
|
||||
macaddress="1122334455aa",
|
||||
hostname="test",
|
||||
),
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
# Host should NOT be updated since the device is currently connected
|
||||
assert entry.data[CONF_HOST] == "192.168.1.2"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_client", "mock_setup_entry", "mock_zeroconf")
|
||||
async def test_discovery_ignored(hass: HomeAssistant) -> None:
|
||||
"""Test discovery does not probe and ignored entry."""
|
||||
|
||||
375
tests/components/google_weather/snapshots/test_diagnostics.ambr
Normal file
375
tests/components/google_weather/snapshots/test_diagnostics.ambr
Normal file
@@ -0,0 +1,375 @@
|
||||
# serializer version: 1
|
||||
# name: test_diagnostics
|
||||
dict({
|
||||
'entry': dict({
|
||||
'created_at': '2026-03-20T21:22:23+00:00',
|
||||
'data': dict({
|
||||
'api_key': '**REDACTED**',
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'discovery_keys': dict({
|
||||
}),
|
||||
'domain': 'google_weather',
|
||||
'minor_version': 1,
|
||||
'modified_at': '2026-03-20T21:22:23+00:00',
|
||||
'options': dict({
|
||||
}),
|
||||
'pref_disable_new_entities': False,
|
||||
'pref_disable_polling': False,
|
||||
'source': 'user',
|
||||
'subentries': list([
|
||||
dict({
|
||||
'data': dict({
|
||||
'latitude': '**REDACTED**',
|
||||
'longitude': '**REDACTED**',
|
||||
}),
|
||||
'subentry_id': 'home-subentry-id',
|
||||
'subentry_type': 'location',
|
||||
'title': 'Home',
|
||||
'unique_id': None,
|
||||
}),
|
||||
]),
|
||||
'title': 'Google Weather',
|
||||
'unique_id': None,
|
||||
'version': 1,
|
||||
}),
|
||||
'subentries': dict({
|
||||
'home-subentry-id': dict({
|
||||
'daily_forecast_data': dict({
|
||||
'forecast_days': list([
|
||||
dict({
|
||||
'daytime_forecast': dict({
|
||||
'cloud_cover': 53,
|
||||
'ice_thickness': None,
|
||||
'interval': dict({
|
||||
'end_time': '2025-02-11T03:00:00Z',
|
||||
'start_time': '2025-02-10T15:00:00Z',
|
||||
}),
|
||||
'precipitation': dict({
|
||||
'probability': dict({
|
||||
'percent': 5,
|
||||
'type': 'RAIN',
|
||||
}),
|
||||
'qpf': dict({
|
||||
'quantity': 0.0,
|
||||
'unit': 'MILLIMETERS',
|
||||
}),
|
||||
'snow_qpf': None,
|
||||
}),
|
||||
'relative_humidity': 54,
|
||||
'thunderstorm_probability': 0,
|
||||
'uv_index': 3,
|
||||
'weather_condition': dict({
|
||||
'description': dict({
|
||||
'language_code': 'en',
|
||||
'text': 'Partly sunny',
|
||||
}),
|
||||
'icon_base_uri': 'https://maps.gstatic.com/weather/v1/party_cloudy',
|
||||
'type': 'PARTLY_CLOUDY',
|
||||
}),
|
||||
'wind': dict({
|
||||
'direction': dict({
|
||||
'cardinal': 'WEST',
|
||||
'degrees': 280,
|
||||
}),
|
||||
'gust': dict({
|
||||
'unit': 'KILOMETERS_PER_HOUR',
|
||||
'value': 14.0,
|
||||
}),
|
||||
'speed': dict({
|
||||
'unit': 'KILOMETERS_PER_HOUR',
|
||||
'value': 6.0,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'display_date': dict({
|
||||
'day': 10,
|
||||
'month': 2,
|
||||
'year': 2025,
|
||||
}),
|
||||
'feels_like_max_temperature': dict({
|
||||
'degrees': 13.3,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'feels_like_min_temperature': dict({
|
||||
'degrees': 1.5,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'ice_thickness': dict({
|
||||
'thickness': 0.0,
|
||||
'unit': 'MILLIMETERS',
|
||||
}),
|
||||
'interval': dict({
|
||||
'end_time': '2025-02-11T15:00:00Z',
|
||||
'start_time': '2025-02-10T15:00:00Z',
|
||||
}),
|
||||
'max_heat_index': dict({
|
||||
'degrees': 13.3,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'max_temperature': dict({
|
||||
'degrees': 13.3,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'min_temperature': dict({
|
||||
'degrees': 1.5,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'moon_events': dict({
|
||||
'moon_phase': 'WAXING_GIBBOUS',
|
||||
'moonrise_times': list([
|
||||
'2025-02-10T23:54:17.713157984Z',
|
||||
]),
|
||||
'moonset_times': list([
|
||||
'2025-02-10T14:13:58.625181191Z',
|
||||
]),
|
||||
}),
|
||||
'nighttime_forecast': dict({
|
||||
'cloud_cover': 70,
|
||||
'ice_thickness': None,
|
||||
'interval': dict({
|
||||
'end_time': '2025-02-11T15:00:00Z',
|
||||
'start_time': '2025-02-11T03:00:00Z',
|
||||
}),
|
||||
'precipitation': dict({
|
||||
'probability': dict({
|
||||
'percent': 10,
|
||||
'type': 'RAIN_AND_SNOW',
|
||||
}),
|
||||
'qpf': dict({
|
||||
'quantity': 0.0,
|
||||
'unit': 'MILLIMETERS',
|
||||
}),
|
||||
'snow_qpf': None,
|
||||
}),
|
||||
'relative_humidity': 85,
|
||||
'thunderstorm_probability': 0,
|
||||
'uv_index': 0,
|
||||
'weather_condition': dict({
|
||||
'description': dict({
|
||||
'language_code': 'en',
|
||||
'text': 'Partly cloudy',
|
||||
}),
|
||||
'icon_base_uri': 'https://maps.gstatic.com/weather/v1/partly_clear',
|
||||
'type': 'PARTLY_CLOUDY',
|
||||
}),
|
||||
'wind': dict({
|
||||
'direction': dict({
|
||||
'cardinal': 'SOUTH_SOUTHWEST',
|
||||
'degrees': 201,
|
||||
}),
|
||||
'gust': dict({
|
||||
'unit': 'KILOMETERS_PER_HOUR',
|
||||
'value': 14.0,
|
||||
}),
|
||||
'speed': dict({
|
||||
'unit': 'KILOMETERS_PER_HOUR',
|
||||
'value': 6.0,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'sun_events': dict({
|
||||
'sunrise_time': '2025-02-10T15:02:35.703929582Z',
|
||||
'sunset_time': '2025-02-11T01:43:00.762932858Z',
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
'next_page_token': None,
|
||||
'time_zone': dict({
|
||||
'id': 'America/Los_Angeles',
|
||||
'version': None,
|
||||
}),
|
||||
}),
|
||||
'hourly_forecast_data': dict({
|
||||
'forecast_hours': list([
|
||||
dict({
|
||||
'air_pressure': dict({
|
||||
'mean_sea_level_millibars': 1019.13,
|
||||
}),
|
||||
'cloud_cover': 0,
|
||||
'dew_point': dict({
|
||||
'degrees': 2.7,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'display_date_time': dict({
|
||||
'day': 5,
|
||||
'hours': 15,
|
||||
'minutes': None,
|
||||
'month': 2,
|
||||
'nanos': None,
|
||||
'seconds': None,
|
||||
'time_zone': None,
|
||||
'utc_offset': '-28800s',
|
||||
'year': 2025,
|
||||
}),
|
||||
'feels_like_temperature': dict({
|
||||
'degrees': 12.0,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'heat_index': dict({
|
||||
'degrees': 12.7,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'ice_thickness': dict({
|
||||
'thickness': 0.0,
|
||||
'unit': 'MILLIMETERS',
|
||||
}),
|
||||
'interval': dict({
|
||||
'end_time': '2025-02-06T00:00:00Z',
|
||||
'start_time': '2025-02-05T23:00:00Z',
|
||||
}),
|
||||
'is_daytime': True,
|
||||
'precipitation': dict({
|
||||
'probability': dict({
|
||||
'percent': 0,
|
||||
'type': 'RAIN',
|
||||
}),
|
||||
'qpf': dict({
|
||||
'quantity': 0.0,
|
||||
'unit': 'MILLIMETERS',
|
||||
}),
|
||||
'snow_qpf': None,
|
||||
}),
|
||||
'relative_humidity': 51,
|
||||
'temperature': dict({
|
||||
'degrees': 12.7,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'thunderstorm_probability': 0,
|
||||
'uv_index': 1,
|
||||
'visibility': dict({
|
||||
'distance': 16.0,
|
||||
'unit': 'KILOMETERS',
|
||||
}),
|
||||
'weather_condition': dict({
|
||||
'description': dict({
|
||||
'language_code': 'en',
|
||||
'text': 'Sunny',
|
||||
}),
|
||||
'icon_base_uri': 'https://maps.gstatic.com/weather/v1/sunny',
|
||||
'type': 'CLEAR',
|
||||
}),
|
||||
'wet_bulb_temperature': dict({
|
||||
'degrees': 7.7,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'wind': dict({
|
||||
'direction': dict({
|
||||
'cardinal': 'NORTH_NORTHWEST',
|
||||
'degrees': 335,
|
||||
}),
|
||||
'gust': dict({
|
||||
'unit': 'KILOMETERS_PER_HOUR',
|
||||
'value': 19.0,
|
||||
}),
|
||||
'speed': dict({
|
||||
'unit': 'KILOMETERS_PER_HOUR',
|
||||
'value': 10.0,
|
||||
}),
|
||||
}),
|
||||
'wind_chill': dict({
|
||||
'degrees': 12.0,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
'next_page_token': None,
|
||||
'time_zone': dict({
|
||||
'id': 'America/Los_Angeles',
|
||||
'version': None,
|
||||
}),
|
||||
}),
|
||||
'observation_data': dict({
|
||||
'air_pressure': dict({
|
||||
'mean_sea_level_millibars': 1019.16,
|
||||
}),
|
||||
'cloud_cover': 0,
|
||||
'current_conditions_history': dict({
|
||||
'max_temperature': dict({
|
||||
'degrees': 14.3,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'min_temperature': dict({
|
||||
'degrees': 3.7,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'qpf': dict({
|
||||
'quantity': 0.0,
|
||||
'unit': 'MILLIMETERS',
|
||||
}),
|
||||
'temperature_change': dict({
|
||||
'degrees': -0.6,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
}),
|
||||
'current_time': '2025-01-28T22:04:12.025273178Z',
|
||||
'dew_point': dict({
|
||||
'degrees': 1.1,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'feels_like_temperature': dict({
|
||||
'degrees': 13.1,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'heat_index': dict({
|
||||
'degrees': 13.7,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'is_daytime': True,
|
||||
'precipitation': dict({
|
||||
'probability': dict({
|
||||
'percent': 0,
|
||||
'type': 'RAIN',
|
||||
}),
|
||||
'qpf': dict({
|
||||
'quantity': 0.0,
|
||||
'unit': 'MILLIMETERS',
|
||||
}),
|
||||
'snow_qpf': None,
|
||||
}),
|
||||
'relative_humidity': 42,
|
||||
'temperature': dict({
|
||||
'degrees': 13.7,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
'thunderstorm_probability': 0,
|
||||
'time_zone': dict({
|
||||
'id': 'America/Los_Angeles',
|
||||
'version': None,
|
||||
}),
|
||||
'uv_index': 1,
|
||||
'visibility': dict({
|
||||
'distance': 16.0,
|
||||
'unit': 'KILOMETERS',
|
||||
}),
|
||||
'weather_condition': dict({
|
||||
'description': dict({
|
||||
'language_code': 'en',
|
||||
'text': 'Sunny',
|
||||
}),
|
||||
'icon_base_uri': 'https://maps.gstatic.com/weather/v1/sunny',
|
||||
'type': 'CLEAR',
|
||||
}),
|
||||
'wind': dict({
|
||||
'direction': dict({
|
||||
'cardinal': 'NORTH_NORTHWEST',
|
||||
'degrees': 335,
|
||||
}),
|
||||
'gust': dict({
|
||||
'unit': 'KILOMETERS_PER_HOUR',
|
||||
'value': 18.0,
|
||||
}),
|
||||
'speed': dict({
|
||||
'unit': 'KILOMETERS_PER_HOUR',
|
||||
'value': 8.0,
|
||||
}),
|
||||
}),
|
||||
'wind_chill': dict({
|
||||
'degrees': 13.1,
|
||||
'unit': 'CELSIUS',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
37
tests/components/google_weather/test_diagnostics.py
Normal file
37
tests/components/google_weather/test_diagnostics.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""Tests for the diagnostics data provided by the Google Weather integration."""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from freezegun import freeze_time
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
from syrupy.filters import props
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def freeze_the_time():
|
||||
"""Freeze the time."""
|
||||
with freeze_time("2026-03-20 21:22:23", tz_offset=0):
|
||||
yield
|
||||
|
||||
|
||||
async def test_diagnostics(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_google_weather_api: AsyncMock,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test diagnostics."""
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert await get_diagnostics_for_config_entry(
|
||||
hass, hass_client, mock_config_entry
|
||||
) == snapshot(exclude=props("entry_id"))
|
||||
@@ -1,4 +1,54 @@
|
||||
# serializer version: 1
|
||||
# name: test_buttons[button.roborock_q10_s5_empty_dustbin-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': None,
|
||||
'entity_id': 'button.roborock_q10_s5_empty_dustbin',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Empty dustbin',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Empty dustbin',
|
||||
'platform': 'roborock',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'empty_dustbin',
|
||||
'unique_id': 'empty_dustbin_q10_duid',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[button.roborock_q10_s5_empty_dustbin-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Roborock Q10 S5+ Empty dustbin',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.roborock_q10_s5_empty_dustbin',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[button.roborock_s7_2_reset_air_filter_consumable-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
|
||||
@@ -272,3 +272,55 @@ async def test_press_a01_button_failure(
|
||||
|
||||
washing_machine.zeo.set_value.assert_called_once()
|
||||
assert hass.states.get(entity_id).state == "2023-10-30T08:50:00+00:00"
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2023-10-30 08:50:00")
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_press_q10_empty_dustbin_button_success(
|
||||
hass: HomeAssistant,
|
||||
bypass_api_client_fixture: None,
|
||||
setup_entry: MockConfigEntry,
|
||||
fake_q10_vacuum: FakeDevice,
|
||||
) -> None:
|
||||
"""Test pressing Q10 empty dustbin button entity."""
|
||||
entity_id = "button.roborock_q10_s5_empty_dustbin"
|
||||
|
||||
assert hass.states.get(entity_id) is not None
|
||||
await hass.services.async_call(
|
||||
"button",
|
||||
SERVICE_PRESS,
|
||||
blocking=True,
|
||||
target={"entity_id": entity_id},
|
||||
)
|
||||
|
||||
assert fake_q10_vacuum.b01_q10_properties is not None
|
||||
fake_q10_vacuum.b01_q10_properties.vacuum.empty_dustbin.assert_called_once()
|
||||
assert hass.states.get(entity_id).state == "2023-10-30T08:50:00+00:00"
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2023-10-30 08:50:00")
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_press_q10_empty_dustbin_button_failure(
|
||||
hass: HomeAssistant,
|
||||
bypass_api_client_fixture: None,
|
||||
setup_entry: MockConfigEntry,
|
||||
fake_q10_vacuum: FakeDevice,
|
||||
) -> None:
|
||||
"""Test failure while pressing Q10 empty dustbin button entity."""
|
||||
entity_id = "button.roborock_q10_s5_empty_dustbin"
|
||||
assert fake_q10_vacuum.b01_q10_properties is not None
|
||||
fake_q10_vacuum.b01_q10_properties.vacuum.empty_dustbin.side_effect = (
|
||||
RoborockException
|
||||
)
|
||||
|
||||
assert hass.states.get(entity_id) is not None
|
||||
with pytest.raises(HomeAssistantError, match="Error while calling empty_dustbin"):
|
||||
await hass.services.async_call(
|
||||
"button",
|
||||
SERVICE_PRESS,
|
||||
blocking=True,
|
||||
target={"entity_id": entity_id},
|
||||
)
|
||||
|
||||
fake_q10_vacuum.b01_q10_properties.vacuum.empty_dustbin.assert_called_once()
|
||||
assert hass.states.get(entity_id).state == "2023-10-30T08:50:00+00:00"
|
||||
|
||||
@@ -74,6 +74,7 @@ DEVICE_FIXTURES = [
|
||||
"da_wm_dw_01011",
|
||||
"da_rvc_normal_000001",
|
||||
"da_rvc_map_01011",
|
||||
"da_vc_stick_01001",
|
||||
"da_ks_microwave_0101x",
|
||||
"da_ks_cooktop_000001",
|
||||
"da_ks_cooktop_31001",
|
||||
|
||||
@@ -0,0 +1,426 @@
|
||||
{
|
||||
"components": {
|
||||
"station": {
|
||||
"samsungce.cleanStationStickStatus": {
|
||||
"status": {
|
||||
"value": "attached",
|
||||
"timestamp": "2026-03-21T15:25:21.619Z"
|
||||
}
|
||||
},
|
||||
"custom.disabledCapabilities": {
|
||||
"disabledCapabilities": {
|
||||
"value": ["samsungce.cleanStationUvCleaning"],
|
||||
"timestamp": "2026-03-21T14:44:08.042Z"
|
||||
}
|
||||
},
|
||||
"samsungce.cleanStationUvCleaning": {
|
||||
"operatingState": {
|
||||
"value": "ready",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"lastFinishedTime": {
|
||||
"value": null
|
||||
},
|
||||
"uvcIntensive": {
|
||||
"value": null
|
||||
},
|
||||
"operationTime": {
|
||||
"value": null
|
||||
},
|
||||
"remainingTime": {
|
||||
"value": null
|
||||
}
|
||||
},
|
||||
"samsungce.stickCleanerDustBag": {
|
||||
"supportedStatus": {
|
||||
"value": ["full", "normal"],
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"usage": {
|
||||
"value": 343,
|
||||
"timestamp": "2026-03-21T15:25:35.951Z"
|
||||
},
|
||||
"status": {
|
||||
"value": "normal",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"custom.disabledComponents": {
|
||||
"disabledComponents": {
|
||||
"value": [],
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
}
|
||||
},
|
||||
"powerConsumptionReport": {
|
||||
"powerConsumption": {
|
||||
"value": {
|
||||
"energy": 4,
|
||||
"deltaEnergy": 3,
|
||||
"power": 0,
|
||||
"powerEnergy": 0.0,
|
||||
"persistedEnergy": 0,
|
||||
"energySaved": 0,
|
||||
"start": "2026-03-21T15:35:31Z",
|
||||
"end": "2026-03-21T15:41:41Z"
|
||||
},
|
||||
"timestamp": "2026-03-21T15:41:41.889Z"
|
||||
}
|
||||
},
|
||||
"samsungce.stickCleanerStatus": {
|
||||
"operatingState": {
|
||||
"value": "ready",
|
||||
"timestamp": "2026-03-21T15:25:35.978Z"
|
||||
}
|
||||
},
|
||||
"refresh": {},
|
||||
"samsungce.notification": {
|
||||
"supportedActionSettings": {
|
||||
"value": [
|
||||
{
|
||||
"action": "stop",
|
||||
"supportedSettings": ["on", "off"]
|
||||
}
|
||||
],
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"actionSetting": {
|
||||
"value": {
|
||||
"stop": {
|
||||
"setting": "on"
|
||||
}
|
||||
},
|
||||
"timestamp": "2026-03-21T14:50:31.556Z"
|
||||
},
|
||||
"supportedContexts": {
|
||||
"value": ["incomingCall", "messageReceived"],
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"supportCustomContent": {
|
||||
"value": false,
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
}
|
||||
},
|
||||
"samsungce.stickCleanerDustbinStatus": {
|
||||
"operatingState": {
|
||||
"value": "ready",
|
||||
"timestamp": "2026-03-21T15:25:35.978Z"
|
||||
},
|
||||
"lastEmptiedTime": {
|
||||
"value": "2026-03-21T15:25:00Z",
|
||||
"timestamp": "2026-03-21T15:25:35.978Z"
|
||||
}
|
||||
},
|
||||
"battery": {
|
||||
"quantity": {
|
||||
"value": null
|
||||
},
|
||||
"battery": {
|
||||
"value": 80,
|
||||
"unit": "%",
|
||||
"timestamp": "2026-03-21T15:41:41.889Z"
|
||||
},
|
||||
"type": {
|
||||
"value": null
|
||||
}
|
||||
},
|
||||
"execute": {
|
||||
"data": {
|
||||
"value": null
|
||||
}
|
||||
},
|
||||
"samsungce.deviceIdentification": {
|
||||
"micomAssayCode": {
|
||||
"value": "50025842",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"modelName": {
|
||||
"value": null
|
||||
},
|
||||
"serialNumber": {
|
||||
"value": null
|
||||
},
|
||||
"serialNumberExtra": {
|
||||
"value": null
|
||||
},
|
||||
"releaseCountry": {
|
||||
"value": null
|
||||
},
|
||||
"modelClassificationCode": {
|
||||
"value": "80030200001711000802000000000000",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"description": {
|
||||
"value": "A-VSWW-TP1-23-VS9700",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"releaseYear": {
|
||||
"value": null
|
||||
},
|
||||
"binaryId": {
|
||||
"value": "A-VSWW-TP1-23-VS9700",
|
||||
"timestamp": "2026-03-21T15:41:41.888Z"
|
||||
}
|
||||
},
|
||||
"sec.wifiConfiguration": {
|
||||
"autoReconnection": {
|
||||
"value": true,
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"minVersion": {
|
||||
"value": "1.0",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"supportedWiFiFreq": {
|
||||
"value": ["2.4G"],
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"supportedAuthType": {
|
||||
"value": ["OPEN", "WEP", "WPA-PSK", "WPA2-PSK", "SAE"],
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"protocolType": {
|
||||
"value": ["helper_hotspot", "ble_ocf"],
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
}
|
||||
},
|
||||
"samsungce.selfCheck": {
|
||||
"result": {
|
||||
"value": "failed",
|
||||
"timestamp": "2026-03-21T15:25:48.370Z"
|
||||
},
|
||||
"supportedActions": {
|
||||
"value": ["start", "cancel"],
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"progress": {
|
||||
"value": 100,
|
||||
"unit": "%",
|
||||
"timestamp": "2026-03-21T15:25:48.370Z"
|
||||
},
|
||||
"errors": {
|
||||
"value": [
|
||||
{
|
||||
"code": "DA_VCS_E_001"
|
||||
}
|
||||
],
|
||||
"timestamp": "2026-03-21T15:25:48.370Z"
|
||||
},
|
||||
"status": {
|
||||
"value": "ready",
|
||||
"timestamp": "2026-03-21T15:25:48.370Z"
|
||||
}
|
||||
},
|
||||
"samsungce.softwareVersion": {
|
||||
"versions": {
|
||||
"value": [
|
||||
{
|
||||
"id": "0",
|
||||
"swType": "Software",
|
||||
"versionNumber": "25051400",
|
||||
"description": "Version"
|
||||
},
|
||||
{
|
||||
"id": "1",
|
||||
"swType": "Firmware",
|
||||
"versionNumber": "00258B23110300",
|
||||
"description": "Version"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"swType": "Firmware",
|
||||
"versionNumber": "00253B23070700",
|
||||
"description": "Version"
|
||||
}
|
||||
],
|
||||
"timestamp": "2026-03-21T14:50:31.808Z"
|
||||
}
|
||||
},
|
||||
"ocf": {
|
||||
"st": {
|
||||
"value": null
|
||||
},
|
||||
"mndt": {
|
||||
"value": null
|
||||
},
|
||||
"mnfv": {
|
||||
"value": "A-VSWW-TP1-23-VS9700_51250514",
|
||||
"timestamp": "2026-03-21T14:50:07.585Z"
|
||||
},
|
||||
"mnhw": {
|
||||
"value": "Realtek",
|
||||
"timestamp": "2026-03-21T14:50:07.585Z"
|
||||
},
|
||||
"di": {
|
||||
"value": "e1f93c0c-6fe0-c65a-a314-c8f7b163c86b",
|
||||
"timestamp": "2026-03-21T14:50:07.585Z"
|
||||
},
|
||||
"mnsl": {
|
||||
"value": "http://www.samsung.com",
|
||||
"timestamp": "2026-03-21T14:50:07.585Z"
|
||||
},
|
||||
"dmv": {
|
||||
"value": "1.2.1",
|
||||
"timestamp": "2026-03-21T14:50:07.585Z"
|
||||
},
|
||||
"n": {
|
||||
"value": "[vacuum] Samsung",
|
||||
"timestamp": "2026-03-21T14:50:07.585Z"
|
||||
},
|
||||
"mnmo": {
|
||||
"value": "A-VSWW-TP1-23-VS9700|50025842|80030200001711000802000000000000",
|
||||
"timestamp": "2026-03-21T14:50:07.585Z"
|
||||
},
|
||||
"vid": {
|
||||
"value": "DA-VC-STICK-01001",
|
||||
"timestamp": "2026-03-21T14:50:07.585Z"
|
||||
},
|
||||
"mnmn": {
|
||||
"value": "Samsung Electronics",
|
||||
"timestamp": "2026-03-21T14:50:07.585Z"
|
||||
},
|
||||
"mnml": {
|
||||
"value": "http://www.samsung.com",
|
||||
"timestamp": "2026-03-21T14:50:07.585Z"
|
||||
},
|
||||
"mnpv": {
|
||||
"value": "SYSTEM 2.0",
|
||||
"timestamp": "2026-03-21T14:50:07.585Z"
|
||||
},
|
||||
"mnos": {
|
||||
"value": "TizenRT 4.0",
|
||||
"timestamp": "2026-03-21T14:50:07.585Z"
|
||||
},
|
||||
"pi": {
|
||||
"value": "e1f93c0c-6fe0-c65a-a314-c8f7b163c86b",
|
||||
"timestamp": "2026-03-21T14:50:07.585Z"
|
||||
},
|
||||
"icv": {
|
||||
"value": "core.1.1.0",
|
||||
"timestamp": "2026-03-21T14:50:07.585Z"
|
||||
}
|
||||
},
|
||||
"samsungce.stickCleanerStickStatus": {
|
||||
"mode": {
|
||||
"value": "none",
|
||||
"timestamp": "2026-03-21T15:25:06.593Z"
|
||||
},
|
||||
"status": {
|
||||
"value": "charging",
|
||||
"timestamp": "2026-03-21T15:25:22.052Z"
|
||||
},
|
||||
"bleConnectionState": {
|
||||
"value": "connected",
|
||||
"timestamp": "2026-03-21T14:50:29.775Z"
|
||||
}
|
||||
},
|
||||
"custom.disabledCapabilities": {
|
||||
"disabledCapabilities": {
|
||||
"value": ["sec.wifiConfiguration"],
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
}
|
||||
},
|
||||
"samsungce.driverVersion": {
|
||||
"versionNumber": {
|
||||
"value": 25040101,
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
}
|
||||
},
|
||||
"samsungce.softwareUpdate": {
|
||||
"targetModule": {
|
||||
"value": {},
|
||||
"timestamp": "2026-03-21T14:44:08.245Z"
|
||||
},
|
||||
"otnDUID": {
|
||||
"value": "BDCPH4AI7GMCS",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"lastUpdatedDate": {
|
||||
"value": null
|
||||
},
|
||||
"availableModules": {
|
||||
"value": [],
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"newVersionAvailable": {
|
||||
"value": false,
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"operatingState": {
|
||||
"value": "none",
|
||||
"timestamp": "2026-03-21T14:44:08.245Z"
|
||||
},
|
||||
"progress": {
|
||||
"value": 0,
|
||||
"unit": "%",
|
||||
"timestamp": "2026-03-21T14:50:09.045Z"
|
||||
}
|
||||
},
|
||||
"sec.diagnosticsInformation": {
|
||||
"logType": {
|
||||
"value": ["errCode", "dump"],
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"endpoint": {
|
||||
"value": "SSM",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"minVersion": {
|
||||
"value": "3.0",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"signinPermission": {
|
||||
"value": null
|
||||
},
|
||||
"setupId": {
|
||||
"value": "VS2",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"protocolType": {
|
||||
"value": "ble_ocf",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"tsId": {
|
||||
"value": "DA01",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"mnId": {
|
||||
"value": "0AJT",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"dumpType": {
|
||||
"value": "file",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
}
|
||||
},
|
||||
"custom.deviceReportStateConfiguration": {
|
||||
"reportStateRealtimePeriod": {
|
||||
"value": null
|
||||
},
|
||||
"reportStateRealtime": {
|
||||
"value": {
|
||||
"state": "enabled",
|
||||
"duration": 10,
|
||||
"unit": "minute"
|
||||
},
|
||||
"timestamp": "2026-03-21T14:44:42.985Z"
|
||||
},
|
||||
"reportStatePeriod": {
|
||||
"value": "enabled",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
}
|
||||
},
|
||||
"samsungce.lamp": {
|
||||
"brightnessLevel": {
|
||||
"value": "on",
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
},
|
||||
"supportedBrightnessLevel": {
|
||||
"value": ["on", "off"],
|
||||
"timestamp": "2026-03-21T14:44:06.339Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"deviceId": "e1f93c0c-6fe0-c65a-a314-c8f7b163c86b",
|
||||
"name": "[vacuum] Samsung",
|
||||
"label": "Stick vacuum",
|
||||
"manufacturerName": "Samsung Electronics",
|
||||
"presentationId": "DA-VC-STICK-01001",
|
||||
"deviceManufacturerCode": "Samsung Electronics",
|
||||
"locationId": "03f25476-ce87-4f94-b153-03d40451dee0",
|
||||
"ownerId": "62619912-9710-ee72-bdf7-6e3910560913",
|
||||
"roomId": "2f820695-73c1-4d43-8ee9-7c6a07feeb9a",
|
||||
"deviceTypeName": "x.com.st.d.stickcleaner",
|
||||
"components": [
|
||||
{
|
||||
"id": "main",
|
||||
"label": "main",
|
||||
"capabilities": [
|
||||
{
|
||||
"id": "ocf",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "execute",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "powerConsumptionReport",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "refresh",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "samsungce.deviceIdentification",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "samsungce.driverVersion",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "samsungce.stickCleanerStickStatus",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "battery",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "samsungce.lamp",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "samsungce.notification",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "samsungce.selfCheck",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "samsungce.stickCleanerDustbinStatus",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "samsungce.softwareUpdate",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "samsungce.softwareVersion",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "samsungce.stickCleanerStatus",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "custom.deviceReportStateConfiguration",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "custom.disabledComponents",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "custom.disabledCapabilities",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "sec.diagnosticsInformation",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "sec.wifiConfiguration",
|
||||
"version": 1
|
||||
}
|
||||
],
|
||||
"categories": [
|
||||
{
|
||||
"name": "StickVacuumCleaner",
|
||||
"categoryType": "manufacturer"
|
||||
}
|
||||
],
|
||||
"optional": false
|
||||
},
|
||||
{
|
||||
"id": "station",
|
||||
"label": "station",
|
||||
"capabilities": [
|
||||
{
|
||||
"id": "samsungce.stickCleanerDustBag",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "samsungce.cleanStationStickStatus",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "samsungce.cleanStationUvCleaning",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "custom.disabledCapabilities",
|
||||
"version": 1
|
||||
}
|
||||
],
|
||||
"categories": [
|
||||
{
|
||||
"name": "Other",
|
||||
"categoryType": "manufacturer"
|
||||
}
|
||||
],
|
||||
"optional": false
|
||||
}
|
||||
],
|
||||
"createTime": "2026-03-21T14:43:55.855Z",
|
||||
"profile": {
|
||||
"id": "21c15481-d69b-34a9-86a6-bcdb478a68cb"
|
||||
},
|
||||
"ocf": {
|
||||
"ocfDeviceType": "x.com.st.d.stickcleaner",
|
||||
"name": "[vacuum] Samsung",
|
||||
"specVersion": "core.1.1.0",
|
||||
"verticalDomainSpecVersion": "1.2.1",
|
||||
"manufacturerName": "Samsung Electronics",
|
||||
"modelNumber": "A-VSWW-TP1-23-VS9700|50025842|80030200001711000802000000000000",
|
||||
"platformVersion": "SYSTEM 2.0",
|
||||
"platformOS": "TizenRT 4.0",
|
||||
"hwVersion": "Realtek",
|
||||
"firmwareVersion": "A-VSWW-TP1-23-VS9700_51250514",
|
||||
"vendorId": "DA-VC-STICK-01001",
|
||||
"vendorResourceClientServerVersion": "Realtek Release 250514",
|
||||
"lastSignupTime": "2026-03-21T14:43:55.796850354Z",
|
||||
"transferCandidate": true,
|
||||
"additionalAuthCodeRequired": false,
|
||||
"modelCode": "VS28C9784QK/WA"
|
||||
},
|
||||
"type": "OCF",
|
||||
"restrictionTier": 0,
|
||||
"allowed": null,
|
||||
"executionContext": "CLOUD",
|
||||
"relationships": []
|
||||
}
|
||||
],
|
||||
"_links": {}
|
||||
}
|
||||
@@ -2023,6 +2023,56 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][binary_sensor.stick_vacuum_stick_cleaner_in_station-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'binary_sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'binary_sensor.stick_vacuum_stick_cleaner_in_station',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Stick cleaner in station',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Stick cleaner in station',
|
||||
'platform': 'smartthings',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'stick_cleaner_status',
|
||||
'unique_id': 'e1f93c0c-6fe0-c65a-a314-c8f7b163c86b_station_samsungce.cleanStationStickStatus_status_status',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][binary_sensor.stick_vacuum_stick_cleaner_in_station-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Stick vacuum Stick cleaner in station',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.stick_vacuum_stick_cleaner_in_station',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_wm_dw_000001][binary_sensor.dishwasher_child_lock-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
|
||||
@@ -1239,6 +1239,37 @@
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_devices[da_vc_stick_01001]
|
||||
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': 'Realtek',
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'smartthings',
|
||||
'e1f93c0c-6fe0-c65a-a314-c8f7b163c86b',
|
||||
),
|
||||
}),
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'Samsung Electronics',
|
||||
'model': 'A-VSWW-TP1-23-VS9700',
|
||||
'model_id': 'VS28C9784QK/WA',
|
||||
'name': 'Stick vacuum',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': None,
|
||||
'sw_version': 'A-VSWW-TP1-23-VS9700_51250514',
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_devices[da_wm_dw_000001]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': 'theater',
|
||||
|
||||
@@ -851,6 +851,65 @@
|
||||
'state': 'medium',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][select.stick_vacuum_lamp-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'on',
|
||||
'off',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'select.stick_vacuum_lamp',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Lamp',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Lamp',
|
||||
'platform': 'smartthings',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'lamp',
|
||||
'unique_id': 'e1f93c0c-6fe0-c65a-a314-c8f7b163c86b_main_samsungce.lamp_brightnessLevel_brightnessLevel',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][select.stick_vacuum_lamp-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Stick vacuum Lamp',
|
||||
'options': list([
|
||||
'on',
|
||||
'off',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.stick_vacuum_lamp',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_wm_dw_000001][select.dishwasher-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
|
||||
@@ -13340,6 +13340,350 @@
|
||||
'state': 'room',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][sensor.stick_vacuum_battery-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'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': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.stick_vacuum_battery',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Battery',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.BATTERY: 'battery'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Battery',
|
||||
'platform': 'smartthings',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'e1f93c0c-6fe0-c65a-a314-c8f7b163c86b_main_battery_battery_battery',
|
||||
'unit_of_measurement': '%',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][sensor.stick_vacuum_battery-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'battery',
|
||||
'friendly_name': 'Stick vacuum Battery',
|
||||
'unit_of_measurement': '%',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.stick_vacuum_battery',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '80',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][sensor.stick_vacuum_energy-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'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.stick_vacuum_energy',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Energy',
|
||||
'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,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'e1f93c0c-6fe0-c65a-a314-c8f7b163c86b_main_powerConsumptionReport_powerConsumption_energy_meter',
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][sensor.stick_vacuum_energy-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'energy',
|
||||
'friendly_name': 'Stick vacuum Energy',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.stick_vacuum_energy',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '0.004',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][sensor.stick_vacuum_energy_difference-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'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.stick_vacuum_energy_difference',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Energy difference',
|
||||
'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,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'energy_difference',
|
||||
'unique_id': 'e1f93c0c-6fe0-c65a-a314-c8f7b163c86b_main_powerConsumptionReport_powerConsumption_deltaEnergy_meter',
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][sensor.stick_vacuum_energy_difference-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'energy',
|
||||
'friendly_name': 'Stick vacuum Energy difference',
|
||||
'state_class': <SensorStateClass.TOTAL: 'total'>,
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.stick_vacuum_energy_difference',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '0.003',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][sensor.stick_vacuum_energy_saved-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'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.stick_vacuum_energy_saved',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Energy saved',
|
||||
'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,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'energy_saved',
|
||||
'unique_id': 'e1f93c0c-6fe0-c65a-a314-c8f7b163c86b_main_powerConsumptionReport_powerConsumption_energySaved_meter',
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][sensor.stick_vacuum_energy_saved-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'energy',
|
||||
'friendly_name': 'Stick vacuum Energy saved',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.stick_vacuum_energy_saved',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '0.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][sensor.stick_vacuum_power-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'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.stick_vacuum_power',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Power',
|
||||
'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,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'e1f93c0c-6fe0-c65a-a314-c8f7b163c86b_main_powerConsumptionReport_powerConsumption_power_meter',
|
||||
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][sensor.stick_vacuum_power-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'power',
|
||||
'friendly_name': 'Stick vacuum Power',
|
||||
'power_consumption_end': '2026-03-21T15:41:41Z',
|
||||
'power_consumption_start': '2026-03-21T15:35:31Z',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.stick_vacuum_power',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '0',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][sensor.stick_vacuum_power_energy-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'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.stick_vacuum_power_energy',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Power energy',
|
||||
'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,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'power_energy',
|
||||
'unique_id': 'e1f93c0c-6fe0-c65a-a314-c8f7b163c86b_main_powerConsumptionReport_powerConsumption_powerEnergy_meter',
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_vc_stick_01001][sensor.stick_vacuum_power_energy-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'energy',
|
||||
'friendly_name': 'Stick vacuum Power energy',
|
||||
'state_class': <SensorStateClass.TOTAL: 'total'>,
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.stick_vacuum_power_energy',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '0.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_wm_dw_000001][sensor.dishwasher_completion_time-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
|
||||
@@ -6,6 +6,7 @@ from pysmlight import Info, Sensors
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.components.smlight.const import DOMAIN
|
||||
from homeassistant.const import STATE_UNKNOWN, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -113,3 +114,55 @@ async def test_zigbee_type_sensors(
|
||||
state = hass.states.get("sensor.mock_title_zigbee_type_2")
|
||||
assert state
|
||||
assert state.state == "router"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_psram_usage_sensor(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test PSRAM usage sensor creation for u-devices."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = Info(
|
||||
MAC="AA:BB:CC:DD:EE:FF",
|
||||
model="SLZB-MR3U",
|
||||
u_device=True,
|
||||
)
|
||||
mock_smlight_client.get_sensors.return_value = Sensors(psram_usage=156)
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = entity_registry.async_get_entity_id(
|
||||
SENSOR_DOMAIN, DOMAIN, "aa:bb:cc:dd:ee:ff_psram_usage"
|
||||
)
|
||||
assert entity_id is not None
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == "156"
|
||||
assert state.attributes["unit_of_measurement"] == "kB"
|
||||
|
||||
|
||||
async def test_psram_usage_sensor_not_created(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test PSRAM usage sensor is not created for non-u devices."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = Info(
|
||||
MAC="AA:BB:CC:DD:EE:FF",
|
||||
model="SLZB-MR3",
|
||||
u_device=False,
|
||||
)
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
assert hass.states.get("sensor.mock_title_psram_usage") is None
|
||||
|
||||
entity_id = entity_registry.async_get_entity_id(
|
||||
SENSOR_DOMAIN, DOMAIN, "aa:bb:cc:dd:ee:ff_psram_usage"
|
||||
)
|
||||
assert entity_id is None
|
||||
|
||||
@@ -75,6 +75,7 @@ def mock_window() -> AsyncMock:
|
||||
window.is_opening = False
|
||||
window.is_closing = False
|
||||
window.position = MagicMock(position_percent=30, closed=False)
|
||||
window.wink = AsyncMock()
|
||||
window.pyvlx = MagicMock()
|
||||
return window
|
||||
|
||||
@@ -213,7 +214,6 @@ def mock_pyvlx(
|
||||
mock_blind,
|
||||
mock_window,
|
||||
mock_exterior_heating,
|
||||
mock_cover_type,
|
||||
]
|
||||
|
||||
pyvlx.scenes = [mock_scene]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# serializer version: 1
|
||||
# name: test_button_snapshot[button.klf_200_gateway_restart-entry]
|
||||
# name: test_button_snapshot[mock_window][button.klf_200_gateway_restart-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
@@ -36,7 +36,7 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_button_snapshot[button.klf_200_gateway_restart-state]
|
||||
# name: test_button_snapshot[mock_window][button.klf_200_gateway_restart-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'restart',
|
||||
@@ -50,3 +50,54 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_button_snapshot[mock_window][button.test_window_identify-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.test_window_identify',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Identify',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Identify',
|
||||
'platform': 'velux',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '123456789_identify',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_button_snapshot[mock_window][button.test_window_identify-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': 'Test Window Identify',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.test_window_identify',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
|
||||
@@ -23,6 +23,7 @@ def platform() -> Platform:
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("setup_integration")
|
||||
@pytest.mark.parametrize("mock_pyvlx", ["mock_window"], indirect=True)
|
||||
async def test_button_snapshot(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
@@ -38,18 +39,33 @@ async def test_button_snapshot(
|
||||
mock_config_entry.entry_id,
|
||||
)
|
||||
|
||||
# Get the button entity setup and test device association
|
||||
entity_entries = er.async_entries_for_config_entry(
|
||||
entity_registry, mock_config_entry.entry_id
|
||||
)
|
||||
assert len(entity_entries) == 1
|
||||
entry = entity_entries[0]
|
||||
assert len(entity_entries) == 2
|
||||
|
||||
assert entry.device_id is not None
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry is not None
|
||||
assert (DOMAIN, f"gateway_{mock_config_entry.entry_id}") in device_entry.identifiers
|
||||
assert device_entry.via_device_id is None
|
||||
# Check Reboot button is associated with the gateway device
|
||||
reboot_entry = next(
|
||||
e for e in entity_entries if e.entity_id == "button.klf_200_gateway_restart"
|
||||
)
|
||||
assert reboot_entry.device_id is not None
|
||||
gateway_device = device_registry.async_get(reboot_entry.device_id)
|
||||
assert gateway_device is not None
|
||||
assert (
|
||||
DOMAIN,
|
||||
f"gateway_{mock_config_entry.entry_id}",
|
||||
) in gateway_device.identifiers
|
||||
assert gateway_device.via_device_id is None
|
||||
|
||||
# Check Identify button is associated with the node device via the gateway
|
||||
identify_entry = next(
|
||||
e for e in entity_entries if e.entity_id == "button.test_window_identify"
|
||||
)
|
||||
assert identify_entry.device_id is not None
|
||||
node_device = device_registry.async_get(identify_entry.device_id)
|
||||
assert node_device is not None
|
||||
assert (DOMAIN, "123456789") in node_device.identifiers
|
||||
assert node_device.via_device_id == gateway_device.id
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("setup_integration")
|
||||
@@ -98,3 +114,54 @@ async def test_button_press_failure(
|
||||
|
||||
# Verify the reboot method was called
|
||||
mock_pyvlx.reboot_gateway.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("setup_integration")
|
||||
@pytest.mark.parametrize("mock_pyvlx", ["mock_window"], indirect=True)
|
||||
async def test_identify_button_press_success(
|
||||
hass: HomeAssistant,
|
||||
mock_window: AsyncMock,
|
||||
) -> None:
|
||||
"""Test successful identify button press."""
|
||||
|
||||
entity_id = "button.test_window_identify"
|
||||
|
||||
# Press the button
|
||||
await hass.services.async_call(
|
||||
BUTTON_DOMAIN,
|
||||
SERVICE_PRESS,
|
||||
{ATTR_ENTITY_ID: entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
# Verify the wink method was called
|
||||
mock_window.wink.assert_awaited_once()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("setup_integration")
|
||||
@pytest.mark.parametrize("mock_pyvlx", ["mock_window"], indirect=True)
|
||||
async def test_identify_button_press_failure(
|
||||
hass: HomeAssistant,
|
||||
mock_window: AsyncMock,
|
||||
) -> None:
|
||||
"""Test identify button press failure handling."""
|
||||
|
||||
entity_id = "button.test_window_identify"
|
||||
|
||||
# Mock wink failure
|
||||
mock_window.wink.side_effect = PyVLXException("Connection failed")
|
||||
|
||||
# Press the button and expect HomeAssistantError
|
||||
with pytest.raises(
|
||||
HomeAssistantError,
|
||||
match='Failed to communicate with Velux device: <PyVLXException description="Connection failed" />',
|
||||
):
|
||||
await hass.services.async_call(
|
||||
BUTTON_DOMAIN,
|
||||
SERVICE_PRESS,
|
||||
{ATTR_ENTITY_ID: entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
# Verify the wink method was called
|
||||
mock_window.wink.assert_awaited_once()
|
||||
|
||||
Reference in New Issue
Block a user