Merge pull request #66624 from home-assistant/rc

This commit is contained in:
Paulus Schoutsen 2022-02-15 19:59:26 -08:00 committed by GitHub
commit e33671db60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 241 additions and 179 deletions

View File

@ -7,7 +7,7 @@
"@Bre77" "@Bre77"
], ],
"requirements": [ "requirements": [
"advantage_air==0.2.5" "advantage_air==0.3.0"
], ],
"quality_scale": "platinum", "quality_scale": "platinum",
"iot_class": "local_polling" "iot_class": "local_polling"

View File

@ -4,7 +4,7 @@
"config_flow": true, "config_flow": true,
"dependencies": ["network"], "dependencies": ["network"],
"documentation": "https://www.home-assistant.io/integrations/flux_led", "documentation": "https://www.home-assistant.io/integrations/flux_led",
"requirements": ["flux_led==0.28.22"], "requirements": ["flux_led==0.28.26"],
"quality_scale": "platinum", "quality_scale": "platinum",
"codeowners": ["@icemanch", "@bdraco"], "codeowners": ["@icemanch", "@bdraco"],
"iot_class": "local_push", "iot_class": "local_push",

View File

@ -12,10 +12,9 @@ from homeassistant.components.button import (
ButtonEntityDescription, ButtonEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ENTITY_CATEGORY_CONFIG
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo, EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .common import AvmWrapper from .common import AvmWrapper
@ -41,28 +40,28 @@ BUTTONS: Final = [
key="firmware_update", key="firmware_update",
name="Firmware Update", name="Firmware Update",
device_class=ButtonDeviceClass.UPDATE, device_class=ButtonDeviceClass.UPDATE,
entity_category=ENTITY_CATEGORY_CONFIG, entity_category=EntityCategory.CONFIG,
press_action=lambda avm_wrapper: avm_wrapper.async_trigger_firmware_update(), press_action=lambda avm_wrapper: avm_wrapper.async_trigger_firmware_update(),
), ),
FritzButtonDescription( FritzButtonDescription(
key="reboot", key="reboot",
name="Reboot", name="Reboot",
device_class=ButtonDeviceClass.RESTART, device_class=ButtonDeviceClass.RESTART,
entity_category=ENTITY_CATEGORY_CONFIG, entity_category=EntityCategory.CONFIG,
press_action=lambda avm_wrapper: avm_wrapper.async_trigger_reboot(), press_action=lambda avm_wrapper: avm_wrapper.async_trigger_reboot(),
), ),
FritzButtonDescription( FritzButtonDescription(
key="reconnect", key="reconnect",
name="Reconnect", name="Reconnect",
device_class=ButtonDeviceClass.RESTART, device_class=ButtonDeviceClass.RESTART,
entity_category=ENTITY_CATEGORY_CONFIG, entity_category=EntityCategory.CONFIG,
press_action=lambda avm_wrapper: avm_wrapper.async_trigger_reconnect(), press_action=lambda avm_wrapper: avm_wrapper.async_trigger_reconnect(),
), ),
FritzButtonDescription( FritzButtonDescription(
key="cleanup", key="cleanup",
name="Cleanup", name="Cleanup",
icon="mdi:broom", icon="mdi:broom",
entity_category=ENTITY_CATEGORY_CONFIG, entity_category=EntityCategory.CONFIG,
press_action=lambda avm_wrapper: avm_wrapper.async_trigger_cleanup(), press_action=lambda avm_wrapper: avm_wrapper.async_trigger_cleanup(),
), ),
] ]

View File

@ -327,10 +327,18 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator):
_LOGGER.debug("Checking host info for FRITZ!Box device %s", self.host) _LOGGER.debug("Checking host info for FRITZ!Box device %s", self.host)
self._update_available, self._latest_firmware = self._update_device_info() self._update_available, self._latest_firmware = self._update_device_info()
if (
"Hosts1" not in self.connection.services
or "X_AVM-DE_GetMeshListPath"
not in self.connection.services["Hosts1"].actions
):
self.mesh_role = MeshRoles.NONE
else:
try: try:
topology = self.fritz_hosts.get_mesh_topology() topology = self.fritz_hosts.get_mesh_topology()
except FritzActionError: except FritzActionError:
self.mesh_role = MeshRoles.SLAVE self.mesh_role = MeshRoles.SLAVE
# Avoid duplicating device trackers
return return
_LOGGER.debug("Checking devices for FRITZ!Box device %s", self.host) _LOGGER.debug("Checking devices for FRITZ!Box device %s", self.host)

View File

@ -13,8 +13,8 @@ from homeassistant.components.binary_sensor import (
BinarySensorEntityDescription, BinarySensorEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ENTITY_CATEGORY_CONFIG
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import FritzBoxEntity from . import FritzBoxEntity
@ -49,7 +49,7 @@ BINARY_SENSOR_TYPES: Final[tuple[FritzBinarySensorEntityDescription, ...]] = (
key="lock", key="lock",
name="Button Lock on Device", name="Button Lock on Device",
device_class=BinarySensorDeviceClass.LOCK, device_class=BinarySensorDeviceClass.LOCK,
entity_category=ENTITY_CATEGORY_CONFIG, entity_category=EntityCategory.CONFIG,
suitable=lambda device: device.lock is not None, suitable=lambda device: device.lock is not None,
is_on=lambda device: not device.lock, is_on=lambda device: not device.lock,
), ),
@ -57,7 +57,7 @@ BINARY_SENSOR_TYPES: Final[tuple[FritzBinarySensorEntityDescription, ...]] = (
key="device_lock", key="device_lock",
name="Button Lock via UI", name="Button Lock via UI",
device_class=BinarySensorDeviceClass.LOCK, device_class=BinarySensorDeviceClass.LOCK,
entity_category=ENTITY_CATEGORY_CONFIG, entity_category=EntityCategory.CONFIG,
suitable=lambda device: device.device_lock is not None, suitable=lambda device: device.device_lock is not None,
is_on=lambda device: not device.device_lock, is_on=lambda device: not device.device_lock,
), ),

View File

@ -9,9 +9,9 @@ from goodwe import Inverter, InverterError
from homeassistant.components.number import NumberEntity, NumberEntityDescription from homeassistant.components.number import NumberEntity, NumberEntityDescription
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ENTITY_CATEGORY_CONFIG, PERCENTAGE, POWER_WATT from homeassistant.const import PERCENTAGE, POWER_WATT
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo, EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN, KEY_DEVICE_INFO, KEY_INVERTER from .const import DOMAIN, KEY_DEVICE_INFO, KEY_INVERTER
@ -39,7 +39,7 @@ NUMBERS = (
key="grid_export_limit", key="grid_export_limit",
name="Grid export limit", name="Grid export limit",
icon="mdi:transmission-tower", icon="mdi:transmission-tower",
entity_category=ENTITY_CATEGORY_CONFIG, entity_category=EntityCategory.CONFIG,
unit_of_measurement=POWER_WATT, unit_of_measurement=POWER_WATT,
getter=lambda inv: inv.get_grid_export_limit(), getter=lambda inv: inv.get_grid_export_limit(),
setter=lambda inv, val: inv.set_grid_export_limit(val), setter=lambda inv, val: inv.set_grid_export_limit(val),
@ -51,7 +51,7 @@ NUMBERS = (
key="battery_discharge_depth", key="battery_discharge_depth",
name="Depth of discharge (on-grid)", name="Depth of discharge (on-grid)",
icon="mdi:battery-arrow-down", icon="mdi:battery-arrow-down",
entity_category=ENTITY_CATEGORY_CONFIG, entity_category=EntityCategory.CONFIG,
unit_of_measurement=PERCENTAGE, unit_of_measurement=PERCENTAGE,
getter=lambda inv: inv.get_ongrid_battery_dod(), getter=lambda inv: inv.get_ongrid_battery_dod(),
setter=lambda inv, val: inv.set_ongrid_battery_dod(val), setter=lambda inv, val: inv.set_ongrid_battery_dod(val),

View File

@ -5,9 +5,8 @@ from goodwe import Inverter, InverterError
from homeassistant.components.select import SelectEntity, SelectEntityDescription from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ENTITY_CATEGORY_CONFIG
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo, EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN, KEY_DEVICE_INFO, KEY_INVERTER from .const import DOMAIN, KEY_DEVICE_INFO, KEY_INVERTER
@ -26,7 +25,7 @@ OPERATION_MODE = SelectEntityDescription(
key="operation_mode", key="operation_mode",
name="Inverter operation mode", name="Inverter operation mode",
icon="mdi:solar-power", icon="mdi:solar-power",
entity_category=ENTITY_CATEGORY_CONFIG, entity_category=EntityCategory.CONFIG,
) )

View File

@ -26,7 +26,7 @@ from pycec.const import (
from homeassistant.components.media_player import MediaPlayerEntity from homeassistant.components.media_player import MediaPlayerEntity
from homeassistant.components.media_player.const import ( from homeassistant.components.media_player.const import (
DOMAIN, DOMAIN as MP_DOMAIN,
SUPPORT_NEXT_TRACK, SUPPORT_NEXT_TRACK,
SUPPORT_PAUSE, SUPPORT_PAUSE,
SUPPORT_PLAY_MEDIA, SUPPORT_PLAY_MEDIA,
@ -48,11 +48,11 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import ATTR_NEW, CecEntity from . import ATTR_NEW, DOMAIN, CecEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
ENTITY_ID_FORMAT = DOMAIN + ".{}" ENTITY_ID_FORMAT = MP_DOMAIN + ".{}"
def setup_platform( def setup_platform(
@ -77,7 +77,7 @@ class CecPlayerEntity(CecEntity, MediaPlayerEntity):
def __init__(self, device, logical) -> None: def __init__(self, device, logical) -> None:
"""Initialize the HDMI device.""" """Initialize the HDMI device."""
CecEntity.__init__(self, device, logical) CecEntity.__init__(self, device, logical)
self.entity_id = f"{DOMAIN}.hdmi_{hex(self._logical_address)[2:]}" self.entity_id = f"{MP_DOMAIN}.hdmi_{hex(self._logical_address)[2:]}"
def send_keypress(self, key): def send_keypress(self, key):
"""Send keypress to CEC adapter.""" """Send keypress to CEC adapter."""

View File

@ -3,17 +3,17 @@ from __future__ import annotations
import logging import logging
from homeassistant.components.switch import DOMAIN, SwitchEntity from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN, SwitchEntity
from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import ATTR_NEW, CecEntity from . import ATTR_NEW, DOMAIN, CecEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
ENTITY_ID_FORMAT = DOMAIN + ".{}" ENTITY_ID_FORMAT = SWITCH_DOMAIN + ".{}"
def setup_platform( def setup_platform(
@ -38,7 +38,7 @@ class CecSwitchEntity(CecEntity, SwitchEntity):
def __init__(self, device, logical) -> None: def __init__(self, device, logical) -> None:
"""Initialize the HDMI device.""" """Initialize the HDMI device."""
CecEntity.__init__(self, device, logical) CecEntity.__init__(self, device, logical)
self.entity_id = f"{DOMAIN}.hdmi_{hex(self._logical_address)[2:]}" self.entity_id = f"{SWITCH_DOMAIN}.hdmi_{hex(self._logical_address)[2:]}"
def turn_on(self, **kwargs) -> None: def turn_on(self, **kwargs) -> None:
"""Turn device on.""" """Turn device on."""

View File

@ -16,13 +16,12 @@ from homeassistant.const import (
DEVICE_CLASS_GAS, DEVICE_CLASS_GAS,
DEVICE_CLASS_POWER, DEVICE_CLASS_POWER,
ENERGY_KILO_WATT_HOUR, ENERGY_KILO_WATT_HOUR,
ENTITY_CATEGORY_DIAGNOSTIC,
PERCENTAGE, PERCENTAGE,
POWER_WATT, POWER_WATT,
VOLUME_CUBIC_METERS, VOLUME_CUBIC_METERS,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo, EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
@ -37,19 +36,19 @@ SENSORS: Final[tuple[SensorEntityDescription, ...]] = (
key="smr_version", key="smr_version",
name="DSMR Version", name="DSMR Version",
icon="mdi:counter", icon="mdi:counter",
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
SensorEntityDescription( SensorEntityDescription(
key="meter_model", key="meter_model",
name="Smart Meter Model", name="Smart Meter Model",
icon="mdi:gauge", icon="mdi:gauge",
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
SensorEntityDescription( SensorEntityDescription(
key="wifi_ssid", key="wifi_ssid",
name="Wifi SSID", name="Wifi SSID",
icon="mdi:wifi", icon="mdi:wifi",
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
SensorEntityDescription( SensorEntityDescription(
key="wifi_strength", key="wifi_strength",
@ -57,7 +56,7 @@ SENSORS: Final[tuple[SensorEntityDescription, ...]] = (
icon="mdi:wifi", icon="mdi:wifi",
native_unit_of_measurement=PERCENTAGE, native_unit_of_measurement=PERCENTAGE,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
), ),
SensorEntityDescription( SensorEntityDescription(

View File

@ -3,7 +3,7 @@
"name": "Philips Hue", "name": "Philips Hue",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/hue", "documentation": "https://www.home-assistant.io/integrations/hue",
"requirements": ["aiohue==4.0.1"], "requirements": ["aiohue==4.1.2"],
"ssdp": [ "ssdp": [
{ {
"manufacturer": "Royal Philips Electronics", "manufacturer": "Royal Philips Electronics",

View File

@ -5,7 +5,11 @@ from typing import Any
from aiohue.v2 import HueBridgeV2 from aiohue.v2 import HueBridgeV2
from aiohue.v2.controllers.events import EventType from aiohue.v2.controllers.events import EventType
from aiohue.v2.controllers.scenes import Scene as HueScene, ScenesController from aiohue.v2.controllers.scenes import (
Scene as HueScene,
ScenePut as HueScenePut,
ScenesController,
)
import voluptuous as vol import voluptuous as vol
from homeassistant.components.scene import ATTR_TRANSITION, Scene as SceneEntity from homeassistant.components.scene import ATTR_TRANSITION, Scene as SceneEntity
@ -131,7 +135,7 @@ class HueSceneEntity(HueBaseEntity, SceneEntity):
await self.bridge.async_request_call( await self.bridge.async_request_call(
self.controller.update, self.controller.update,
self.resource.id, self.resource.id,
HueScene(self.resource.id, speed=speed / 100), HueScenePut(speed=speed / 100),
) )
await self.bridge.async_request_call( await self.bridge.async_request_call(

View File

@ -5,8 +5,12 @@ from typing import Any, Union
from aiohue.v2 import HueBridgeV2 from aiohue.v2 import HueBridgeV2
from aiohue.v2.controllers.events import EventType from aiohue.v2.controllers.events import EventType
from aiohue.v2.controllers.sensors import LightLevelController, MotionController from aiohue.v2.controllers.sensors import (
from aiohue.v2.models.resource import SensingService LightLevel,
LightLevelController,
Motion,
MotionController,
)
from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -20,6 +24,8 @@ from .v2.entity import HueBaseEntity
ControllerType = Union[LightLevelController, MotionController] ControllerType = Union[LightLevelController, MotionController]
SensingService = Union[LightLevel, Motion]
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,

View File

@ -4,13 +4,13 @@ from __future__ import annotations
from typing import Any, Union from typing import Any, Union
from aiohue.v2 import HueBridgeV2 from aiohue.v2 import HueBridgeV2
from aiohue.v2.controllers.config import EntertainmentConfigurationController from aiohue.v2.controllers.config import (
EntertainmentConfiguration,
EntertainmentConfigurationController,
)
from aiohue.v2.controllers.events import EventType from aiohue.v2.controllers.events import EventType
from aiohue.v2.controllers.sensors import MotionController from aiohue.v2.controllers.sensors import MotionController
from aiohue.v2.models.entertainment import ( from aiohue.v2.models.entertainment_configuration import EntertainmentStatus
EntertainmentConfiguration,
EntertainmentStatus,
)
from aiohue.v2.models.motion import Motion from aiohue.v2.models.motion import Motion
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
@ -109,4 +109,4 @@ class HueEntertainmentActiveSensor(HueBinarySensorBase):
def name(self) -> str: def name(self) -> str:
"""Return sensor name.""" """Return sensor name."""
type_title = self.resource.type.value.replace("_", " ").title() type_title = self.resource.type.value.replace("_", " ").title()
return f"{self.resource.name}: {type_title}" return f"{self.resource.metadata.name}: {type_title}"

View File

@ -1,11 +1,12 @@
"""Generic Hue Entity Model.""" """Generic Hue Entity Model."""
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, Union
from aiohue.v2.controllers.base import BaseResourcesController from aiohue.v2.controllers.base import BaseResourcesController
from aiohue.v2.controllers.events import EventType from aiohue.v2.controllers.events import EventType
from aiohue.v2.models.clip import CLIPResource
from aiohue.v2.models.connectivity import ConnectivityServiceStatus
from aiohue.v2.models.resource import ResourceTypes from aiohue.v2.models.resource import ResourceTypes
from aiohue.v2.models.zigbee_connectivity import ConnectivityServiceStatus
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity import DeviceInfo, Entity
@ -14,6 +15,16 @@ from homeassistant.helpers.entity_registry import async_get as async_get_entity_
from ..bridge import HueBridge from ..bridge import HueBridge
from ..const import CONF_IGNORE_AVAILABILITY, DOMAIN from ..const import CONF_IGNORE_AVAILABILITY, DOMAIN
if TYPE_CHECKING:
from aiohue.v2.models.device_power import DevicePower
from aiohue.v2.models.grouped_light import GroupedLight
from aiohue.v2.models.light import Light
from aiohue.v2.models.light_level import LightLevel
from aiohue.v2.models.motion import Motion
HueResource = Union[Light, DevicePower, GroupedLight, LightLevel, Motion]
RESOURCE_TYPE_NAMES = { RESOURCE_TYPE_NAMES = {
# a simple mapping of hue resource type to Hass name # a simple mapping of hue resource type to Hass name
ResourceTypes.LIGHT_LEVEL: "Illuminance", ResourceTypes.LIGHT_LEVEL: "Illuminance",
@ -30,7 +41,7 @@ class HueBaseEntity(Entity):
self, self,
bridge: HueBridge, bridge: HueBridge,
controller: BaseResourcesController, controller: BaseResourcesController,
resource: CLIPResource, resource: HueResource,
) -> None: ) -> None:
"""Initialize a generic Hue resource entity.""" """Initialize a generic Hue resource entity."""
self.bridge = bridge self.bridge = bridge
@ -122,7 +133,7 @@ class HueBaseEntity(Entity):
# used in subclasses # used in subclasses
@callback @callback
def _handle_event(self, event_type: EventType, resource: CLIPResource) -> None: def _handle_event(self, event_type: EventType, resource: HueResource) -> None:
"""Handle status event for this resource (or it's parent).""" """Handle status event for this resource (or it's parent)."""
if event_type == EventType.RESOURCE_DELETED and resource.id == self.resource.id: if event_type == EventType.RESOURCE_DELETED and resource.id == self.resource.id:
self.logger.debug("Received delete for %s", self.entity_id) self.logger.debug("Received delete for %s", self.entity_id)

View File

@ -7,7 +7,7 @@ from typing import Any
from aiohue.v2 import HueBridgeV2 from aiohue.v2 import HueBridgeV2
from aiohue.v2.controllers.events import EventType from aiohue.v2.controllers.events import EventType
from aiohue.v2.controllers.groups import GroupedLight, Room, Zone from aiohue.v2.controllers.groups import GroupedLight, Room, Zone
from aiohue.v2.models.feature import DynamicsFeatureStatus from aiohue.v2.models.feature import DynamicStatus
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
@ -283,7 +283,7 @@ class GroupedHueLight(HueBaseEntity, LightEntity):
total_brightness += dimming.brightness total_brightness += dimming.brightness
if ( if (
light.dynamics light.dynamics
and light.dynamics.status == DynamicsFeatureStatus.DYNAMIC_PALETTE and light.dynamics.status == DynamicStatus.DYNAMIC_PALETTE
): ):
lights_in_dynamic_mode += 1 lights_in_dynamic_mode += 1

View File

@ -12,10 +12,10 @@ from aiohue.v2.controllers.sensors import (
TemperatureController, TemperatureController,
ZigbeeConnectivityController, ZigbeeConnectivityController,
) )
from aiohue.v2.models.connectivity import ZigbeeConnectivity
from aiohue.v2.models.device_power import DevicePower from aiohue.v2.models.device_power import DevicePower
from aiohue.v2.models.light_level import LightLevel from aiohue.v2.models.light_level import LightLevel
from aiohue.v2.models.temperature import Temperature from aiohue.v2.models.temperature import Temperature
from aiohue.v2.models.zigbee_connectivity import ZigbeeConnectivity
from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (

View File

@ -8,8 +8,8 @@ write_coil:
required: true required: true
selector: selector:
number: number:
min: 1 min: 0
max: 255 max: 65535
state: state:
name: State name: State
description: State to write. description: State to write.
@ -42,8 +42,8 @@ write_register:
required: true required: true
selector: selector:
number: number:
min: 1 min: 0
max: 255 max: 65535
unit: unit:
name: Unit name: Unit
description: Address of the modbus unit. description: Address of the modbus unit.

View File

@ -127,6 +127,15 @@ class NestCamera(Camera):
return STREAM_TYPE_WEB_RTC return STREAM_TYPE_WEB_RTC
return super().frontend_stream_type return super().frontend_stream_type
@property
def available(self) -> bool:
"""Return True if entity is available."""
# Cameras are marked unavailable on stream errors in #54659 however nest streams have
# a high error rate (#60353). Given nest streams are so flaky, marking the stream
# unavailable has other side effects like not showing the camera image which sometimes
# are still able to work. Until the streams are fixed, just leave the streams as available.
return True
async def stream_source(self) -> str | None: async def stream_source(self) -> str | None:
"""Return the source of the stream.""" """Return the source of the stream."""
if not self.supported_features & SUPPORT_STREAM: if not self.supported_features & SUPPORT_STREAM:

View File

@ -2,8 +2,8 @@
from homeassistant.components.button import ButtonDeviceClass, ButtonEntity from homeassistant.components.button import ButtonDeviceClass, ButtonEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ENTITY_CATEGORY_CONFIG
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .base import ONVIFBaseEntity from .base import ONVIFBaseEntity
@ -25,7 +25,7 @@ class RebootButton(ONVIFBaseEntity, ButtonEntity):
"""Defines a ONVIF reboot button.""" """Defines a ONVIF reboot button."""
_attr_device_class = ButtonDeviceClass.RESTART _attr_device_class = ButtonDeviceClass.RESTART
_attr_entity_category = ENTITY_CATEGORY_CONFIG _attr_entity_category = EntityCategory.CONFIG
def __init__(self, device: ONVIFDevice) -> None: def __init__(self, device: ONVIFDevice) -> None:
"""Initialize the button entity.""" """Initialize the button entity."""

View File

@ -148,9 +148,12 @@ class PhilipsTVDataUpdateCoordinator(DataUpdateCoordinator[None]):
@property @property
def unique_id(self) -> str: def unique_id(self) -> str:
"""Return the system descriptor.""" """Return the system descriptor."""
assert self.config_entry entry: ConfigEntry = self.config_entry
assert self.config_entry.unique_id assert entry
return self.config_entry.unique_id if entry.unique_id:
return entry.unique_id
assert entry.entry_id
return entry.entry_id
@property @property
def _notify_wanted(self): def _notify_wanted(self):

View File

@ -122,8 +122,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
LOGGER.exception("Unexpected exception") LOGGER.exception("Unexpected exception")
errors["base"] = "unknown" errors["base"] = "unknown"
else: else:
if serialnumber := hub.system.get("serialnumber"):
await self.async_set_unique_id(hub.system["serialnumber"]) await self.async_set_unique_id(serialnumber)
self._abort_if_unique_id_configured() self._abort_if_unique_id_configured()
self._current[CONF_SYSTEM] = hub.system self._current[CONF_SYSTEM] = hub.system

View File

@ -2,7 +2,6 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import TYPE_CHECKING
import voluptuous as vol import voluptuous as vol
@ -109,20 +108,12 @@ class I2CHatBinarySensor(BinarySensorEntity):
self._device_class = device_class self._device_class = device_class
self._state = self.I2C_HATS_MANAGER.read_di(self._address, self._channel) self._state = self.I2C_HATS_MANAGER.read_di(self._address, self._channel)
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
if TYPE_CHECKING:
assert self.I2C_HATS_MANAGER
def online_callback(): def online_callback():
"""Call fired when board is online.""" """Call fired when board is online."""
self.schedule_update_ha_state() self.schedule_update_ha_state()
await self.hass.async_add_executor_job( self.I2C_HATS_MANAGER.register_online_callback(
self.I2C_HATS_MANAGER.register_online_callback, self._address, self._channel, online_callback
self._address,
self._channel,
online_callback,
) )
def edge_callback(state): def edge_callback(state):
@ -130,11 +121,8 @@ class I2CHatBinarySensor(BinarySensorEntity):
self._state = state self._state = state
self.schedule_update_ha_state() self.schedule_update_ha_state()
await self.hass.async_add_executor_job( self.I2C_HATS_MANAGER.register_di_callback(
self.I2C_HATS_MANAGER.register_di_callback, self._address, self._channel, edge_callback
self._address,
self._channel,
edge_callback,
) )
@property @property

View File

@ -2,7 +2,6 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import TYPE_CHECKING
import voluptuous as vol import voluptuous as vol
@ -101,7 +100,6 @@ class I2CHatSwitch(SwitchEntity):
self._channel = channel self._channel = channel
self._name = name or DEVICE_DEFAULT_NAME self._name = name or DEVICE_DEFAULT_NAME
self._invert_logic = invert_logic self._invert_logic = invert_logic
self._state = initial_state
if initial_state is not None: if initial_state is not None:
if self._invert_logic: if self._invert_logic:
state = not initial_state state = not initial_state
@ -109,27 +107,14 @@ class I2CHatSwitch(SwitchEntity):
state = initial_state state = initial_state
self.I2C_HATS_MANAGER.write_dq(self._address, self._channel, state) self.I2C_HATS_MANAGER.write_dq(self._address, self._channel, state)
async def async_added_to_hass(self) -> None: def online_callback():
"""Register callbacks."""
if TYPE_CHECKING:
assert self.I2C_HATS_MANAGER
await self.hass.async_add_executor_job(
self.I2C_HATS_MANAGER.register_online_callback,
self._address,
self._channel,
self.online_callback,
)
def online_callback(self):
"""Call fired when board is online.""" """Call fired when board is online."""
try:
self._state = self.I2C_HATS_MANAGER.read_dq(self._address, self._channel)
except I2CHatsException as ex:
_LOGGER.error(self._log_message(f"Is ON check failed, {ex!s}"))
self._state = False
self.schedule_update_ha_state() self.schedule_update_ha_state()
self.I2C_HATS_MANAGER.register_online_callback(
self._address, self._channel, online_callback
)
def _log_message(self, message): def _log_message(self, message):
"""Create log message.""" """Create log message."""
string = f"{self._name} " string = f"{self._name} "
@ -150,7 +135,12 @@ class I2CHatSwitch(SwitchEntity):
@property @property
def is_on(self): def is_on(self):
"""Return true if device is on.""" """Return true if device is on."""
return self._state != self._invert_logic try:
state = self.I2C_HATS_MANAGER.read_dq(self._address, self._channel)
return state != self._invert_logic
except I2CHatsException as ex:
_LOGGER.error(self._log_message(f"Is ON check failed, {ex!s}"))
return False
def turn_on(self, **kwargs): def turn_on(self, **kwargs):
"""Turn the device on.""" """Turn the device on."""

View File

@ -23,7 +23,7 @@ UID_POSTFIX = "01400"
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
_T = TypeVar("_T", "SonosSpeaker", "SonosEntity") _T = TypeVar("_T", bound="SonosSpeaker | SonosEntity")
_R = TypeVar("_R") _R = TypeVar("_R")
_P = ParamSpec("_P") _P = ParamSpec("_P")

View File

@ -3,6 +3,7 @@ from __future__ import annotations
import datetime import datetime
import logging import logging
from typing import Any
from soco.exceptions import SoCoException, SoCoSlaveException, SoCoUPnPException from soco.exceptions import SoCoException, SoCoSlaveException, SoCoUPnPException
@ -342,20 +343,20 @@ class SonosAlarmEntity(SonosEntity, SwitchEntity):
ATTR_INCLUDE_LINKED_ZONES: self.alarm.include_linked_zones, ATTR_INCLUDE_LINKED_ZONES: self.alarm.include_linked_zones,
} }
async def async_turn_on(self, **kwargs) -> None: def turn_on(self, **kwargs: Any) -> None:
"""Turn alarm switch on.""" """Turn alarm switch on."""
await self.async_handle_switch_on_off(turn_on=True) self._handle_switch_on_off(turn_on=True)
async def async_turn_off(self, **kwargs) -> None: def turn_off(self, **kwargs: Any) -> None:
"""Turn alarm switch off.""" """Turn alarm switch off."""
await self.async_handle_switch_on_off(turn_on=False) self._handle_switch_on_off(turn_on=False)
async def async_handle_switch_on_off(self, turn_on: bool) -> None: def _handle_switch_on_off(self, turn_on: bool) -> None:
"""Handle turn on/off of alarm switch.""" """Handle turn on/off of alarm switch."""
try: try:
_LOGGER.debug("Toggling the state of %s", self.entity_id) _LOGGER.debug("Toggling the state of %s", self.entity_id)
self.alarm.enabled = turn_on self.alarm.enabled = turn_on
await self.hass.async_add_executor_job(self.alarm.save) self.alarm.save()
except (OSError, SoCoException, SoCoUPnPException) as exc: except (OSError, SoCoException, SoCoUPnPException) as exc:
_LOGGER.error("Could not update %s: %s", self.entity_id, exc) _LOGGER.error("Could not update %s: %s", self.entity_id, exc)

View File

@ -121,6 +121,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
raise ConfigEntryNotReady from err raise ConfigEntryNotReady from err
async def _update_devices() -> list[dict[str, Any]]: async def _update_devices() -> list[dict[str, Any]]:
if not session.valid_token:
await session.async_ensure_token_valid()
await hass.async_add_executor_job(
spotify.set_auth, session.token["access_token"]
)
try: try:
devices: dict[str, Any] | None = await hass.async_add_executor_job( devices: dict[str, Any] | None = await hass.async_add_executor_job(
spotify.devices spotify.devices

View File

@ -515,9 +515,7 @@ class SpotifyMediaPlayer(MediaPlayerEntity):
run_coroutine_threadsafe( run_coroutine_threadsafe(
self._session.async_ensure_token_valid(), self.hass.loop self._session.async_ensure_token_valid(), self.hass.loop
).result() ).result()
self._spotify_data[DATA_SPOTIFY_CLIENT] = Spotify( self._spotify.set_auth(auth=self._session.token["access_token"])
auth=self._session.token["access_token"]
)
current = self._spotify.current_playback() current = self._spotify.current_playback()
self._currently_playing = current or {} self._currently_playing = current or {}
@ -581,7 +579,11 @@ async def async_browse_media_internal(
partial(library_payload, can_play_artist=can_play_artist) partial(library_payload, can_play_artist=can_play_artist)
) )
if not session.valid_token:
await session.async_ensure_token_valid() await session.async_ensure_token_valid()
await hass.async_add_executor_job(
spotify.set_auth, session.token["access_token"]
)
# Strip prefix # Strip prefix
media_content_type = media_content_type[len(MEDIA_PLAYER_PREFIX) :] media_content_type = media_content_type[len(MEDIA_PLAYER_PREFIX) :]

View File

@ -342,7 +342,9 @@ class Stream:
stream_state.discontinuity() stream_state.discontinuity()
if not self.keepalive or self._thread_quit.is_set(): if not self.keepalive or self._thread_quit.is_set():
if self._fast_restart_once: if self._fast_restart_once:
# The stream source is updated, restart without any delay. # The stream source is updated, restart without any delay and reset the retry
# backoff for the new url.
wait_timeout = 0
self._fast_restart_once = False self._fast_restart_once = False
self._thread_quit.clear() self._thread_quit.clear()
continue continue

View File

@ -8,7 +8,7 @@ DATA_BRIDGE = "bridge"
DATA_DEVICE = "device" DATA_DEVICE = "device"
DATA_DISCOVERY = "discovery" DATA_DISCOVERY = "discovery"
DISCOVERY_TIME_SEC = 6 DISCOVERY_TIME_SEC = 12
SIGNAL_DEVICE_ADD = "switcher_device_add" SIGNAL_DEVICE_ADD = "switcher_device_add"
@ -19,4 +19,4 @@ SERVICE_SET_AUTO_OFF_NAME = "set_auto_off"
SERVICE_TURN_ON_WITH_TIMER_NAME = "turn_on_with_timer" SERVICE_TURN_ON_WITH_TIMER_NAME = "turn_on_with_timer"
# Defines the maximum interval device must send an update before it marked unavailable # Defines the maximum interval device must send an update before it marked unavailable
MAX_UPDATE_INTERVAL_SEC = 20 MAX_UPDATE_INTERVAL_SEC = 30

View File

@ -158,7 +158,10 @@ async def async_setup_entry(
device = hass_data.device_manager.device_map[device_id] device = hass_data.device_manager.device_map[device_id]
if descriptions := COVERS.get(device.category): if descriptions := COVERS.get(device.category):
for description in descriptions: for description in descriptions:
if description.key in device.status: if (
description.key in device.function
or description.key in device.status_range
):
entities.append( entities.append(
TuyaCoverEntity( TuyaCoverEntity(
device, hass_data.device_manager, description device, hass_data.device_manager, description

View File

@ -182,8 +182,6 @@ class TariffSelect(RestoreEntity):
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Run when entity about to be added.""" """Run when entity about to be added."""
await super().async_added_to_hass() await super().async_added_to_hass()
if self._current_tariff is not None:
return
state = await self.async_get_last_state() state = await self.async_get_last_state()
if not state or state.state not in self._tariffs: if not state or state.state not in self._tariffs:

View File

@ -32,6 +32,7 @@ from homeassistant.helpers.event import (
async_track_state_change_event, async_track_state_change_event,
) )
from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.template import is_number
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
@ -166,13 +167,10 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
self._parent_meter = parent_meter self._parent_meter = parent_meter
self._sensor_source_id = source_entity self._sensor_source_id = source_entity
self._state = None self._state = None
self._last_period = 0 self._last_period = Decimal(0)
self._last_reset = dt_util.utcnow() self._last_reset = dt_util.utcnow()
self._collecting = None self._collecting = None
if name:
self._name = name self._name = name
else:
self._name = f"{source_entity} meter"
self._unit_of_measurement = None self._unit_of_measurement = None
self._period = meter_type self._period = meter_type
if meter_type is not None: if meter_type is not None:
@ -231,8 +229,6 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
return return
self._state += adjustment self._state += adjustment
except ValueError as err:
_LOGGER.warning("While processing state changes: %s", err)
except DecimalException as err: except DecimalException as err:
_LOGGER.warning( _LOGGER.warning(
"Invalid state (%s > %s): %s", old_state.state, new_state.state, err "Invalid state (%s > %s): %s", old_state.state, new_state.state, err
@ -282,7 +278,7 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
return return
_LOGGER.debug("Reset utility meter <%s>", self.entity_id) _LOGGER.debug("Reset utility meter <%s>", self.entity_id)
self._last_reset = dt_util.utcnow() self._last_reset = dt_util.utcnow()
self._last_period = str(self._state) self._last_period = Decimal(self._state) if self._state else Decimal(0)
self._state = 0 self._state = 0
self.async_write_ha_state() self.async_write_ha_state()
@ -319,9 +315,10 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
ATTR_UNIT_OF_MEASUREMENT ATTR_UNIT_OF_MEASUREMENT
) )
self._last_period = ( self._last_period = (
float(state.attributes.get(ATTR_LAST_PERIOD)) Decimal(state.attributes[ATTR_LAST_PERIOD])
if state.attributes.get(ATTR_LAST_PERIOD) if state.attributes.get(ATTR_LAST_PERIOD)
else 0 and is_number(state.attributes[ATTR_LAST_PERIOD])
else Decimal(0)
) )
self._last_reset = dt_util.as_utc( self._last_reset = dt_util.as_utc(
dt_util.parse_datetime(state.attributes.get(ATTR_LAST_RESET)) dt_util.parse_datetime(state.attributes.get(ATTR_LAST_RESET))
@ -399,7 +396,7 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
state_attr = { state_attr = {
ATTR_SOURCE_ID: self._sensor_source_id, ATTR_SOURCE_ID: self._sensor_source_id,
ATTR_STATUS: PAUSED if self._collecting is None else COLLECTING, ATTR_STATUS: PAUSED if self._collecting is None else COLLECTING,
ATTR_LAST_PERIOD: self._last_period, ATTR_LAST_PERIOD: str(self._last_period),
} }
if self._period is not None: if self._period is not None:
state_attr[ATTR_PERIOD] = self._period state_attr[ATTR_PERIOD] = self._period

View File

@ -14,8 +14,8 @@ import requests
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ENTITY_CATEGORY_CONFIG
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import ViCareRequiredKeysMixin from . import ViCareRequiredKeysMixin
@ -36,7 +36,7 @@ BUTTON_DESCRIPTIONS: tuple[ViCareButtonEntityDescription, ...] = (
key=BUTTON_DHW_ACTIVATE_ONETIME_CHARGE, key=BUTTON_DHW_ACTIVATE_ONETIME_CHARGE,
name="Activate one-time charge", name="Activate one-time charge",
icon="mdi:shower-head", icon="mdi:shower-head",
entity_category=ENTITY_CATEGORY_CONFIG, entity_category=EntityCategory.CONFIG,
value_getter=lambda api: api.activateOneTimeCharge(), value_getter=lambda api: api.activateOneTimeCharge(),
), ),
) )

View File

@ -145,7 +145,7 @@ class VizioDevice(MediaPlayerEntity):
self._volume_step = config_entry.options[CONF_VOLUME_STEP] self._volume_step = config_entry.options[CONF_VOLUME_STEP]
self._current_input = None self._current_input = None
self._current_app_config = None self._current_app_config = None
self._app_name = None self._attr_app_name = None
self._available_inputs = [] self._available_inputs = []
self._available_apps = [] self._available_apps = []
self._all_apps = apps_coordinator.data if apps_coordinator else None self._all_apps = apps_coordinator.data if apps_coordinator else None
@ -209,7 +209,7 @@ class VizioDevice(MediaPlayerEntity):
self._attr_volume_level = None self._attr_volume_level = None
self._attr_is_volume_muted = None self._attr_is_volume_muted = None
self._current_input = None self._current_input = None
self._app_name = None self._attr_app_name = None
self._current_app_config = None self._current_app_config = None
self._attr_sound_mode = None self._attr_sound_mode = None
return return
@ -265,13 +265,13 @@ class VizioDevice(MediaPlayerEntity):
log_api_exception=False log_api_exception=False
) )
self._app_name = find_app_name( self._attr_app_name = find_app_name(
self._current_app_config, self._current_app_config,
[APP_HOME, *self._all_apps, *self._additional_app_configs], [APP_HOME, *self._all_apps, *self._additional_app_configs],
) )
if self._app_name == NO_APP_RUNNING: if self._attr_app_name == NO_APP_RUNNING:
self._app_name = None self._attr_app_name = None
def _get_additional_app_names(self) -> list[dict[str, Any]]: def _get_additional_app_names(self) -> list[dict[str, Any]]:
"""Return list of additional apps that were included in configuration.yaml.""" """Return list of additional apps that were included in configuration.yaml."""
@ -337,8 +337,8 @@ class VizioDevice(MediaPlayerEntity):
@property @property
def source(self) -> str | None: def source(self) -> str | None:
"""Return current input of the device.""" """Return current input of the device."""
if self._app_name is not None and self._current_input in INPUT_APPS: if self._attr_app_name is not None and self._current_input in INPUT_APPS:
return self._app_name return self._attr_app_name
return self._current_input return self._current_input
@ -364,14 +364,6 @@ class VizioDevice(MediaPlayerEntity):
return self._available_inputs return self._available_inputs
@property
def app_name(self) -> str | None:
"""Return the name of the current app."""
if self.source == self._app_name:
return self._app_name
return None
@property @property
def app_id(self) -> str | None: def app_id(self) -> str | None:
"""Return the ID of the current app if it is unknown by pyvizio.""" """Return the ID of the current app if it is unknown by pyvizio."""

View File

@ -8,9 +8,10 @@ from typing import Any
from homeassistant.components.button import ButtonDeviceClass, ButtonEntity from homeassistant.components.button import ButtonDeviceClass, ButtonEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ENTITY_CATEGORY_DIAGNOSTIC, Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .core import discovery from .core import discovery
@ -96,7 +97,7 @@ class ZHAIdentifyButton(ZHAButton):
return cls(unique_id, zha_device, channels, **kwargs) return cls(unique_id, zha_device, channels, **kwargs)
_attr_device_class: ButtonDeviceClass = ButtonDeviceClass.UPDATE _attr_device_class: ButtonDeviceClass = ButtonDeviceClass.UPDATE
_attr_entity_category = ENTITY_CATEGORY_DIAGNOSTIC _attr_entity_category = EntityCategory.DIAGNOSTIC
_command_name = "identify" _command_name = "identify"
def get_args(self) -> list[Any]: def get_args(self) -> list[Any]:

View File

@ -8,9 +8,10 @@ from zigpy.zcl.clusters.security import IasWd
from homeassistant.components.select import SelectEntity from homeassistant.components.select import SelectEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ENTITY_CATEGORY_CONFIG, STATE_UNKNOWN, Platform from homeassistant.const import STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .core import discovery from .core import discovery
@ -46,7 +47,7 @@ async def async_setup_entry(
class ZHAEnumSelectEntity(ZhaEntity, SelectEntity): class ZHAEnumSelectEntity(ZhaEntity, SelectEntity):
"""Representation of a ZHA select entity.""" """Representation of a ZHA select entity."""
_attr_entity_category = ENTITY_CATEGORY_CONFIG _attr_entity_category = EntityCategory.CONFIG
_enum: Enum = None _enum: Enum = None
def __init__( def __init__(

View File

@ -25,7 +25,6 @@ from homeassistant.const import (
ELECTRIC_CURRENT_AMPERE, ELECTRIC_CURRENT_AMPERE,
ELECTRIC_POTENTIAL_VOLT, ELECTRIC_POTENTIAL_VOLT,
ENERGY_KILO_WATT_HOUR, ENERGY_KILO_WATT_HOUR,
ENTITY_CATEGORY_DIAGNOSTIC,
LIGHT_LUX, LIGHT_LUX,
PERCENTAGE, PERCENTAGE,
POWER_VOLT_AMPERE, POWER_VOLT_AMPERE,
@ -699,7 +698,7 @@ class RSSISensor(Sensor, id_suffix="rssi"):
_state_class: SensorStateClass = SensorStateClass.MEASUREMENT _state_class: SensorStateClass = SensorStateClass.MEASUREMENT
_device_class: SensorDeviceClass = SensorDeviceClass.SIGNAL_STRENGTH _device_class: SensorDeviceClass = SensorDeviceClass.SIGNAL_STRENGTH
_attr_entity_category = ENTITY_CATEGORY_DIAGNOSTIC _attr_entity_category = EntityCategory.DIAGNOSTIC
_attr_entity_registry_enabled_default = False _attr_entity_registry_enabled_default = False
@classmethod @classmethod

View File

@ -7,7 +7,7 @@ from .backports.enum import StrEnum
MAJOR_VERSION: Final = 2022 MAJOR_VERSION: Final = 2022
MINOR_VERSION: Final = 2 MINOR_VERSION: Final = 2
PATCH_VERSION: Final = "6" PATCH_VERSION: Final = "7"
__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, 9, 0) REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0)

View File

@ -114,7 +114,7 @@ adext==0.4.2
adguardhome==0.5.1 adguardhome==0.5.1
# homeassistant.components.advantage_air # homeassistant.components.advantage_air
advantage_air==0.2.5 advantage_air==0.3.0
# homeassistant.components.frontier_silicon # homeassistant.components.frontier_silicon
afsapi==0.0.4 afsapi==0.0.4
@ -191,7 +191,7 @@ aiohomekit==0.6.11
aiohttp_cors==0.7.0 aiohttp_cors==0.7.0
# homeassistant.components.hue # homeassistant.components.hue
aiohue==4.0.1 aiohue==4.1.2
# homeassistant.components.homewizard # homeassistant.components.homewizard
aiohwenergy==0.8.0 aiohwenergy==0.8.0
@ -681,7 +681,7 @@ fjaraskupan==1.0.2
flipr-api==1.4.1 flipr-api==1.4.1
# homeassistant.components.flux_led # homeassistant.components.flux_led
flux_led==0.28.22 flux_led==0.28.26
# homeassistant.components.homekit # homeassistant.components.homekit
fnvhash==0.1.0 fnvhash==0.1.0

View File

@ -70,7 +70,7 @@ adext==0.4.2
adguardhome==0.5.1 adguardhome==0.5.1
# homeassistant.components.advantage_air # homeassistant.components.advantage_air
advantage_air==0.2.5 advantage_air==0.3.0
# homeassistant.components.agent_dvr # homeassistant.components.agent_dvr
agent-py==0.0.23 agent-py==0.0.23
@ -141,7 +141,7 @@ aiohomekit==0.6.11
aiohttp_cors==0.7.0 aiohttp_cors==0.7.0
# homeassistant.components.hue # homeassistant.components.hue
aiohue==4.0.1 aiohue==4.1.2
# homeassistant.components.homewizard # homeassistant.components.homewizard
aiohwenergy==0.8.0 aiohwenergy==0.8.0
@ -427,7 +427,7 @@ fjaraskupan==1.0.2
flipr-api==1.4.1 flipr-api==1.4.1
# homeassistant.components.flux_led # homeassistant.components.flux_led
flux_led==0.28.22 flux_led==0.28.26
# homeassistant.components.homekit # homeassistant.components.homekit
fnvhash==0.1.0 fnvhash==0.1.0

View File

@ -1,6 +1,6 @@
[metadata] [metadata]
name = homeassistant name = homeassistant
version = 2022.2.6 version = 2022.2.7
author = The Home Assistant Authors author = The Home Assistant Authors
author_email = hello@home-assistant.io author_email = hello@home-assistant.io
license = Apache-2.0 license = Apache-2.0

View File

@ -8,7 +8,6 @@ from unittest.mock import AsyncMock, Mock, patch
import aiohue.v1 as aiohue_v1 import aiohue.v1 as aiohue_v1
import aiohue.v2 as aiohue_v2 import aiohue.v2 as aiohue_v2
from aiohue.v2.controllers.events import EventType from aiohue.v2.controllers.events import EventType
from aiohue.v2.models.clip import parse_clip_resource
import pytest import pytest
from homeassistant.components import hue from homeassistant.components import hue
@ -187,7 +186,7 @@ def create_mock_api_v2(hass):
def emit_event(event_type, data): def emit_event(event_type, data):
"""Emit an event from a (hue resource) dict.""" """Emit an event from a (hue resource) dict."""
api.events.emit(EventType(event_type), parse_clip_resource(data)) api.events.emit(EventType(event_type), data)
api.load_test_data = load_test_data api.load_test_data = load_test_data
api.emit_event = emit_event api.emit_event = emit_event

View File

@ -97,8 +97,12 @@ async def test_light_turn_on_service(hass, mock_bridge_v2, v2_resources_test_dat
assert mock_bridge_v2.mock_requests[0]["json"]["color_temperature"]["mirek"] == 300 assert mock_bridge_v2.mock_requests[0]["json"]["color_temperature"]["mirek"] == 300
# Now generate update event by emitting the json we've sent as incoming event # Now generate update event by emitting the json we've sent as incoming event
mock_bridge_v2.mock_requests[0]["json"]["color_temperature"].pop("mirek_valid") event = {
mock_bridge_v2.api.emit_event("update", mock_bridge_v2.mock_requests[0]["json"]) "id": "3a6710fa-4474-4eba-b533-5e6e72968feb",
"type": "light",
**mock_bridge_v2.mock_requests[0]["json"],
}
mock_bridge_v2.api.emit_event("update", event)
await hass.async_block_till_done() await hass.async_block_till_done()
# the light should now be on # the light should now be on
@ -186,7 +190,12 @@ async def test_light_turn_off_service(hass, mock_bridge_v2, v2_resources_test_da
assert mock_bridge_v2.mock_requests[0]["json"]["on"]["on"] is False assert mock_bridge_v2.mock_requests[0]["json"]["on"]["on"] is False
# Now generate update event by emitting the json we've sent as incoming event # Now generate update event by emitting the json we've sent as incoming event
mock_bridge_v2.api.emit_event("update", mock_bridge_v2.mock_requests[0]["json"]) event = {
"id": "02cba059-9c2c-4d45-97e4-4f79b1bfbaa1",
"type": "light",
**mock_bridge_v2.mock_requests[0]["json"],
}
mock_bridge_v2.api.emit_event("update", event)
await hass.async_block_till_done() await hass.async_block_till_done()
# the light should now be off # the light should now be off
@ -377,10 +386,20 @@ async def test_grouped_lights(hass, mock_bridge_v2, v2_resources_test_data):
) )
# Now generate update events by emitting the json we've sent as incoming events # Now generate update events by emitting the json we've sent as incoming events
for index in range(0, 3): for index, light_id in enumerate(
mock_bridge_v2.api.emit_event( [
"update", mock_bridge_v2.mock_requests[index]["json"] "02cba059-9c2c-4d45-97e4-4f79b1bfbaa1",
) "b3fe71ef-d0ef-48de-9355-d9e604377df0",
"8015b17f-8336-415b-966a-b364bd082397",
]
):
event = {
"id": light_id,
"type": "light",
**mock_bridge_v2.mock_requests[index]["json"],
}
mock_bridge_v2.api.emit_event("update", event)
await hass.async_block_till_done()
await hass.async_block_till_done() await hass.async_block_till_done()
# the light should now be on and have the properties we've set # the light should now be on and have the properties we've set
@ -406,6 +425,12 @@ async def test_grouped_lights(hass, mock_bridge_v2, v2_resources_test_data):
assert mock_bridge_v2.mock_requests[0]["json"]["on"]["on"] is False assert mock_bridge_v2.mock_requests[0]["json"]["on"]["on"] is False
# Now generate update event by emitting the json we've sent as incoming event # Now generate update event by emitting the json we've sent as incoming event
event = {
"id": "f2416154-9607-43ab-a684-4453108a200e",
"type": "grouped_light",
**mock_bridge_v2.mock_requests[0]["json"],
}
mock_bridge_v2.api.emit_event("update", event)
mock_bridge_v2.api.emit_event("update", mock_bridge_v2.mock_requests[0]["json"]) mock_bridge_v2.api.emit_event("update", mock_bridge_v2.mock_requests[0]["json"])
await hass.async_block_till_done() await hass.async_block_till_done()

View File

@ -69,7 +69,12 @@ async def test_switch_turn_off_service(hass, mock_bridge_v2, v2_resources_test_d
assert mock_bridge_v2.mock_requests[0]["json"]["enabled"] is False assert mock_bridge_v2.mock_requests[0]["json"]["enabled"] is False
# Now generate update event by emitting the json we've sent as incoming event # Now generate update event by emitting the json we've sent as incoming event
mock_bridge_v2.api.emit_event("update", mock_bridge_v2.mock_requests[0]["json"]) event = {
"id": "b6896534-016d-4052-8cb4-ef04454df62c",
"type": "motion",
**mock_bridge_v2.mock_requests[0]["json"],
}
mock_bridge_v2.api.emit_event("update", event)
await hass.async_block_till_done() await hass.async_block_till_done()
# the switch should now be off # the switch should now be off

View File

@ -62,7 +62,12 @@ async def test_services(hass):
"source": "sensor.energy", "source": "sensor.energy",
"cycle": "hourly", "cycle": "hourly",
"tariffs": ["peak", "offpeak"], "tariffs": ["peak", "offpeak"],
} },
"energy_bill2": {
"source": "sensor.energy",
"cycle": "hourly",
"tariffs": ["peak", "offpeak"],
},
} }
} }
@ -153,6 +158,10 @@ async def test_services(hass):
state = hass.states.get("sensor.energy_bill_offpeak") state = hass.states.get("sensor.energy_bill_offpeak")
assert state.state == "0" assert state.state == "0"
# meanwhile energy_bill2_peak accumulated all kWh
state = hass.states.get("sensor.energy_bill2_peak")
assert state.state == "4"
async def test_cron(hass, legacy_patchable_time): async def test_cron(hass, legacy_patchable_time):
"""Test cron pattern and offset fails.""" """Test cron pattern and offset fails."""

View File

@ -304,6 +304,10 @@ async def test_restore_state(hass):
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
}, },
), ),
State(
"sensor.energy_bill_midpeak",
"error",
),
State( State(
"sensor.energy_bill_offpeak", "sensor.energy_bill_offpeak",
"6", "6",
@ -326,6 +330,9 @@ async def test_restore_state(hass):
assert state.attributes.get("last_reset") == last_reset assert state.attributes.get("last_reset") == last_reset
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
state = hass.states.get("sensor.energy_bill_midpeak")
assert state.state == STATE_UNKNOWN
state = hass.states.get("sensor.energy_bill_offpeak") state = hass.states.get("sensor.energy_bill_offpeak")
assert state.state == "6" assert state.state == "6"
assert state.attributes.get("status") == COLLECTING assert state.attributes.get("status") == COLLECTING
@ -530,7 +537,7 @@ async def _test_self_reset(hass, config, start_time, expect_reset=True):
assert state.attributes.get("last_reset") == now.isoformat() assert state.attributes.get("last_reset") == now.isoformat()
assert state.state == "3" assert state.state == "3"
else: else:
assert state.attributes.get("last_period") == 0 assert state.attributes.get("last_period") == "0"
assert state.state == "5" assert state.state == "5"
start_time_str = dt_util.parse_datetime(start_time).isoformat() start_time_str = dt_util.parse_datetime(start_time).isoformat()
assert state.attributes.get("last_reset") == start_time_str assert state.attributes.get("last_reset") == start_time_str
@ -559,7 +566,7 @@ async def _test_self_reset(hass, config, start_time, expect_reset=True):
assert state.attributes.get("last_period") == "2" assert state.attributes.get("last_period") == "2"
assert state.state == "7" assert state.state == "7"
else: else:
assert state.attributes.get("last_period") == 0 assert state.attributes.get("last_period") == "0"
assert state.state == "9" assert state.state == "9"

View File

@ -764,6 +764,5 @@ async def test_vizio_update_with_apps_on_input(
) )
await _add_config_entry_to_hass(hass, config_entry) await _add_config_entry_to_hass(hass, config_entry)
attr = _get_attr_and_assert_base_attr(hass, DEVICE_CLASS_TV, STATE_ON) attr = _get_attr_and_assert_base_attr(hass, DEVICE_CLASS_TV, STATE_ON)
# App name and app ID should not be in the attributes # app ID should not be in the attributes
assert "app_name" not in attr
assert "app_id" not in attr assert "app_id" not in attr