mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 07:37:34 +00:00
2024.7.1 (#121289)
This commit is contained in:
commit
1cf62916a7
@ -6,5 +6,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/anova",
|
"documentation": "https://www.home-assistant.io/integrations/anova",
|
||||||
"iot_class": "cloud_push",
|
"iot_class": "cloud_push",
|
||||||
"loggers": ["anova_wifi"],
|
"loggers": ["anova_wifi"],
|
||||||
"requirements": ["anova-wifi==0.14.0"]
|
"requirements": ["anova-wifi==0.15.0"]
|
||||||
}
|
}
|
||||||
|
@ -8,5 +8,5 @@
|
|||||||
"integration_type": "device",
|
"integration_type": "device",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"loggers": ["aioaquacell"],
|
"loggers": ["aioaquacell"],
|
||||||
"requirements": ["aioaquacell==0.1.7"]
|
"requirements": ["aioaquacell==0.1.8"]
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
from typing import cast
|
from typing import cast
|
||||||
|
|
||||||
from aiohttp import ClientResponseError
|
from aiohttp import ClientResponseError
|
||||||
from path import Path
|
|
||||||
from yalexs.exceptions import AugustApiAIOHTTPError
|
from yalexs.exceptions import AugustApiAIOHTTPError
|
||||||
from yalexs.manager.exceptions import CannotConnect, InvalidAuth, RequireValidation
|
from yalexs.manager.exceptions import CannotConnect, InvalidAuth, RequireValidation
|
||||||
from yalexs.manager.gateway import Config as YaleXSConfig
|
from yalexs.manager.gateway import Config as YaleXSConfig
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/ecovacs",
|
"documentation": "https://www.home-assistant.io/integrations/ecovacs",
|
||||||
"iot_class": "cloud_push",
|
"iot_class": "cloud_push",
|
||||||
"loggers": ["sleekxmppfs", "sucks", "deebot_client"],
|
"loggers": ["sleekxmppfs", "sucks", "deebot_client"],
|
||||||
"requirements": ["py-sucks==0.9.10", "deebot-client==8.1.0"]
|
"requirements": ["py-sucks==0.9.10", "deebot-client==8.1.1"]
|
||||||
}
|
}
|
||||||
|
@ -20,5 +20,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||||
"integration_type": "system",
|
"integration_type": "system",
|
||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
"requirements": ["home-assistant-frontend==20240703.0"]
|
"requirements": ["home-assistant-frontend==20240705.0"]
|
||||||
}
|
}
|
||||||
|
@ -5,5 +5,5 @@
|
|||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/holiday",
|
"documentation": "https://www.home-assistant.io/integrations/holiday",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"requirements": ["holidays==0.51", "babel==2.15.0"]
|
"requirements": ["holidays==0.52", "babel==2.15.0"]
|
||||||
}
|
}
|
||||||
|
@ -216,14 +216,13 @@ class HomematicipGenericEntity(Entity):
|
|||||||
@property
|
@property
|
||||||
def unique_id(self) -> str:
|
def unique_id(self) -> str:
|
||||||
"""Return a unique ID."""
|
"""Return a unique ID."""
|
||||||
suffix = ""
|
unique_id = f"{self.__class__.__name__}_{self._device.id}"
|
||||||
if self._post is not None:
|
|
||||||
suffix = f"_{self._post}"
|
|
||||||
|
|
||||||
if self._is_multi_channel:
|
if self._is_multi_channel:
|
||||||
return f"{self.__class__.__name__}_Channel{self._channel}_{self._device.id}{suffix}"
|
unique_id = (
|
||||||
|
f"{self.__class__.__name__}_Channel{self._channel}_{self._device.id}"
|
||||||
|
)
|
||||||
|
|
||||||
return f"{self.__class__.__name__}_{self._device.id}{suffix}"
|
return unique_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def icon(self) -> str | None:
|
def icon(self) -> str | None:
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from homematicip.aio.device import (
|
from homematicip.aio.device import (
|
||||||
@ -36,7 +35,6 @@ from homematicip.base.functionalChannels import FunctionalChannel
|
|||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
SensorEntityDescription,
|
|
||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
@ -163,19 +161,28 @@ async def async_setup_entry(
|
|||||||
for ch in get_channels_from_device(
|
for ch in get_channels_from_device(
|
||||||
device, FunctionalChannelType.ENERGY_SENSORS_INTERFACE_CHANNEL
|
device, FunctionalChannelType.ENERGY_SENSORS_INTERFACE_CHANNEL
|
||||||
):
|
):
|
||||||
if ch.connectedEnergySensorType not in SENSORS_ESI:
|
if ch.connectedEnergySensorType == ESI_CONNECTED_SENSOR_TYPE_IEC:
|
||||||
continue
|
if ch.currentPowerConsumption is not None:
|
||||||
|
entities.append(HmipEsiIecPowerConsumption(hap, device))
|
||||||
|
if ch.energyCounterOneType != ESI_TYPE_UNKNOWN:
|
||||||
|
entities.append(HmipEsiIecEnergyCounterHighTariff(hap, device))
|
||||||
|
if ch.energyCounterTwoType != ESI_TYPE_UNKNOWN:
|
||||||
|
entities.append(HmipEsiIecEnergyCounterLowTariff(hap, device))
|
||||||
|
if ch.energyCounterThreeType != ESI_TYPE_UNKNOWN:
|
||||||
|
entities.append(
|
||||||
|
HmipEsiIecEnergyCounterInputSingleTariff(hap, device)
|
||||||
|
)
|
||||||
|
|
||||||
new_entities = [
|
if ch.connectedEnergySensorType == ESI_CONNECTED_SENSOR_TYPE_GAS:
|
||||||
HmipEsiSensorEntity(hap, device, ch.index, description)
|
if ch.currentGasFlow is not None:
|
||||||
for description in SENSORS_ESI[ch.connectedEnergySensorType]
|
entities.append(HmipEsiGasCurrentGasFlow(hap, device))
|
||||||
]
|
if ch.gasVolume is not None:
|
||||||
|
entities.append(HmipEsiGasGasVolume(hap, device))
|
||||||
|
|
||||||
entities.extend(
|
if ch.connectedEnergySensorType == ESI_CONNECTED_SENSOR_TYPE_LED:
|
||||||
entity
|
if ch.currentPowerConsumption is not None:
|
||||||
for entity in new_entities
|
entities.append(HmipEsiLedCurrentPowerConsumption(hap, device))
|
||||||
if entity.entity_description.exists_fn(ch)
|
entities.append(HmipEsiLedEnergyCounterHighTariff(hap, device))
|
||||||
)
|
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
@ -434,132 +441,185 @@ class HomematicpTemperatureExternalSensorDelta(HomematicipGenericEntity, SensorE
|
|||||||
return self._device.temperatureExternalDelta
|
return self._device.temperatureExternalDelta
|
||||||
|
|
||||||
|
|
||||||
@dataclass(kw_only=True, frozen=True)
|
|
||||||
class HmipEsiSensorEntityDescription(SensorEntityDescription):
|
|
||||||
"""SensorEntityDescription for HmIP Sensors."""
|
|
||||||
|
|
||||||
value_fn: Callable[[AsyncEnergySensorsInterface], StateType]
|
|
||||||
exists_fn: Callable[[FunctionalChannel], bool]
|
|
||||||
type_fn: Callable[[AsyncEnergySensorsInterface], str]
|
|
||||||
|
|
||||||
|
|
||||||
SENSORS_ESI = {
|
|
||||||
ESI_CONNECTED_SENSOR_TYPE_IEC: [
|
|
||||||
HmipEsiSensorEntityDescription(
|
|
||||||
key=ESI_TYPE_CURRENT_POWER_CONSUMPTION,
|
|
||||||
native_unit_of_measurement=UnitOfPower.WATT,
|
|
||||||
device_class=SensorDeviceClass.POWER,
|
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
|
||||||
value_fn=lambda device: device.functional_channel.currentPowerConsumption,
|
|
||||||
exists_fn=lambda channel: channel.currentPowerConsumption is not None,
|
|
||||||
type_fn=lambda device: "CurrentPowerConsumption",
|
|
||||||
),
|
|
||||||
HmipEsiSensorEntityDescription(
|
|
||||||
key=ESI_TYPE_ENERGY_COUNTER_USAGE_HIGH_TARIFF,
|
|
||||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
|
||||||
device_class=SensorDeviceClass.ENERGY,
|
|
||||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
|
||||||
value_fn=lambda device: device.functional_channel.energyCounterOne,
|
|
||||||
exists_fn=lambda channel: channel.energyCounterOneType != ESI_TYPE_UNKNOWN,
|
|
||||||
type_fn=lambda device: device.functional_channel.energyCounterOneType,
|
|
||||||
),
|
|
||||||
HmipEsiSensorEntityDescription(
|
|
||||||
key=ESI_TYPE_ENERGY_COUNTER_USAGE_LOW_TARIFF,
|
|
||||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
|
||||||
device_class=SensorDeviceClass.ENERGY,
|
|
||||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
|
||||||
value_fn=lambda device: device.functional_channel.energyCounterTwo,
|
|
||||||
exists_fn=lambda channel: channel.energyCounterTwoType != ESI_TYPE_UNKNOWN,
|
|
||||||
type_fn=lambda device: device.functional_channel.energyCounterTwoType,
|
|
||||||
),
|
|
||||||
HmipEsiSensorEntityDescription(
|
|
||||||
key=ESI_TYPE_ENERGY_COUNTER_INPUT_SINGLE_TARIFF,
|
|
||||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
|
||||||
device_class=SensorDeviceClass.ENERGY,
|
|
||||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
|
||||||
value_fn=lambda device: device.functional_channel.energyCounterThree,
|
|
||||||
exists_fn=lambda channel: channel.energyCounterThreeType
|
|
||||||
!= ESI_TYPE_UNKNOWN,
|
|
||||||
type_fn=lambda device: device.functional_channel.energyCounterThreeType,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
ESI_CONNECTED_SENSOR_TYPE_LED: [
|
|
||||||
HmipEsiSensorEntityDescription(
|
|
||||||
key=ESI_TYPE_CURRENT_POWER_CONSUMPTION,
|
|
||||||
native_unit_of_measurement=UnitOfPower.WATT,
|
|
||||||
device_class=SensorDeviceClass.POWER,
|
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
|
||||||
value_fn=lambda device: device.functional_channel.currentPowerConsumption,
|
|
||||||
exists_fn=lambda channel: channel.currentPowerConsumption is not None,
|
|
||||||
type_fn=lambda device: "CurrentPowerConsumption",
|
|
||||||
),
|
|
||||||
HmipEsiSensorEntityDescription(
|
|
||||||
key=ESI_TYPE_ENERGY_COUNTER_USAGE_HIGH_TARIFF,
|
|
||||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
|
||||||
device_class=SensorDeviceClass.ENERGY,
|
|
||||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
|
||||||
value_fn=lambda device: device.functional_channel.energyCounterOne,
|
|
||||||
exists_fn=lambda channel: channel.energyCounterOne is not None,
|
|
||||||
type_fn=lambda device: ESI_TYPE_ENERGY_COUNTER_USAGE_HIGH_TARIFF,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
ESI_CONNECTED_SENSOR_TYPE_GAS: [
|
|
||||||
HmipEsiSensorEntityDescription(
|
|
||||||
key=ESI_TYPE_CURRENT_GAS_FLOW,
|
|
||||||
native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR,
|
|
||||||
device_class=SensorDeviceClass.VOLUME_FLOW_RATE,
|
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
|
||||||
value_fn=lambda device: device.functional_channel.currentGasFlow,
|
|
||||||
exists_fn=lambda channel: channel.currentGasFlow is not None,
|
|
||||||
type_fn=lambda device: "CurrentGasFlow",
|
|
||||||
),
|
|
||||||
HmipEsiSensorEntityDescription(
|
|
||||||
key=ESI_TYPE_CURRENT_GAS_VOLUME,
|
|
||||||
native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
|
|
||||||
device_class=SensorDeviceClass.VOLUME,
|
|
||||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
|
||||||
value_fn=lambda device: device.functional_channel.gasVolume,
|
|
||||||
exists_fn=lambda channel: channel.gasVolume is not None,
|
|
||||||
type_fn=lambda device: "GasVolume",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class HmipEsiSensorEntity(HomematicipGenericEntity, SensorEntity):
|
class HmipEsiSensorEntity(HomematicipGenericEntity, SensorEntity):
|
||||||
"""EntityDescription for HmIP-ESI Sensors."""
|
"""EntityDescription for HmIP-ESI Sensors."""
|
||||||
|
|
||||||
entity_description: HmipEsiSensorEntityDescription
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
hap: HomematicipHAP,
|
hap: HomematicipHAP,
|
||||||
device: HomematicipGenericEntity,
|
device: HomematicipGenericEntity,
|
||||||
channel_index: int,
|
key: str,
|
||||||
entity_description: HmipEsiSensorEntityDescription,
|
value_fn: Callable[[FunctionalChannel], StateType],
|
||||||
|
type_fn: Callable[[FunctionalChannel], str],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize Sensor Entity."""
|
"""Initialize Sensor Entity."""
|
||||||
super().__init__(
|
super().__init__(
|
||||||
hap=hap,
|
hap=hap,
|
||||||
device=device,
|
device=device,
|
||||||
channel=channel_index,
|
channel=1,
|
||||||
post=entity_description.key,
|
post=key,
|
||||||
is_multi_channel=False,
|
is_multi_channel=False,
|
||||||
)
|
)
|
||||||
self.entity_description = entity_description
|
|
||||||
|
self._value_fn = value_fn
|
||||||
|
self._type_fn = type_fn
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def extra_state_attributes(self) -> dict[str, Any]:
|
def extra_state_attributes(self) -> dict[str, Any]:
|
||||||
"""Return the state attributes of the esi sensor."""
|
"""Return the state attributes of the esi sensor."""
|
||||||
state_attr = super().extra_state_attributes
|
state_attr = super().extra_state_attributes
|
||||||
state_attr[ATTR_ESI_TYPE] = self.entity_description.type_fn(self)
|
state_attr[ATTR_ESI_TYPE] = self._type_fn(self.functional_channel)
|
||||||
|
|
||||||
return state_attr
|
return state_attr
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def native_value(self) -> str | None:
|
def native_value(self) -> str | None:
|
||||||
"""Return the state of the sensor."""
|
"""Return the state of the sensor."""
|
||||||
return str(self.entity_description.value_fn(self))
|
return str(self._value_fn(self.functional_channel))
|
||||||
|
|
||||||
|
|
||||||
|
class HmipEsiIecPowerConsumption(HmipEsiSensorEntity):
|
||||||
|
"""Representation of the Hmip-ESI IEC currentPowerConsumption sensor."""
|
||||||
|
|
||||||
|
_attr_device_class = SensorDeviceClass.POWER
|
||||||
|
_attr_native_unit_of_measurement = UnitOfPower.WATT
|
||||||
|
_attr_state_class = SensorStateClass.MEASUREMENT
|
||||||
|
|
||||||
|
def __init__(self, hap: HomematicipHAP, device) -> None:
|
||||||
|
"""Initialize the device."""
|
||||||
|
super().__init__(
|
||||||
|
hap,
|
||||||
|
device,
|
||||||
|
key="CurrentPowerConsumption",
|
||||||
|
value_fn=lambda channel: channel.currentPowerConsumption,
|
||||||
|
type_fn=lambda channel: "CurrentPowerConsumption",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class HmipEsiIecEnergyCounterHighTariff(HmipEsiSensorEntity):
|
||||||
|
"""Representation of the Hmip-ESI IEC energyCounterOne sensor."""
|
||||||
|
|
||||||
|
_attr_device_class = SensorDeviceClass.ENERGY
|
||||||
|
_attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
|
||||||
|
_attr_state_class = SensorStateClass.TOTAL_INCREASING
|
||||||
|
|
||||||
|
def __init__(self, hap: HomematicipHAP, device) -> None:
|
||||||
|
"""Initialize the device."""
|
||||||
|
super().__init__(
|
||||||
|
hap,
|
||||||
|
device,
|
||||||
|
key=ESI_TYPE_ENERGY_COUNTER_USAGE_HIGH_TARIFF,
|
||||||
|
value_fn=lambda channel: channel.energyCounterOne,
|
||||||
|
type_fn=lambda channel: channel.energyCounterOneType,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class HmipEsiIecEnergyCounterLowTariff(HmipEsiSensorEntity):
|
||||||
|
"""Representation of the Hmip-ESI IEC energyCounterTwo sensor."""
|
||||||
|
|
||||||
|
_attr_device_class = SensorDeviceClass.ENERGY
|
||||||
|
_attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
|
||||||
|
_attr_state_class = SensorStateClass.TOTAL_INCREASING
|
||||||
|
|
||||||
|
def __init__(self, hap: HomematicipHAP, device) -> None:
|
||||||
|
"""Initialize the device."""
|
||||||
|
super().__init__(
|
||||||
|
hap,
|
||||||
|
device,
|
||||||
|
key=ESI_TYPE_ENERGY_COUNTER_USAGE_LOW_TARIFF,
|
||||||
|
value_fn=lambda channel: channel.energyCounterTwo,
|
||||||
|
type_fn=lambda channel: channel.energyCounterTwoType,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class HmipEsiIecEnergyCounterInputSingleTariff(HmipEsiSensorEntity):
|
||||||
|
"""Representation of the Hmip-ESI IEC energyCounterThree sensor."""
|
||||||
|
|
||||||
|
_attr_device_class = SensorDeviceClass.ENERGY
|
||||||
|
_attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
|
||||||
|
_attr_state_class = SensorStateClass.TOTAL_INCREASING
|
||||||
|
|
||||||
|
def __init__(self, hap: HomematicipHAP, device) -> None:
|
||||||
|
"""Initialize the device."""
|
||||||
|
super().__init__(
|
||||||
|
hap,
|
||||||
|
device,
|
||||||
|
key=ESI_TYPE_ENERGY_COUNTER_INPUT_SINGLE_TARIFF,
|
||||||
|
value_fn=lambda channel: channel.energyCounterThree,
|
||||||
|
type_fn=lambda channel: channel.energyCounterThreeType,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class HmipEsiGasCurrentGasFlow(HmipEsiSensorEntity):
|
||||||
|
"""Representation of the Hmip-ESI Gas currentGasFlow sensor."""
|
||||||
|
|
||||||
|
_attr_device_class = SensorDeviceClass.VOLUME_FLOW_RATE
|
||||||
|
_attr_native_unit_of_measurement = UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR
|
||||||
|
_attr_state_class = SensorStateClass.MEASUREMENT
|
||||||
|
|
||||||
|
def __init__(self, hap: HomematicipHAP, device) -> None:
|
||||||
|
"""Initialize the device."""
|
||||||
|
super().__init__(
|
||||||
|
hap,
|
||||||
|
device,
|
||||||
|
key="CurrentGasFlow",
|
||||||
|
value_fn=lambda channel: channel.currentGasFlow,
|
||||||
|
type_fn=lambda channel: "CurrentGasFlow",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class HmipEsiGasGasVolume(HmipEsiSensorEntity):
|
||||||
|
"""Representation of the Hmip-ESI Gas gasVolume sensor."""
|
||||||
|
|
||||||
|
_attr_device_class = SensorDeviceClass.GAS
|
||||||
|
_attr_native_unit_of_measurement = UnitOfVolume.CUBIC_METERS
|
||||||
|
_attr_state_class = SensorStateClass.TOTAL_INCREASING
|
||||||
|
|
||||||
|
def __init__(self, hap: HomematicipHAP, device) -> None:
|
||||||
|
"""Initialize the device."""
|
||||||
|
super().__init__(
|
||||||
|
hap,
|
||||||
|
device,
|
||||||
|
key="GasVolume",
|
||||||
|
value_fn=lambda channel: channel.gasVolume,
|
||||||
|
type_fn=lambda channel: "GasVolume",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class HmipEsiLedCurrentPowerConsumption(HmipEsiSensorEntity):
|
||||||
|
"""Representation of the Hmip-ESI LED currentPowerConsumption sensor."""
|
||||||
|
|
||||||
|
_attr_device_class = SensorDeviceClass.POWER
|
||||||
|
_attr_native_unit_of_measurement = UnitOfPower.WATT
|
||||||
|
_attr_state_class = SensorStateClass.MEASUREMENT
|
||||||
|
|
||||||
|
def __init__(self, hap: HomematicipHAP, device) -> None:
|
||||||
|
"""Initialize the device."""
|
||||||
|
super().__init__(
|
||||||
|
hap,
|
||||||
|
device,
|
||||||
|
key="CurrentPowerConsumption",
|
||||||
|
value_fn=lambda channel: channel.currentPowerConsumption,
|
||||||
|
type_fn=lambda channel: "CurrentPowerConsumption",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class HmipEsiLedEnergyCounterHighTariff(HmipEsiSensorEntity):
|
||||||
|
"""Representation of the Hmip-ESI LED energyCounterOne sensor."""
|
||||||
|
|
||||||
|
_attr_device_class = SensorDeviceClass.ENERGY
|
||||||
|
_attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
|
||||||
|
_attr_state_class = SensorStateClass.TOTAL_INCREASING
|
||||||
|
|
||||||
|
def __init__(self, hap: HomematicipHAP, device) -> None:
|
||||||
|
"""Initialize the device."""
|
||||||
|
super().__init__(
|
||||||
|
hap,
|
||||||
|
device,
|
||||||
|
key=ESI_TYPE_ENERGY_COUNTER_USAGE_HIGH_TARIFF,
|
||||||
|
value_fn=lambda channel: channel.energyCounterOne,
|
||||||
|
type_fn=lambda channel: ESI_TYPE_ENERGY_COUNTER_USAGE_HIGH_TARIFF,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class HomematicipPassageDetectorDeltaCounter(HomematicipGenericEntity, SensorEntity):
|
class HomematicipPassageDetectorDeltaCounter(HomematicipGenericEntity, SensorEntity):
|
||||||
|
@ -184,6 +184,8 @@ RESTRICTED_REASONS: list = [
|
|||||||
RestrictedReasons.WEEK_SCHEDULE.lower(),
|
RestrictedReasons.WEEK_SCHEDULE.lower(),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
STATE_NO_WORK_AREA_ACTIVE = "no_work_area_active"
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _get_work_area_names(data: MowerAttributes) -> list[str]:
|
def _get_work_area_names(data: MowerAttributes) -> list[str]:
|
||||||
@ -191,16 +193,21 @@ def _get_work_area_names(data: MowerAttributes) -> list[str]:
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
# Sensor does not get created if it is None
|
# Sensor does not get created if it is None
|
||||||
assert data.work_areas is not None
|
assert data.work_areas is not None
|
||||||
return [data.work_areas[work_area_id].name for work_area_id in data.work_areas]
|
work_area_list = [
|
||||||
|
data.work_areas[work_area_id].name for work_area_id in data.work_areas
|
||||||
|
]
|
||||||
|
work_area_list.append(STATE_NO_WORK_AREA_ACTIVE)
|
||||||
|
return work_area_list
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _get_current_work_area_name(data: MowerAttributes) -> str:
|
def _get_current_work_area_name(data: MowerAttributes) -> str:
|
||||||
"""Return the name of the current work area."""
|
"""Return the name of the current work area."""
|
||||||
|
if data.mower.work_area_id is None:
|
||||||
|
return STATE_NO_WORK_AREA_ACTIVE
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
# Sensor does not get created if values are None
|
# Sensor does not get created if values are None
|
||||||
assert data.work_areas is not None
|
assert data.work_areas is not None
|
||||||
assert data.mower.work_area_id is not None
|
|
||||||
return data.work_areas[data.mower.work_area_id].name
|
return data.work_areas[data.mower.work_area_id].name
|
||||||
|
|
||||||
|
|
||||||
|
@ -252,7 +252,8 @@
|
|||||||
"work_area": {
|
"work_area": {
|
||||||
"name": "Work area",
|
"name": "Work area",
|
||||||
"state": {
|
"state": {
|
||||||
"my_lawn": "My lawn"
|
"my_lawn": "My lawn",
|
||||||
|
"no_work_area_active": "No work area active"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -28,5 +28,5 @@
|
|||||||
"dependencies": ["bluetooth_adapters"],
|
"dependencies": ["bluetooth_adapters"],
|
||||||
"documentation": "https://www.home-assistant.io/integrations/inkbird",
|
"documentation": "https://www.home-assistant.io/integrations/inkbird",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"requirements": ["inkbird-ble==0.5.7"]
|
"requirements": ["inkbird-ble==0.5.8"]
|
||||||
}
|
}
|
||||||
|
@ -350,6 +350,7 @@ DISCOVERY_SCHEMAS = [
|
|||||||
clusters.Thermostat.Attributes.TemperatureSetpointHold,
|
clusters.Thermostat.Attributes.TemperatureSetpointHold,
|
||||||
clusters.Thermostat.Attributes.UnoccupiedCoolingSetpoint,
|
clusters.Thermostat.Attributes.UnoccupiedCoolingSetpoint,
|
||||||
clusters.Thermostat.Attributes.UnoccupiedHeatingSetpoint,
|
clusters.Thermostat.Attributes.UnoccupiedHeatingSetpoint,
|
||||||
|
clusters.OnOff.Attributes.OnOff,
|
||||||
),
|
),
|
||||||
device_type=(device_types.Thermostat, device_types.RoomAirConditioner),
|
device_type=(device_types.Thermostat, device_types.RoomAirConditioner),
|
||||||
),
|
),
|
||||||
|
@ -313,6 +313,7 @@ DISCOVERY_SCHEMAS = [
|
|||||||
clusters.FanControl.Attributes.RockSetting,
|
clusters.FanControl.Attributes.RockSetting,
|
||||||
clusters.FanControl.Attributes.WindSetting,
|
clusters.FanControl.Attributes.WindSetting,
|
||||||
clusters.FanControl.Attributes.AirflowDirection,
|
clusters.FanControl.Attributes.AirflowDirection,
|
||||||
|
clusters.OnOff.Attributes.OnOff,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
@ -446,6 +446,8 @@ DISCOVERY_SCHEMAS = [
|
|||||||
device_types.DimmablePlugInUnit,
|
device_types.DimmablePlugInUnit,
|
||||||
device_types.ExtendedColorLight,
|
device_types.ExtendedColorLight,
|
||||||
device_types.OnOffLight,
|
device_types.OnOffLight,
|
||||||
|
device_types.DimmerSwitch,
|
||||||
|
device_types.ColorDimmerSwitch,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
# Additional schema to match (HS Color) lights with incorrect/missing device type
|
# Additional schema to match (HS Color) lights with incorrect/missing device type
|
||||||
|
@ -90,6 +90,9 @@ class MatterLock(MatterEntity, LockEntity):
|
|||||||
|
|
||||||
async def async_lock(self, **kwargs: Any) -> None:
|
async def async_lock(self, **kwargs: Any) -> None:
|
||||||
"""Lock the lock with pin if needed."""
|
"""Lock the lock with pin if needed."""
|
||||||
|
# optimistically signal locking to state machine
|
||||||
|
self._attr_is_locking = True
|
||||||
|
self.async_write_ha_state()
|
||||||
code: str | None = kwargs.get(ATTR_CODE)
|
code: str | None = kwargs.get(ATTR_CODE)
|
||||||
code_bytes = code.encode() if code else None
|
code_bytes = code.encode() if code else None
|
||||||
await self.send_device_command(
|
await self.send_device_command(
|
||||||
@ -98,6 +101,9 @@ class MatterLock(MatterEntity, LockEntity):
|
|||||||
|
|
||||||
async def async_unlock(self, **kwargs: Any) -> None:
|
async def async_unlock(self, **kwargs: Any) -> None:
|
||||||
"""Unlock the lock with pin if needed."""
|
"""Unlock the lock with pin if needed."""
|
||||||
|
# optimistically signal unlocking to state machine
|
||||||
|
self._attr_is_unlocking = True
|
||||||
|
self.async_write_ha_state()
|
||||||
code: str | None = kwargs.get(ATTR_CODE)
|
code: str | None = kwargs.get(ATTR_CODE)
|
||||||
code_bytes = code.encode() if code else None
|
code_bytes = code.encode() if code else None
|
||||||
if self.supports_unbolt:
|
if self.supports_unbolt:
|
||||||
@ -114,6 +120,9 @@ class MatterLock(MatterEntity, LockEntity):
|
|||||||
|
|
||||||
async def async_open(self, **kwargs: Any) -> None:
|
async def async_open(self, **kwargs: Any) -> None:
|
||||||
"""Open the door latch."""
|
"""Open the door latch."""
|
||||||
|
# optimistically signal unlocking to state machine
|
||||||
|
self._attr_is_unlocking = True
|
||||||
|
self.async_write_ha_state()
|
||||||
code: str | None = kwargs.get(ATTR_CODE)
|
code: str | None = kwargs.get(ATTR_CODE)
|
||||||
code_bytes = code.encode() if code else None
|
code_bytes = code.encode() if code else None
|
||||||
await self.send_device_command(
|
await self.send_device_command(
|
||||||
@ -135,26 +144,23 @@ class MatterLock(MatterEntity, LockEntity):
|
|||||||
clusters.DoorLock.Attributes.LockState
|
clusters.DoorLock.Attributes.LockState
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# always reset the optimisically (un)locking state on state update
|
||||||
|
self._attr_is_locking = False
|
||||||
|
self._attr_is_unlocking = False
|
||||||
|
|
||||||
LOGGER.debug("Lock state: %s for %s", lock_state, self.entity_id)
|
LOGGER.debug("Lock state: %s for %s", lock_state, self.entity_id)
|
||||||
|
|
||||||
if lock_state is clusters.DoorLock.Enums.DlLockState.kLocked:
|
if lock_state is clusters.DoorLock.Enums.DlLockState.kLocked:
|
||||||
self._attr_is_locked = True
|
self._attr_is_locked = True
|
||||||
self._attr_is_locking = False
|
elif lock_state in (
|
||||||
self._attr_is_unlocking = False
|
clusters.DoorLock.Enums.DlLockState.kUnlocked,
|
||||||
elif lock_state is clusters.DoorLock.Enums.DlLockState.kUnlocked:
|
clusters.DoorLock.Enums.DlLockState.kUnlatched,
|
||||||
|
clusters.DoorLock.Enums.DlLockState.kNotFullyLocked,
|
||||||
|
):
|
||||||
self._attr_is_locked = False
|
self._attr_is_locked = False
|
||||||
self._attr_is_locking = False
|
|
||||||
self._attr_is_unlocking = False
|
|
||||||
elif lock_state is clusters.DoorLock.Enums.DlLockState.kNotFullyLocked:
|
|
||||||
if self.is_locked is True:
|
|
||||||
self._attr_is_unlocking = True
|
|
||||||
elif self.is_locked is False:
|
|
||||||
self._attr_is_locking = True
|
|
||||||
else:
|
else:
|
||||||
# According to the matter docs a null state can happen during device startup.
|
# According to the matter docs a null state can happen during device startup.
|
||||||
self._attr_is_locked = None
|
self._attr_is_locked = None
|
||||||
self._attr_is_locking = None
|
|
||||||
self._attr_is_unlocking = None
|
|
||||||
|
|
||||||
if self.supports_door_position_sensor:
|
if self.supports_door_position_sensor:
|
||||||
door_state = self.get_matter_attribute_value(
|
door_state = self.get_matter_attribute_value(
|
||||||
|
@ -114,6 +114,7 @@ DISCOVERY_SCHEMAS = [
|
|||||||
device_types.ColorTemperatureLight,
|
device_types.ColorTemperatureLight,
|
||||||
device_types.DimmableLight,
|
device_types.DimmableLight,
|
||||||
device_types.ExtendedColorLight,
|
device_types.ExtendedColorLight,
|
||||||
|
device_types.DimmerSwitch,
|
||||||
device_types.ColorDimmerSwitch,
|
device_types.ColorDimmerSwitch,
|
||||||
device_types.OnOffLight,
|
device_types.OnOffLight,
|
||||||
device_types.AirPurifier,
|
device_types.AirPurifier,
|
||||||
|
@ -44,5 +44,95 @@
|
|||||||
"title": "[%key:component::random::config::step::sensor::title%]"
|
"title": "[%key:component::random::config::step::sensor::title%]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"selector": {
|
||||||
|
"binary_sensor_device_class": {
|
||||||
|
"options": {
|
||||||
|
"battery": "[%key:component::binary_sensor::entity_component::battery::name%]",
|
||||||
|
"battery_charging": "[%key:component::binary_sensor::entity_component::battery_charging::name%]",
|
||||||
|
"carbon_monoxide": "[%key:component::binary_sensor::entity_component::carbon_monoxide::name%]",
|
||||||
|
"connectivity": "[%key:component::binary_sensor::entity_component::connectivity::name%]",
|
||||||
|
"cold": "[%key:component::binary_sensor::entity_component::cold::name%]",
|
||||||
|
"door": "[%key:component::binary_sensor::entity_component::door::name%]",
|
||||||
|
"garage_door": "[%key:component::binary_sensor::entity_component::garage_door::name%]",
|
||||||
|
"gas": "[%key:component::binary_sensor::entity_component::gas::name%]",
|
||||||
|
"heat": "[%key:component::binary_sensor::entity_component::heat::name%]",
|
||||||
|
"light": "[%key:component::binary_sensor::entity_component::light::name%]",
|
||||||
|
"lock": "[%key:component::binary_sensor::entity_component::lock::name%]",
|
||||||
|
"moisture": "[%key:component::binary_sensor::entity_component::moisture::name%]",
|
||||||
|
"motion": "[%key:component::binary_sensor::entity_component::motion::name%]",
|
||||||
|
"moving": "[%key:component::binary_sensor::entity_component::moving::name%]",
|
||||||
|
"occupancy": "[%key:component::binary_sensor::entity_component::occupancy::name%]",
|
||||||
|
"opening": "[%key:component::binary_sensor::entity_component::opening::name%]",
|
||||||
|
"plug": "[%key:component::binary_sensor::entity_component::plug::name%]",
|
||||||
|
"power": "[%key:component::binary_sensor::entity_component::power::name%]",
|
||||||
|
"presence": "[%key:component::binary_sensor::entity_component::presence::name%]",
|
||||||
|
"problem": "[%key:component::binary_sensor::entity_component::problem::name%]",
|
||||||
|
"running": "[%key:component::binary_sensor::entity_component::running::name%]",
|
||||||
|
"safety": "[%key:component::binary_sensor::entity_component::safety::name%]",
|
||||||
|
"smoke": "[%key:component::binary_sensor::entity_component::smoke::name%]",
|
||||||
|
"sound": "[%key:component::binary_sensor::entity_component::sound::name%]",
|
||||||
|
"tamper": "[%key:component::binary_sensor::entity_component::tamper::name%]",
|
||||||
|
"update": "[%key:component::binary_sensor::entity_component::update::name%]",
|
||||||
|
"vibration": "[%key:component::binary_sensor::entity_component::vibration::name%]",
|
||||||
|
"window": "[%key:component::binary_sensor::entity_component::window::name%]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sensor_device_class": {
|
||||||
|
"options": {
|
||||||
|
"apparent_power": "[%key:component::sensor::entity_component::apparent_power::name%]",
|
||||||
|
"aqi": "[%key:component::sensor::entity_component::aqi::name%]",
|
||||||
|
"atmospheric_pressure": "[%key:component::sensor::entity_component::atmospheric_pressure::name%]",
|
||||||
|
"battery": "[%key:component::sensor::entity_component::battery::name%]",
|
||||||
|
"carbon_dioxide": "[%key:component::sensor::entity_component::carbon_dioxide::name%]",
|
||||||
|
"carbon_monoxide": "[%key:component::sensor::entity_component::carbon_monoxide::name%]",
|
||||||
|
"conductivity": "[%key:component::sensor::entity_component::conductivity::name%]",
|
||||||
|
"current": "[%key:component::sensor::entity_component::current::name%]",
|
||||||
|
"data_rate": "[%key:component::sensor::entity_component::data_rate::name%]",
|
||||||
|
"data_size": "[%key:component::sensor::entity_component::data_size::name%]",
|
||||||
|
"date": "[%key:component::sensor::entity_component::date::name%]",
|
||||||
|
"distance": "[%key:component::sensor::entity_component::distance::name%]",
|
||||||
|
"duration": "[%key:component::sensor::entity_component::duration::name%]",
|
||||||
|
"energy": "[%key:component::sensor::entity_component::energy::name%]",
|
||||||
|
"energy_storage": "[%key:component::sensor::entity_component::energy_storage::name%]",
|
||||||
|
"frequency": "[%key:component::sensor::entity_component::frequency::name%]",
|
||||||
|
"gas": "[%key:component::sensor::entity_component::gas::name%]",
|
||||||
|
"humidity": "[%key:component::sensor::entity_component::humidity::name%]",
|
||||||
|
"illuminance": "[%key:component::sensor::entity_component::illuminance::name%]",
|
||||||
|
"irradiance": "[%key:component::sensor::entity_component::irradiance::name%]",
|
||||||
|
"moisture": "[%key:component::sensor::entity_component::moisture::name%]",
|
||||||
|
"monetary": "[%key:component::sensor::entity_component::monetary::name%]",
|
||||||
|
"nitrogen_dioxide": "[%key:component::sensor::entity_component::nitrogen_dioxide::name%]",
|
||||||
|
"nitrogen_monoxide": "[%key:component::sensor::entity_component::nitrogen_monoxide::name%]",
|
||||||
|
"nitrous_oxide": "[%key:component::sensor::entity_component::nitrous_oxide::name%]",
|
||||||
|
"ozone": "[%key:component::sensor::entity_component::ozone::name%]",
|
||||||
|
"ph": "[%key:component::sensor::entity_component::ph::name%]",
|
||||||
|
"pm1": "[%key:component::sensor::entity_component::pm1::name%]",
|
||||||
|
"pm10": "[%key:component::sensor::entity_component::pm10::name%]",
|
||||||
|
"pm25": "[%key:component::sensor::entity_component::pm25::name%]",
|
||||||
|
"power": "[%key:component::sensor::entity_component::power::name%]",
|
||||||
|
"power_factor": "[%key:component::sensor::entity_component::power_factor::name%]",
|
||||||
|
"precipitation": "[%key:component::sensor::entity_component::precipitation::name%]",
|
||||||
|
"precipitation_intensity": "[%key:component::sensor::entity_component::precipitation_intensity::name%]",
|
||||||
|
"pressure": "[%key:component::sensor::entity_component::pressure::name%]",
|
||||||
|
"reactive_power": "[%key:component::sensor::entity_component::reactive_power::name%]",
|
||||||
|
"signal_strength": "[%key:component::sensor::entity_component::signal_strength::name%]",
|
||||||
|
"sound_pressure": "[%key:component::sensor::entity_component::sound_pressure::name%]",
|
||||||
|
"speed": "[%key:component::sensor::entity_component::speed::name%]",
|
||||||
|
"sulphur_dioxide": "[%key:component::sensor::entity_component::sulphur_dioxide::name%]",
|
||||||
|
"temperature": "[%key:component::sensor::entity_component::temperature::name%]",
|
||||||
|
"timestamp": "[%key:component::sensor::entity_component::timestamp::name%]",
|
||||||
|
"volatile_organic_compounds": "[%key:component::sensor::entity_component::volatile_organic_compounds::name%]",
|
||||||
|
"volatile_organic_compounds_parts": "[%key:component::sensor::entity_component::volatile_organic_compounds::name%]",
|
||||||
|
"voltage": "[%key:component::sensor::entity_component::voltage::name%]",
|
||||||
|
"volume": "[%key:component::sensor::entity_component::volume::name%]",
|
||||||
|
"volume_flow_rate": "[%key:component::sensor::entity_component::volume_flow_rate::name%]",
|
||||||
|
"volume_storage": "[%key:component::sensor::entity_component::volume_storage::name%]",
|
||||||
|
"water": "[%key:component::sensor::entity_component::water::name%]",
|
||||||
|
"weight": "[%key:component::sensor::entity_component::weight::name%]",
|
||||||
|
"wind_speed": "[%key:component::sensor::entity_component::wind_speed::name%]"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -960,14 +960,18 @@ RPC_SENSORS: Final = {
|
|||||||
name="Analog input",
|
name="Analog input",
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
removal_condition=lambda config, _status, key: (config[key]["enable"] is False),
|
removal_condition=lambda config, _, key: (
|
||||||
|
config[key]["type"] != "analog" or config[key]["enable"] is False
|
||||||
|
),
|
||||||
),
|
),
|
||||||
"analoginput_xpercent": RpcSensorDescription(
|
"analoginput_xpercent": RpcSensorDescription(
|
||||||
key="input",
|
key="input",
|
||||||
sub_key="xpercent",
|
sub_key="xpercent",
|
||||||
name="Analog value",
|
name="Analog value",
|
||||||
removal_condition=lambda config, status, key: (
|
removal_condition=lambda config, status, key: (
|
||||||
config[key]["enable"] is False or status[key].get("xpercent") is None
|
config[key]["type"] != "analog"
|
||||||
|
or config[key]["enable"] is False
|
||||||
|
or status[key].get("xpercent") is None
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
"pulse_counter": RpcSensorDescription(
|
"pulse_counter": RpcSensorDescription(
|
||||||
@ -977,7 +981,9 @@ RPC_SENSORS: Final = {
|
|||||||
native_unit_of_measurement="pulse",
|
native_unit_of_measurement="pulse",
|
||||||
state_class=SensorStateClass.TOTAL,
|
state_class=SensorStateClass.TOTAL,
|
||||||
value=lambda status, _: status["total"],
|
value=lambda status, _: status["total"],
|
||||||
removal_condition=lambda config, _status, key: (config[key]["enable"] is False),
|
removal_condition=lambda config, _status, key: (
|
||||||
|
config[key]["type"] != "count" or config[key]["enable"] is False
|
||||||
|
),
|
||||||
),
|
),
|
||||||
"counter_value": RpcSensorDescription(
|
"counter_value": RpcSensorDescription(
|
||||||
key="input",
|
key="input",
|
||||||
@ -985,26 +991,29 @@ RPC_SENSORS: Final = {
|
|||||||
name="Counter value",
|
name="Counter value",
|
||||||
value=lambda status, _: status["xtotal"],
|
value=lambda status, _: status["xtotal"],
|
||||||
removal_condition=lambda config, status, key: (
|
removal_condition=lambda config, status, key: (
|
||||||
config[key]["enable"] is False
|
config[key]["type"] != "count"
|
||||||
|
or config[key]["enable"] is False
|
||||||
or status[key]["counts"].get("xtotal") is None
|
or status[key]["counts"].get("xtotal") is None
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
"counter_frequency": RpcSensorDescription(
|
"counter_frequency": RpcSensorDescription(
|
||||||
key="input",
|
key="input",
|
||||||
sub_key="counts",
|
sub_key="freq",
|
||||||
name="Pulse counter frequency",
|
name="Pulse counter frequency",
|
||||||
native_unit_of_measurement=UnitOfFrequency.HERTZ,
|
native_unit_of_measurement=UnitOfFrequency.HERTZ,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
value=lambda status, _: status["freq"],
|
removal_condition=lambda config, _, key: (
|
||||||
removal_condition=lambda config, status, key: (config[key]["enable"] is False),
|
config[key]["type"] != "count" or config[key]["enable"] is False
|
||||||
|
),
|
||||||
),
|
),
|
||||||
"counter_frequency_value": RpcSensorDescription(
|
"counter_frequency_value": RpcSensorDescription(
|
||||||
key="input",
|
key="input",
|
||||||
sub_key="counts",
|
sub_key="xfreq",
|
||||||
name="Pulse counter frequency value",
|
name="Pulse counter frequency value",
|
||||||
value=lambda status, _: status["xfreq"],
|
|
||||||
removal_condition=lambda config, status, key: (
|
removal_condition=lambda config, status, key: (
|
||||||
config[key]["enable"] is False or status[key]["counts"].get("xfreq") is None
|
config[key]["type"] != "count"
|
||||||
|
or config[key]["enable"] is False
|
||||||
|
or status[key].get("xfreq") is None
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ from typing import Any
|
|||||||
from starline import StarlineApi, StarlineDevice
|
from starline import StarlineApi, StarlineDevice
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
@ -65,9 +65,9 @@ class StarlineAccount:
|
|||||||
)
|
)
|
||||||
self._api.set_slnet_token(slnet_token)
|
self._api.set_slnet_token(slnet_token)
|
||||||
self._api.set_user_id(user_id)
|
self._api.set_user_id(user_id)
|
||||||
self._hass.config_entries.async_update_entry(
|
self._hass.add_job(
|
||||||
self._config_entry,
|
self._save_slnet_token,
|
||||||
data={
|
{
|
||||||
**self._config_entry.data,
|
**self._config_entry.data,
|
||||||
DATA_SLNET_TOKEN: slnet_token,
|
DATA_SLNET_TOKEN: slnet_token,
|
||||||
DATA_EXPIRES: slnet_token_expires,
|
DATA_EXPIRES: slnet_token_expires,
|
||||||
@ -77,6 +77,13 @@ class StarlineAccount:
|
|||||||
except Exception as err: # noqa: BLE001
|
except Exception as err: # noqa: BLE001
|
||||||
_LOGGER.error("Error updating SLNet token: %s", err)
|
_LOGGER.error("Error updating SLNet token: %s", err)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _save_slnet_token(self, data) -> None:
|
||||||
|
self._hass.config_entries.async_update_entry(
|
||||||
|
self._config_entry,
|
||||||
|
data=data,
|
||||||
|
)
|
||||||
|
|
||||||
def _update_data(self):
|
def _update_data(self):
|
||||||
"""Update StarLine data."""
|
"""Update StarLine data."""
|
||||||
self._check_slnet_token(self._update_interval)
|
self._check_slnet_token(self._update_interval)
|
||||||
|
@ -48,6 +48,14 @@ class StreamWorkerError(Exception):
|
|||||||
"""An exception thrown while processing a stream."""
|
"""An exception thrown while processing a stream."""
|
||||||
|
|
||||||
|
|
||||||
|
def redact_av_error_string(err: av.AVError) -> str:
|
||||||
|
"""Return an error string with credentials redacted from the url."""
|
||||||
|
parts = [str(err.type), err.strerror]
|
||||||
|
if err.filename is not None:
|
||||||
|
parts.append(redact_credentials(err.filename))
|
||||||
|
return ", ".join(parts)
|
||||||
|
|
||||||
|
|
||||||
class StreamEndedError(StreamWorkerError):
|
class StreamEndedError(StreamWorkerError):
|
||||||
"""Raised when the stream is complete, exposed for facilitating testing."""
|
"""Raised when the stream is complete, exposed for facilitating testing."""
|
||||||
|
|
||||||
@ -517,8 +525,7 @@ def stream_worker(
|
|||||||
container = av.open(source, options=pyav_options, timeout=SOURCE_TIMEOUT)
|
container = av.open(source, options=pyav_options, timeout=SOURCE_TIMEOUT)
|
||||||
except av.AVError as err:
|
except av.AVError as err:
|
||||||
raise StreamWorkerError(
|
raise StreamWorkerError(
|
||||||
f"Error opening stream ({err.type}, {err.strerror})"
|
f"Error opening stream ({redact_av_error_string(err)})"
|
||||||
f" {redact_credentials(str(source))}"
|
|
||||||
) from err
|
) from err
|
||||||
try:
|
try:
|
||||||
video_stream = container.streams.video[0]
|
video_stream = container.streams.video[0]
|
||||||
@ -593,7 +600,7 @@ def stream_worker(
|
|||||||
except av.AVError as ex:
|
except av.AVError as ex:
|
||||||
container.close()
|
container.close()
|
||||||
raise StreamWorkerError(
|
raise StreamWorkerError(
|
||||||
f"Error demuxing stream while finding first packet: {ex!s}"
|
f"Error demuxing stream while finding first packet ({redact_av_error_string(ex)})"
|
||||||
) from ex
|
) from ex
|
||||||
|
|
||||||
muxer = StreamMuxer(
|
muxer = StreamMuxer(
|
||||||
@ -618,7 +625,9 @@ def stream_worker(
|
|||||||
except StopIteration as ex:
|
except StopIteration as ex:
|
||||||
raise StreamEndedError("Stream ended; no additional packets") from ex
|
raise StreamEndedError("Stream ended; no additional packets") from ex
|
||||||
except av.AVError as ex:
|
except av.AVError as ex:
|
||||||
raise StreamWorkerError(f"Error demuxing stream: {ex!s}") from ex
|
raise StreamWorkerError(
|
||||||
|
f"Error demuxing stream ({redact_av_error_string(ex)})"
|
||||||
|
) from ex
|
||||||
|
|
||||||
muxer.mux_packet(packet)
|
muxer.mux_packet(packet)
|
||||||
|
|
||||||
|
@ -382,7 +382,12 @@ class TPLinkLightEffectEntity(TPLinkLightEntity):
|
|||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the light on."""
|
"""Turn the light on."""
|
||||||
brightness, transition = self._async_extract_brightness_transition(**kwargs)
|
brightness, transition = self._async_extract_brightness_transition(**kwargs)
|
||||||
if ATTR_EFFECT in kwargs:
|
if (
|
||||||
|
(effect := kwargs.get(ATTR_EFFECT))
|
||||||
|
# Effect is unlikely to be LIGHT_EFFECTS_OFF but check for it anyway
|
||||||
|
and effect not in {LightEffect.LIGHT_EFFECTS_OFF, EFFECT_OFF}
|
||||||
|
and effect in self._effect_module.effect_list
|
||||||
|
):
|
||||||
await self._effect_module.set_effect(
|
await self._effect_module.set_effect(
|
||||||
kwargs[ATTR_EFFECT], brightness=brightness, transition=transition
|
kwargs[ATTR_EFFECT], brightness=brightness, transition=transition
|
||||||
)
|
)
|
||||||
|
@ -297,5 +297,5 @@
|
|||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["kasa"],
|
"loggers": ["kasa"],
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"requirements": ["python-kasa[speedups]==0.7.0.2"]
|
"requirements": ["python-kasa[speedups]==0.7.0.3"]
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
"velbus-packet",
|
"velbus-packet",
|
||||||
"velbus-protocol"
|
"velbus-protocol"
|
||||||
],
|
],
|
||||||
"requirements": ["velbus-aio==2024.5.1"],
|
"requirements": ["velbus-aio==2024.7.5"],
|
||||||
"usb": [
|
"usb": [
|
||||||
{
|
{
|
||||||
"vid": "10CF",
|
"vid": "10CF",
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["aiowebostv"],
|
"loggers": ["aiowebostv"],
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"requirements": ["aiowebostv==0.4.1"],
|
"requirements": ["aiowebostv==0.4.2"],
|
||||||
"ssdp": [
|
"ssdp": [
|
||||||
{
|
{
|
||||||
"st": "urn:lge-com:service:webos-second-screen:1"
|
"st": "urn:lge-com:service:webos-second-screen:1"
|
||||||
|
@ -239,7 +239,8 @@ class LgWebOSMediaPlayerEntity(RestoreEntity, MediaPlayerEntity):
|
|||||||
|
|
||||||
self._attr_assumed_state = True
|
self._attr_assumed_state = True
|
||||||
if (
|
if (
|
||||||
self._client.media_state is not None
|
self._client.is_on
|
||||||
|
and self._client.media_state is not None
|
||||||
and self._client.media_state.get("foregroundAppInfo") is not None
|
and self._client.media_state.get("foregroundAppInfo") is not None
|
||||||
):
|
):
|
||||||
self._attr_assumed_state = False
|
self._attr_assumed_state = False
|
||||||
|
@ -7,5 +7,5 @@
|
|||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["holidays"],
|
"loggers": ["holidays"],
|
||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
"requirements": ["holidays==0.51"]
|
"requirements": ["holidays==0.52"]
|
||||||
}
|
}
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/youless",
|
"documentation": "https://www.home-assistant.io/integrations/youless",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["youless_api"],
|
"loggers": ["youless_api"],
|
||||||
"requirements": ["youless-api==2.1.0"]
|
"requirements": ["youless-api==2.1.2"]
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ if TYPE_CHECKING:
|
|||||||
APPLICATION_NAME: Final = "HomeAssistant"
|
APPLICATION_NAME: Final = "HomeAssistant"
|
||||||
MAJOR_VERSION: Final = 2024
|
MAJOR_VERSION: Final = 2024
|
||||||
MINOR_VERSION: Final = 7
|
MINOR_VERSION: Final = 7
|
||||||
PATCH_VERSION: Final = "0"
|
PATCH_VERSION: Final = "1"
|
||||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 12, 0)
|
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 12, 0)
|
||||||
|
@ -32,7 +32,7 @@ habluetooth==3.1.3
|
|||||||
hass-nabucasa==0.81.1
|
hass-nabucasa==0.81.1
|
||||||
hassil==1.7.1
|
hassil==1.7.1
|
||||||
home-assistant-bluetooth==1.12.2
|
home-assistant-bluetooth==1.12.2
|
||||||
home-assistant-frontend==20240703.0
|
home-assistant-frontend==20240705.0
|
||||||
home-assistant-intents==2024.7.3
|
home-assistant-intents==2024.7.3
|
||||||
httpx==0.27.0
|
httpx==0.27.0
|
||||||
ifaddr==0.2.0
|
ifaddr==0.2.0
|
||||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "homeassistant"
|
name = "homeassistant"
|
||||||
version = "2024.7.0"
|
version = "2024.7.1"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "Open-source home automation platform running on Python 3."
|
description = "Open-source home automation platform running on Python 3."
|
||||||
readme = "README.rst"
|
readme = "README.rst"
|
||||||
|
@ -195,7 +195,7 @@ aioambient==2024.01.0
|
|||||||
aioapcaccess==0.4.2
|
aioapcaccess==0.4.2
|
||||||
|
|
||||||
# homeassistant.components.aquacell
|
# homeassistant.components.aquacell
|
||||||
aioaquacell==0.1.7
|
aioaquacell==0.1.8
|
||||||
|
|
||||||
# homeassistant.components.aseko_pool_live
|
# homeassistant.components.aseko_pool_live
|
||||||
aioaseko==0.1.1
|
aioaseko==0.1.1
|
||||||
@ -404,7 +404,7 @@ aiowaqi==3.1.0
|
|||||||
aiowatttime==0.1.1
|
aiowatttime==0.1.1
|
||||||
|
|
||||||
# homeassistant.components.webostv
|
# homeassistant.components.webostv
|
||||||
aiowebostv==0.4.1
|
aiowebostv==0.4.2
|
||||||
|
|
||||||
# homeassistant.components.withings
|
# homeassistant.components.withings
|
||||||
aiowithings==3.0.2
|
aiowithings==3.0.2
|
||||||
@ -449,7 +449,7 @@ androidtvremote2==0.1.1
|
|||||||
anel-pwrctrl-homeassistant==0.0.1.dev2
|
anel-pwrctrl-homeassistant==0.0.1.dev2
|
||||||
|
|
||||||
# homeassistant.components.anova
|
# homeassistant.components.anova
|
||||||
anova-wifi==0.14.0
|
anova-wifi==0.15.0
|
||||||
|
|
||||||
# homeassistant.components.anthemav
|
# homeassistant.components.anthemav
|
||||||
anthemav==1.4.1
|
anthemav==1.4.1
|
||||||
@ -709,7 +709,7 @@ debugpy==1.8.1
|
|||||||
# decora==0.6
|
# decora==0.6
|
||||||
|
|
||||||
# homeassistant.components.ecovacs
|
# homeassistant.components.ecovacs
|
||||||
deebot-client==8.1.0
|
deebot-client==8.1.1
|
||||||
|
|
||||||
# homeassistant.components.ihc
|
# homeassistant.components.ihc
|
||||||
# homeassistant.components.namecheapdns
|
# homeassistant.components.namecheapdns
|
||||||
@ -1087,10 +1087,10 @@ hole==0.8.0
|
|||||||
|
|
||||||
# homeassistant.components.holiday
|
# homeassistant.components.holiday
|
||||||
# homeassistant.components.workday
|
# homeassistant.components.workday
|
||||||
holidays==0.51
|
holidays==0.52
|
||||||
|
|
||||||
# homeassistant.components.frontend
|
# homeassistant.components.frontend
|
||||||
home-assistant-frontend==20240703.0
|
home-assistant-frontend==20240705.0
|
||||||
|
|
||||||
# homeassistant.components.conversation
|
# homeassistant.components.conversation
|
||||||
home-assistant-intents==2024.7.3
|
home-assistant-intents==2024.7.3
|
||||||
@ -1161,7 +1161,7 @@ influxdb-client==1.24.0
|
|||||||
influxdb==5.3.1
|
influxdb==5.3.1
|
||||||
|
|
||||||
# homeassistant.components.inkbird
|
# homeassistant.components.inkbird
|
||||||
inkbird-ble==0.5.7
|
inkbird-ble==0.5.8
|
||||||
|
|
||||||
# homeassistant.components.insteon
|
# homeassistant.components.insteon
|
||||||
insteon-frontend-home-assistant==0.5.0
|
insteon-frontend-home-assistant==0.5.0
|
||||||
@ -2275,7 +2275,7 @@ python-join-api==0.0.9
|
|||||||
python-juicenet==1.1.0
|
python-juicenet==1.1.0
|
||||||
|
|
||||||
# homeassistant.components.tplink
|
# homeassistant.components.tplink
|
||||||
python-kasa[speedups]==0.7.0.2
|
python-kasa[speedups]==0.7.0.3
|
||||||
|
|
||||||
# homeassistant.components.lirc
|
# homeassistant.components.lirc
|
||||||
# python-lirc==1.2.3
|
# python-lirc==1.2.3
|
||||||
@ -2830,7 +2830,7 @@ vallox-websocket-api==5.3.0
|
|||||||
vehicle==2.2.1
|
vehicle==2.2.1
|
||||||
|
|
||||||
# homeassistant.components.velbus
|
# homeassistant.components.velbus
|
||||||
velbus-aio==2024.5.1
|
velbus-aio==2024.7.5
|
||||||
|
|
||||||
# homeassistant.components.venstar
|
# homeassistant.components.venstar
|
||||||
venstarcolortouch==0.19
|
venstarcolortouch==0.19
|
||||||
@ -2945,7 +2945,7 @@ yeelightsunflower==0.0.10
|
|||||||
yolink-api==0.4.4
|
yolink-api==0.4.4
|
||||||
|
|
||||||
# homeassistant.components.youless
|
# homeassistant.components.youless
|
||||||
youless-api==2.1.0
|
youless-api==2.1.2
|
||||||
|
|
||||||
# homeassistant.components.youtube
|
# homeassistant.components.youtube
|
||||||
youtubeaio==1.1.5
|
youtubeaio==1.1.5
|
||||||
|
@ -174,7 +174,7 @@ aioambient==2024.01.0
|
|||||||
aioapcaccess==0.4.2
|
aioapcaccess==0.4.2
|
||||||
|
|
||||||
# homeassistant.components.aquacell
|
# homeassistant.components.aquacell
|
||||||
aioaquacell==0.1.7
|
aioaquacell==0.1.8
|
||||||
|
|
||||||
# homeassistant.components.aseko_pool_live
|
# homeassistant.components.aseko_pool_live
|
||||||
aioaseko==0.1.1
|
aioaseko==0.1.1
|
||||||
@ -377,7 +377,7 @@ aiowaqi==3.1.0
|
|||||||
aiowatttime==0.1.1
|
aiowatttime==0.1.1
|
||||||
|
|
||||||
# homeassistant.components.webostv
|
# homeassistant.components.webostv
|
||||||
aiowebostv==0.4.1
|
aiowebostv==0.4.2
|
||||||
|
|
||||||
# homeassistant.components.withings
|
# homeassistant.components.withings
|
||||||
aiowithings==3.0.2
|
aiowithings==3.0.2
|
||||||
@ -413,7 +413,7 @@ androidtv[async]==0.0.73
|
|||||||
androidtvremote2==0.1.1
|
androidtvremote2==0.1.1
|
||||||
|
|
||||||
# homeassistant.components.anova
|
# homeassistant.components.anova
|
||||||
anova-wifi==0.14.0
|
anova-wifi==0.15.0
|
||||||
|
|
||||||
# homeassistant.components.anthemav
|
# homeassistant.components.anthemav
|
||||||
anthemav==1.4.1
|
anthemav==1.4.1
|
||||||
@ -590,7 +590,7 @@ dbus-fast==2.22.1
|
|||||||
debugpy==1.8.1
|
debugpy==1.8.1
|
||||||
|
|
||||||
# homeassistant.components.ecovacs
|
# homeassistant.components.ecovacs
|
||||||
deebot-client==8.1.0
|
deebot-client==8.1.1
|
||||||
|
|
||||||
# homeassistant.components.ihc
|
# homeassistant.components.ihc
|
||||||
# homeassistant.components.namecheapdns
|
# homeassistant.components.namecheapdns
|
||||||
@ -892,10 +892,10 @@ hole==0.8.0
|
|||||||
|
|
||||||
# homeassistant.components.holiday
|
# homeassistant.components.holiday
|
||||||
# homeassistant.components.workday
|
# homeassistant.components.workday
|
||||||
holidays==0.51
|
holidays==0.52
|
||||||
|
|
||||||
# homeassistant.components.frontend
|
# homeassistant.components.frontend
|
||||||
home-assistant-frontend==20240703.0
|
home-assistant-frontend==20240705.0
|
||||||
|
|
||||||
# homeassistant.components.conversation
|
# homeassistant.components.conversation
|
||||||
home-assistant-intents==2024.7.3
|
home-assistant-intents==2024.7.3
|
||||||
@ -951,7 +951,7 @@ influxdb-client==1.24.0
|
|||||||
influxdb==5.3.1
|
influxdb==5.3.1
|
||||||
|
|
||||||
# homeassistant.components.inkbird
|
# homeassistant.components.inkbird
|
||||||
inkbird-ble==0.5.7
|
inkbird-ble==0.5.8
|
||||||
|
|
||||||
# homeassistant.components.insteon
|
# homeassistant.components.insteon
|
||||||
insteon-frontend-home-assistant==0.5.0
|
insteon-frontend-home-assistant==0.5.0
|
||||||
@ -1775,7 +1775,7 @@ python-izone==1.2.9
|
|||||||
python-juicenet==1.1.0
|
python-juicenet==1.1.0
|
||||||
|
|
||||||
# homeassistant.components.tplink
|
# homeassistant.components.tplink
|
||||||
python-kasa[speedups]==0.7.0.2
|
python-kasa[speedups]==0.7.0.3
|
||||||
|
|
||||||
# homeassistant.components.matter
|
# homeassistant.components.matter
|
||||||
python-matter-server==6.2.2
|
python-matter-server==6.2.2
|
||||||
@ -2204,7 +2204,7 @@ vallox-websocket-api==5.3.0
|
|||||||
vehicle==2.2.1
|
vehicle==2.2.1
|
||||||
|
|
||||||
# homeassistant.components.velbus
|
# homeassistant.components.velbus
|
||||||
velbus-aio==2024.5.1
|
velbus-aio==2024.7.5
|
||||||
|
|
||||||
# homeassistant.components.venstar
|
# homeassistant.components.venstar
|
||||||
venstarcolortouch==0.19
|
venstarcolortouch==0.19
|
||||||
@ -2301,7 +2301,7 @@ yeelight==0.7.14
|
|||||||
yolink-api==0.4.4
|
yolink-api==0.4.4
|
||||||
|
|
||||||
# homeassistant.components.youless
|
# homeassistant.components.youless
|
||||||
youless-api==2.1.0
|
youless-api==2.1.2
|
||||||
|
|
||||||
# homeassistant.components.youtube
|
# homeassistant.components.youtube
|
||||||
youtubeaio==1.1.5
|
youtubeaio==1.1.5
|
||||||
|
@ -7757,6 +7757,141 @@
|
|||||||
"serializedGlobalTradeItemNumber": "3014F711000000000ESIIEC2",
|
"serializedGlobalTradeItemNumber": "3014F711000000000ESIIEC2",
|
||||||
"type": "ENERGY_SENSORS_INTERFACE",
|
"type": "ENERGY_SENSORS_INTERFACE",
|
||||||
"updateState": "UP_TO_DATE"
|
"updateState": "UP_TO_DATE"
|
||||||
|
},
|
||||||
|
"3014F7110000000000ESIIE3": {
|
||||||
|
"availableFirmwareVersion": "0.0.0",
|
||||||
|
"connectionType": "HMIP_RF",
|
||||||
|
"deviceArchetype": "HMIP",
|
||||||
|
"firmwareVersion": "1.0.6",
|
||||||
|
"firmwareVersionInteger": 65542,
|
||||||
|
"functionalChannels": {
|
||||||
|
"0": {
|
||||||
|
"busConfigMismatch": null,
|
||||||
|
"coProFaulty": false,
|
||||||
|
"coProRestartNeeded": false,
|
||||||
|
"coProUpdateFailure": false,
|
||||||
|
"configPending": false,
|
||||||
|
"controlsMountingOrientation": null,
|
||||||
|
"daliBusState": null,
|
||||||
|
"defaultLinkedGroup": [],
|
||||||
|
"deviceCommunicationError": null,
|
||||||
|
"deviceDriveError": null,
|
||||||
|
"deviceDriveModeError": null,
|
||||||
|
"deviceId": "3014F7110000000000ESIIE3",
|
||||||
|
"deviceOperationMode": null,
|
||||||
|
"deviceOverheated": false,
|
||||||
|
"deviceOverloaded": false,
|
||||||
|
"devicePowerFailureDetected": false,
|
||||||
|
"deviceUndervoltage": false,
|
||||||
|
"displayContrast": null,
|
||||||
|
"dutyCycle": false,
|
||||||
|
"functionalChannelType": "DEVICE_BASE",
|
||||||
|
"groupIndex": 0,
|
||||||
|
"groups": ["00000000-0000-0000-0000-000000000031"],
|
||||||
|
"index": 0,
|
||||||
|
"label": "",
|
||||||
|
"lockJammed": null,
|
||||||
|
"lowBat": false,
|
||||||
|
"mountingOrientation": null,
|
||||||
|
"multicastRoutingEnabled": false,
|
||||||
|
"particulateMatterSensorCommunicationError": null,
|
||||||
|
"particulateMatterSensorError": null,
|
||||||
|
"powerShortCircuit": null,
|
||||||
|
"profilePeriodLimitReached": null,
|
||||||
|
"routerModuleEnabled": false,
|
||||||
|
"routerModuleSupported": false,
|
||||||
|
"rssiDeviceValue": -94,
|
||||||
|
"rssiPeerValue": null,
|
||||||
|
"sensorCommunicationError": false,
|
||||||
|
"sensorError": true,
|
||||||
|
"shortCircuitDataLine": null,
|
||||||
|
"supportedOptionalFeatures": {
|
||||||
|
"IFeatureBusConfigMismatch": false,
|
||||||
|
"IFeatureDeviceCoProError": false,
|
||||||
|
"IFeatureDeviceCoProRestart": false,
|
||||||
|
"IFeatureDeviceCoProUpdate": false,
|
||||||
|
"IFeatureDeviceCommunicationError": false,
|
||||||
|
"IFeatureDeviceDaliBusError": false,
|
||||||
|
"IFeatureDeviceDriveError": false,
|
||||||
|
"IFeatureDeviceDriveModeError": false,
|
||||||
|
"IFeatureDeviceIdentify": false,
|
||||||
|
"IFeatureDeviceOverheated": false,
|
||||||
|
"IFeatureDeviceOverloaded": false,
|
||||||
|
"IFeatureDeviceParticulateMatterSensorCommunicationError": false,
|
||||||
|
"IFeatureDeviceParticulateMatterSensorError": false,
|
||||||
|
"IFeatureDevicePowerFailure": false,
|
||||||
|
"IFeatureDeviceSensorCommunicationError": true,
|
||||||
|
"IFeatureDeviceSensorError": true,
|
||||||
|
"IFeatureDeviceTemperatureHumiditySensorCommunicationError": false,
|
||||||
|
"IFeatureDeviceTemperatureHumiditySensorError": false,
|
||||||
|
"IFeatureDeviceTemperatureOutOfRange": false,
|
||||||
|
"IFeatureDeviceUndervoltage": false,
|
||||||
|
"IFeatureMulticastRouter": false,
|
||||||
|
"IFeaturePowerShortCircuit": false,
|
||||||
|
"IFeatureProfilePeriodLimit": false,
|
||||||
|
"IFeatureRssiValue": true,
|
||||||
|
"IFeatureShortCircuitDataLine": false,
|
||||||
|
"IOptionalFeatureDefaultLinkedGroup": false,
|
||||||
|
"IOptionalFeatureDeviceErrorLockJammed": false,
|
||||||
|
"IOptionalFeatureDeviceOperationMode": false,
|
||||||
|
"IOptionalFeatureDisplayContrast": false,
|
||||||
|
"IOptionalFeatureDutyCycle": true,
|
||||||
|
"IOptionalFeatureLowBat": true,
|
||||||
|
"IOptionalFeatureMountingOrientation": false
|
||||||
|
},
|
||||||
|
"temperatureHumiditySensorCommunicationError": null,
|
||||||
|
"temperatureHumiditySensorError": null,
|
||||||
|
"temperatureOutOfRange": false,
|
||||||
|
"unreach": false
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"channelRole": "ENERGY_SENSOR",
|
||||||
|
"connectedEnergySensorType": "ES_LED",
|
||||||
|
"currentGasFlow": null,
|
||||||
|
"currentPowerConsumption": 189.15,
|
||||||
|
"deviceId": "3014F7110000000000ESIIE3",
|
||||||
|
"energyCounterOne": 23825.748,
|
||||||
|
"energyCounterOneType": "UNKNOWN",
|
||||||
|
"energyCounterThree": null,
|
||||||
|
"energyCounterThreeType": "UNKNOWN",
|
||||||
|
"energyCounterTwo": null,
|
||||||
|
"energyCounterTwoType": "UNKNOWN",
|
||||||
|
"functionalChannelType": "ENERGY_SENSORS_INTERFACE_CHANNEL",
|
||||||
|
"gasVolume": null,
|
||||||
|
"gasVolumePerImpulse": 0.01,
|
||||||
|
"groupIndex": 1,
|
||||||
|
"groups": ["00000000-0000-0000-0000-000000000057"],
|
||||||
|
"impulsesPerKWH": 1000,
|
||||||
|
"index": 1,
|
||||||
|
"label": "",
|
||||||
|
"supportedOptionalFeatures": {
|
||||||
|
"IOptionalFeatureCounterOffset": true,
|
||||||
|
"IOptionalFeatureCurrentGasFlow": false,
|
||||||
|
"IOptionalFeatureCurrentPowerConsumption": true,
|
||||||
|
"IOptionalFeatureEnergyCounterOne": true,
|
||||||
|
"IOptionalFeatureEnergyCounterThree": false,
|
||||||
|
"IOptionalFeatureEnergyCounterTwo": false,
|
||||||
|
"IOptionalFeatureGasVolume": false,
|
||||||
|
"IOptionalFeatureGasVolumePerImpulse": false,
|
||||||
|
"IOptionalFeatureImpulsesPerKWH": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"homeId": "00000000-0000-0000-0000-000000000001",
|
||||||
|
"id": "3014F7110000000000ESIIE3",
|
||||||
|
"label": "esi_led",
|
||||||
|
"lastStatusUpdate": 1702420986697,
|
||||||
|
"liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED",
|
||||||
|
"manuallyUpdateForced": false,
|
||||||
|
"manufacturerCode": 1,
|
||||||
|
"measuredAttributes": {},
|
||||||
|
"modelId": 509,
|
||||||
|
"modelType": "HmIP-ESI",
|
||||||
|
"oem": "eQ-3",
|
||||||
|
"permanentlyReachable": false,
|
||||||
|
"serializedGlobalTradeItemNumber": "3014F7110000000000ESIIE3",
|
||||||
|
"type": "ENERGY_SENSORS_INTERFACE",
|
||||||
|
"updateState": "UP_TO_DATE"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
|
@ -26,7 +26,7 @@ async def test_hmip_load_all_supported_devices(
|
|||||||
test_devices=None, test_groups=None
|
test_devices=None, test_groups=None
|
||||||
)
|
)
|
||||||
|
|
||||||
assert len(mock_hap.hmip_device_by_entity_id) == 290
|
assert len(mock_hap.hmip_device_by_entity_id) == 293
|
||||||
|
|
||||||
|
|
||||||
async def test_hmip_remove_device(
|
async def test_hmip_remove_device(
|
||||||
|
@ -634,3 +634,39 @@ async def test_hmip_esi_gas_gas_volume(
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert ha_state.state == "1019.26"
|
assert ha_state.state == "1019.26"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_hmip_esi_led_current_power_consumption(
|
||||||
|
hass: HomeAssistant, default_mock_hap_factory
|
||||||
|
) -> None:
|
||||||
|
"""Test ESI-IEC currentPowerConsumption Sensor."""
|
||||||
|
entity_id = "sensor.esi_led_currentPowerConsumption"
|
||||||
|
entity_name = "esi_led CurrentPowerConsumption"
|
||||||
|
device_model = "HmIP-ESI"
|
||||||
|
mock_hap = await default_mock_hap_factory.async_get_mock_hap(
|
||||||
|
test_devices=["esi_led"]
|
||||||
|
)
|
||||||
|
|
||||||
|
ha_state, hmip_device = get_and_check_entity_basics(
|
||||||
|
hass, mock_hap, entity_id, entity_name, device_model
|
||||||
|
)
|
||||||
|
|
||||||
|
assert ha_state.state == "189.15"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_hmip_esi_led_energy_counter_usage_high_tariff(
|
||||||
|
hass: HomeAssistant, default_mock_hap_factory
|
||||||
|
) -> None:
|
||||||
|
"""Test ESI-IEC ENERGY_COUNTER_USAGE_HIGH_TARIFF."""
|
||||||
|
entity_id = "sensor.esi_led_energy_counter_usage_high_tariff"
|
||||||
|
entity_name = "esi_led ENERGY_COUNTER_USAGE_HIGH_TARIFF"
|
||||||
|
device_model = "HmIP-ESI"
|
||||||
|
mock_hap = await default_mock_hap_factory.async_get_mock_hap(
|
||||||
|
test_devices=["esi_led"]
|
||||||
|
)
|
||||||
|
|
||||||
|
ha_state, hmip_device = get_and_check_entity_basics(
|
||||||
|
hass, mock_hap, entity_id, entity_name, device_model
|
||||||
|
)
|
||||||
|
|
||||||
|
assert ha_state.state == "23825.748"
|
||||||
|
@ -1059,6 +1059,7 @@
|
|||||||
'Front lawn',
|
'Front lawn',
|
||||||
'Back lawn',
|
'Back lawn',
|
||||||
'my_lawn',
|
'my_lawn',
|
||||||
|
'no_work_area_active',
|
||||||
]),
|
]),
|
||||||
}),
|
}),
|
||||||
'config_entry_id': <ANY>,
|
'config_entry_id': <ANY>,
|
||||||
@ -1097,6 +1098,7 @@
|
|||||||
'Front lawn',
|
'Front lawn',
|
||||||
'Back lawn',
|
'Back lawn',
|
||||||
'my_lawn',
|
'my_lawn',
|
||||||
|
'no_work_area_active',
|
||||||
]),
|
]),
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
|
@ -87,6 +87,38 @@ async def test_next_start_sensor(
|
|||||||
assert state.state == STATE_UNKNOWN
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
async def test_work_area_sensor(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_automower_client: AsyncMock,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
) -> None:
|
||||||
|
"""Test the work area sensor."""
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
state = hass.states.get("sensor.test_mower_1_work_area")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "Front lawn"
|
||||||
|
|
||||||
|
values = mower_list_to_dictionary_dataclass(
|
||||||
|
load_json_value_fixture("mower.json", DOMAIN)
|
||||||
|
)
|
||||||
|
values[TEST_MOWER_ID].mower.work_area_id = None
|
||||||
|
mock_automower_client.get_status.return_value = values
|
||||||
|
freezer.tick(SCAN_INTERVAL)
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get("sensor.test_mower_1_work_area")
|
||||||
|
assert state.state == "no_work_area_active"
|
||||||
|
|
||||||
|
values[TEST_MOWER_ID].mower.work_area_id = 0
|
||||||
|
mock_automower_client.get_status.return_value = values
|
||||||
|
freezer.tick(SCAN_INTERVAL)
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get("sensor.test_mower_1_work_area")
|
||||||
|
assert state.state == "my_lawn"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("sensor_to_test"),
|
("sensor_to_test"),
|
||||||
[
|
[
|
||||||
|
@ -8,13 +8,11 @@ import pytest
|
|||||||
|
|
||||||
from homeassistant.components.lock import (
|
from homeassistant.components.lock import (
|
||||||
STATE_LOCKED,
|
STATE_LOCKED,
|
||||||
STATE_LOCKING,
|
|
||||||
STATE_OPEN,
|
STATE_OPEN,
|
||||||
STATE_UNLOCKED,
|
STATE_UNLOCKED,
|
||||||
STATE_UNLOCKING,
|
|
||||||
LockEntityFeature,
|
LockEntityFeature,
|
||||||
)
|
)
|
||||||
from homeassistant.const import ATTR_CODE, STATE_UNKNOWN
|
from homeassistant.const import ATTR_CODE, STATE_LOCKING, STATE_UNKNOWN
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ServiceValidationError
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
import homeassistant.helpers.entity_registry as er
|
import homeassistant.helpers.entity_registry as er
|
||||||
@ -68,14 +66,14 @@ async def test_lock(
|
|||||||
|
|
||||||
state = hass.states.get("lock.mock_door_lock_lock")
|
state = hass.states.get("lock.mock_door_lock_lock")
|
||||||
assert state
|
assert state
|
||||||
assert state.state == STATE_LOCKED
|
assert state.state == STATE_LOCKING
|
||||||
|
|
||||||
set_node_attribute(door_lock, 1, 257, 0, 0)
|
set_node_attribute(door_lock, 1, 257, 0, 0)
|
||||||
await trigger_subscription_callback(hass, matter_client)
|
await trigger_subscription_callback(hass, matter_client)
|
||||||
|
|
||||||
state = hass.states.get("lock.mock_door_lock_lock")
|
state = hass.states.get("lock.mock_door_lock_lock")
|
||||||
assert state
|
assert state
|
||||||
assert state.state == STATE_UNLOCKING
|
assert state.state == STATE_UNLOCKED
|
||||||
|
|
||||||
set_node_attribute(door_lock, 1, 257, 0, 2)
|
set_node_attribute(door_lock, 1, 257, 0, 2)
|
||||||
await trigger_subscription_callback(hass, matter_client)
|
await trigger_subscription_callback(hass, matter_client)
|
||||||
@ -89,7 +87,7 @@ async def test_lock(
|
|||||||
|
|
||||||
state = hass.states.get("lock.mock_door_lock_lock")
|
state = hass.states.get("lock.mock_door_lock_lock")
|
||||||
assert state
|
assert state
|
||||||
assert state.state == STATE_LOCKING
|
assert state.state == STATE_UNLOCKED
|
||||||
|
|
||||||
set_node_attribute(door_lock, 1, 257, 0, None)
|
set_node_attribute(door_lock, 1, 257, 0, None)
|
||||||
await trigger_subscription_callback(hass, matter_client)
|
await trigger_subscription_callback(hass, matter_client)
|
||||||
|
@ -228,7 +228,9 @@ MOCK_STATUS_RPC = {
|
|||||||
"input:1": {"id": 1, "percent": 89, "xpercent": 8.9},
|
"input:1": {"id": 1, "percent": 89, "xpercent": 8.9},
|
||||||
"input:2": {
|
"input:2": {
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"counts": {"total": 56174, "xtotal": 561.74, "freq": 208.00, "xfreq": 6.11},
|
"counts": {"total": 56174, "xtotal": 561.74},
|
||||||
|
"freq": 208.00,
|
||||||
|
"xfreq": 6.11,
|
||||||
},
|
},
|
||||||
"light:0": {"output": True, "brightness": 53.0},
|
"light:0": {"output": True, "brightness": 53.0},
|
||||||
"light:1": {"output": True, "brightness": 53.0},
|
"light:1": {"output": True, "brightness": 53.0},
|
||||||
|
@ -828,3 +828,29 @@ async def test_rpc_pulse_counter_frequency_sensors(
|
|||||||
entry = entity_registry.async_get(entity_id)
|
entry = entity_registry.async_get(entity_id)
|
||||||
assert entry
|
assert entry
|
||||||
assert entry.unique_id == "123456789ABC-input:2-counter_frequency_value"
|
assert entry.unique_id == "123456789ABC-input:2-counter_frequency_value"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_rpc_disabled_xfreq(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_rpc_device: Mock,
|
||||||
|
entity_registry: EntityRegistry,
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
"""Test RPC input with the xfreq sensor disabled."""
|
||||||
|
status = deepcopy(mock_rpc_device.status)
|
||||||
|
status["input:2"] = {
|
||||||
|
"id": 2,
|
||||||
|
"counts": {"total": 56174, "xtotal": 561.74},
|
||||||
|
"freq": 208.00,
|
||||||
|
}
|
||||||
|
monkeypatch.setattr(mock_rpc_device, "status", status)
|
||||||
|
|
||||||
|
await init_integration(hass, 2)
|
||||||
|
|
||||||
|
entity_id = f"{SENSOR_DOMAIN}.gas_pulse_counter_frequency_value"
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert not state
|
||||||
|
|
||||||
|
entry = entity_registry.async_get(entity_id)
|
||||||
|
assert not entry
|
||||||
|
@ -772,12 +772,15 @@ async def test_worker_log(
|
|||||||
|
|
||||||
with patch("av.open") as av_open:
|
with patch("av.open") as av_open:
|
||||||
# pylint: disable-next=c-extension-no-member
|
# pylint: disable-next=c-extension-no-member
|
||||||
av_open.side_effect = av.error.InvalidDataError(-2, "error")
|
av_open.side_effect = av.error.InvalidDataError(
|
||||||
|
code=-2, message="Invalid data", filename=stream_url
|
||||||
|
)
|
||||||
with pytest.raises(StreamWorkerError) as err:
|
with pytest.raises(StreamWorkerError) as err:
|
||||||
run_worker(hass, stream, stream_url)
|
run_worker(hass, stream, stream_url)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert (
|
assert (
|
||||||
str(err.value) == f"Error opening stream (ERRORTYPE_-2, error) {redacted_url}"
|
str(err.value)
|
||||||
|
== f"Error opening stream (ERRORTYPE_-2, Invalid data, {redacted_url})"
|
||||||
)
|
)
|
||||||
assert stream_url not in caplog.text
|
assert stream_url not in caplog.text
|
||||||
|
|
||||||
|
@ -210,7 +210,8 @@ def _mocked_device(
|
|||||||
|
|
||||||
if modules:
|
if modules:
|
||||||
device.modules = {
|
device.modules = {
|
||||||
module_name: MODULE_TO_MOCK_GEN[module_name]() for module_name in modules
|
module_name: MODULE_TO_MOCK_GEN[module_name](device)
|
||||||
|
for module_name in modules
|
||||||
}
|
}
|
||||||
|
|
||||||
if features:
|
if features:
|
||||||
@ -298,7 +299,7 @@ def _mocked_feature(
|
|||||||
return feature
|
return feature
|
||||||
|
|
||||||
|
|
||||||
def _mocked_light_module() -> Light:
|
def _mocked_light_module(device) -> Light:
|
||||||
light = MagicMock(spec=Light, name="Mocked light module")
|
light = MagicMock(spec=Light, name="Mocked light module")
|
||||||
light.update = AsyncMock()
|
light.update = AsyncMock()
|
||||||
light.brightness = 50
|
light.brightness = 50
|
||||||
@ -314,26 +315,58 @@ def _mocked_light_module() -> Light:
|
|||||||
light.hsv = (10, 30, 5)
|
light.hsv = (10, 30, 5)
|
||||||
light.valid_temperature_range = ColorTempRange(min=4000, max=9000)
|
light.valid_temperature_range = ColorTempRange(min=4000, max=9000)
|
||||||
light.hw_info = {"sw_ver": "1.0.0", "hw_ver": "1.0.0"}
|
light.hw_info = {"sw_ver": "1.0.0", "hw_ver": "1.0.0"}
|
||||||
light.set_state = AsyncMock()
|
|
||||||
light.set_brightness = AsyncMock()
|
async def _set_state(state, *_, **__):
|
||||||
light.set_hsv = AsyncMock()
|
light.state = state
|
||||||
light.set_color_temp = AsyncMock()
|
|
||||||
|
light.set_state = AsyncMock(wraps=_set_state)
|
||||||
|
|
||||||
|
async def _set_brightness(brightness, *_, **__):
|
||||||
|
light.state.brightness = brightness
|
||||||
|
light.state.light_on = brightness > 0
|
||||||
|
|
||||||
|
light.set_brightness = AsyncMock(wraps=_set_brightness)
|
||||||
|
|
||||||
|
async def _set_hsv(h, s, v, *_, **__):
|
||||||
|
light.state.hue = h
|
||||||
|
light.state.saturation = s
|
||||||
|
light.state.brightness = v
|
||||||
|
light.state.light_on = True
|
||||||
|
|
||||||
|
light.set_hsv = AsyncMock(wraps=_set_hsv)
|
||||||
|
|
||||||
|
async def _set_color_temp(temp, *_, **__):
|
||||||
|
light.state.color_temp = temp
|
||||||
|
light.state.light_on = True
|
||||||
|
|
||||||
|
light.set_color_temp = AsyncMock(wraps=_set_color_temp)
|
||||||
light.protocol = _mock_protocol()
|
light.protocol = _mock_protocol()
|
||||||
return light
|
return light
|
||||||
|
|
||||||
|
|
||||||
def _mocked_light_effect_module() -> LightEffect:
|
def _mocked_light_effect_module(device) -> LightEffect:
|
||||||
effect = MagicMock(spec=LightEffect, name="Mocked light effect")
|
effect = MagicMock(spec=LightEffect, name="Mocked light effect")
|
||||||
effect.has_effects = True
|
effect.has_effects = True
|
||||||
effect.has_custom_effects = True
|
effect.has_custom_effects = True
|
||||||
effect.effect = "Effect1"
|
effect.effect = "Effect1"
|
||||||
effect.effect_list = ["Off", "Effect1", "Effect2"]
|
effect.effect_list = ["Off", "Effect1", "Effect2"]
|
||||||
effect.set_effect = AsyncMock()
|
|
||||||
|
async def _set_effect(effect_name, *_, **__):
|
||||||
|
assert (
|
||||||
|
effect_name in effect.effect_list
|
||||||
|
), f"set_effect '{effect_name}' not in {effect.effect_list}"
|
||||||
|
assert device.modules[
|
||||||
|
Module.Light
|
||||||
|
], "Need a light module to test set_effect method"
|
||||||
|
device.modules[Module.Light].state.light_on = True
|
||||||
|
effect.effect = effect_name
|
||||||
|
|
||||||
|
effect.set_effect = AsyncMock(wraps=_set_effect)
|
||||||
effect.set_custom_effect = AsyncMock()
|
effect.set_custom_effect = AsyncMock()
|
||||||
return effect
|
return effect
|
||||||
|
|
||||||
|
|
||||||
def _mocked_fan_module() -> Fan:
|
def _mocked_fan_module(effect) -> Fan:
|
||||||
fan = MagicMock(auto_spec=Fan, name="Mocked fan")
|
fan = MagicMock(auto_spec=Fan, name="Mocked fan")
|
||||||
fan.fan_speed_level = 0
|
fan.fan_speed_level = 0
|
||||||
fan.set_fan_speed_level = AsyncMock()
|
fan.set_fan_speed_level = AsyncMock()
|
||||||
|
@ -5,6 +5,7 @@ from __future__ import annotations
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from unittest.mock import MagicMock, PropertyMock
|
from unittest.mock import MagicMock, PropertyMock
|
||||||
|
|
||||||
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
from kasa import (
|
from kasa import (
|
||||||
AuthenticationError,
|
AuthenticationError,
|
||||||
DeviceType,
|
DeviceType,
|
||||||
@ -36,7 +37,13 @@ from homeassistant.components.light import (
|
|||||||
)
|
)
|
||||||
from homeassistant.components.tplink.const import DOMAIN
|
from homeassistant.components.tplink.const import DOMAIN
|
||||||
from homeassistant.config_entries import SOURCE_REAUTH
|
from homeassistant.config_entries import SOURCE_REAUTH
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, STATE_OFF, STATE_ON
|
from homeassistant.const import (
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
|
CONF_HOST,
|
||||||
|
STATE_OFF,
|
||||||
|
STATE_ON,
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
@ -920,3 +927,82 @@ async def test_light_child(
|
|||||||
assert child_entity
|
assert child_entity
|
||||||
assert child_entity.unique_id == f"{DEVICE_ID}0{light_id}"
|
assert child_entity.unique_id == f"{DEVICE_ID}0{light_id}"
|
||||||
assert child_entity.device_id == entity.device_id
|
assert child_entity.device_id == entity.device_id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_scene_effect_light(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
) -> None:
|
||||||
|
"""Test activating a scene works with effects.
|
||||||
|
|
||||||
|
i.e. doesn't try to set the effect to 'off'
|
||||||
|
"""
|
||||||
|
already_migrated_config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN, data={CONF_HOST: "127.0.0.1"}, unique_id=MAC_ADDRESS
|
||||||
|
)
|
||||||
|
already_migrated_config_entry.add_to_hass(hass)
|
||||||
|
device = _mocked_device(
|
||||||
|
modules=[Module.Light, Module.LightEffect], alias="my_light"
|
||||||
|
)
|
||||||
|
light_effect = device.modules[Module.LightEffect]
|
||||||
|
light_effect.effect = LightEffect.LIGHT_EFFECTS_OFF
|
||||||
|
|
||||||
|
with _patch_discovery(device=device), _patch_connect(device=device):
|
||||||
|
assert await async_setup_component(hass, tplink.DOMAIN, {tplink.DOMAIN: {}})
|
||||||
|
assert await async_setup_component(hass, "scene", {})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
entity_id = "light.my_light"
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN, "turn_on", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
freezer.tick(5)
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.state is STATE_ON
|
||||||
|
assert state.attributes["effect"] is EFFECT_OFF
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
"scene",
|
||||||
|
"create",
|
||||||
|
{"scene_id": "effect_off_scene", "snapshot_entities": [entity_id]},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
scene_state = hass.states.get("scene.effect_off_scene")
|
||||||
|
assert scene_state.state is STATE_UNKNOWN
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
freezer.tick(5)
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.state is STATE_OFF
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
"scene",
|
||||||
|
"turn_on",
|
||||||
|
{
|
||||||
|
"entity_id": "scene.effect_off_scene",
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
scene_state = hass.states.get("scene.effect_off_scene")
|
||||||
|
assert scene_state.state is not STATE_UNKNOWN
|
||||||
|
|
||||||
|
freezer.tick(5)
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.state is STATE_ON
|
||||||
|
assert state.attributes["effect"] is EFFECT_OFF
|
||||||
|
@ -832,3 +832,7 @@ async def test_update_media_state(hass: HomeAssistant, client, monkeypatch) -> N
|
|||||||
monkeypatch.setattr(client, "media_state", data)
|
monkeypatch.setattr(client, "media_state", data)
|
||||||
await client.mock_state_update()
|
await client.mock_state_update()
|
||||||
assert hass.states.get(ENTITY_ID).state == MediaPlayerState.IDLE
|
assert hass.states.get(ENTITY_ID).state == MediaPlayerState.IDLE
|
||||||
|
|
||||||
|
monkeypatch.setattr(client, "is_on", False)
|
||||||
|
await client.mock_state_update()
|
||||||
|
assert hass.states.get(ENTITY_ID).state == STATE_OFF
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
'last_changed': <ANY>,
|
'last_changed': <ANY>,
|
||||||
'last_reported': <ANY>,
|
'last_reported': <ANY>,
|
||||||
'last_updated': <ANY>,
|
'last_updated': <ANY>,
|
||||||
'state': 'unavailable',
|
'state': '0.0',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_sensors[sensor.energy_delivery_low-entry]
|
# name: test_sensors[sensor.energy_delivery_low-entry]
|
||||||
@ -98,7 +98,7 @@
|
|||||||
'last_changed': <ANY>,
|
'last_changed': <ANY>,
|
||||||
'last_reported': <ANY>,
|
'last_reported': <ANY>,
|
||||||
'last_updated': <ANY>,
|
'last_updated': <ANY>,
|
||||||
'state': 'unavailable',
|
'state': '0.029',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_sensors[sensor.energy_high-entry]
|
# name: test_sensors[sensor.energy_high-entry]
|
||||||
@ -405,7 +405,7 @@
|
|||||||
'last_changed': <ANY>,
|
'last_changed': <ANY>,
|
||||||
'last_reported': <ANY>,
|
'last_reported': <ANY>,
|
||||||
'last_updated': <ANY>,
|
'last_updated': <ANY>,
|
||||||
'state': '1234.564',
|
'state': '1624.264',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_sensors[sensor.phase_1_current-entry]
|
# name: test_sensors[sensor.phase_1_current-entry]
|
||||||
@ -967,6 +967,6 @@
|
|||||||
'last_changed': <ANY>,
|
'last_changed': <ANY>,
|
||||||
'last_reported': <ANY>,
|
'last_reported': <ANY>,
|
||||||
'last_updated': <ANY>,
|
'last_updated': <ANY>,
|
||||||
'state': 'unavailable',
|
'state': '1234.564',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
Loading…
x
Reference in New Issue
Block a user