mirror of
https://github.com/home-assistant/core.git
synced 2026-02-03 22:05:35 +01:00
Compare commits
4 Commits
dependabot
...
pr-162044
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b9647e55e | ||
|
|
1146899115 | ||
|
|
eb4577ef33 | ||
|
|
cd2c946107 |
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -24,11 +24,11 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # v4.32.1
|
||||
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
||||
with:
|
||||
languages: python
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # v4.32.1
|
||||
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
||||
with:
|
||||
category: "/language:python"
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"preview_features": {
|
||||
"snapshots": {
|
||||
"feedback_url": "https://forms.gle/GqvRmgmghSDco8M46",
|
||||
"learn_more_url": "https://www.home-assistant.io/blog/2026/02/02/about-device-database/",
|
||||
"report_issue_url": "https://github.com/OHF-Device-Database/device-database/issues/new"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""Support for Baidu speech service."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from aip import AipSpeech
|
||||
import voluptuous as vol
|
||||
@@ -10,7 +9,6 @@ from homeassistant.components.tts import (
|
||||
CONF_LANG,
|
||||
PLATFORM_SCHEMA as TTS_PLATFORM_SCHEMA,
|
||||
Provider,
|
||||
TtsAudioType,
|
||||
)
|
||||
from homeassistant.const import CONF_API_KEY
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
@@ -87,17 +85,17 @@ class BaiduTTSProvider(Provider):
|
||||
}
|
||||
|
||||
@property
|
||||
def default_language(self) -> str:
|
||||
def default_language(self):
|
||||
"""Return the default language."""
|
||||
return self._lang
|
||||
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
def supported_languages(self):
|
||||
"""Return a list of supported languages."""
|
||||
return SUPPORTED_LANGUAGES
|
||||
|
||||
@property
|
||||
def default_options(self) -> dict[str, Any]:
|
||||
def default_options(self):
|
||||
"""Return a dict including default options."""
|
||||
return {
|
||||
CONF_PERSON: self._speech_conf_data[_OPTIONS[CONF_PERSON]],
|
||||
@@ -107,16 +105,11 @@ class BaiduTTSProvider(Provider):
|
||||
}
|
||||
|
||||
@property
|
||||
def supported_options(self) -> list[str]:
|
||||
def supported_options(self):
|
||||
"""Return a list of supported options."""
|
||||
return SUPPORTED_OPTIONS
|
||||
|
||||
def get_tts_audio(
|
||||
self,
|
||||
message: str,
|
||||
language: str,
|
||||
options: dict[str, Any],
|
||||
) -> TtsAudioType:
|
||||
def get_tts_audio(self, message, language, options):
|
||||
"""Load TTS from BaiduTTS."""
|
||||
|
||||
aip_speech = AipSpeech(
|
||||
|
||||
@@ -6,9 +6,16 @@ from typing import Any
|
||||
|
||||
from blinkpy.auth import Auth
|
||||
from blinkpy.blinkpy import Blink
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import persistent_notification
|
||||
from homeassistant.const import CONF_SCAN_INTERVAL
|
||||
from homeassistant.const import (
|
||||
CONF_FILE_PATH,
|
||||
CONF_FILENAME,
|
||||
CONF_NAME,
|
||||
CONF_PIN,
|
||||
CONF_SCAN_INTERVAL,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
@@ -20,6 +27,13 @@ from .services import async_setup_services
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SERVICE_SAVE_VIDEO_SCHEMA = vol.Schema(
|
||||
{vol.Required(CONF_NAME): cv.string, vol.Required(CONF_FILENAME): cv.string}
|
||||
)
|
||||
SERVICE_SEND_PIN_SCHEMA = vol.Schema({vol.Optional(CONF_PIN): cv.string})
|
||||
SERVICE_SAVE_RECENT_CLIPS_SCHEMA = vol.Schema(
|
||||
{vol.Required(CONF_NAME): cv.string, vol.Required(CONF_FILE_PATH): cv.string}
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
|
||||
|
||||
@@ -9,23 +9,35 @@ from typing import Any
|
||||
from blinkpy.auth import UnauthorizedError
|
||||
from blinkpy.camera import BlinkCamera as BlinkCameraAPI
|
||||
from requests.exceptions import ChunkedEncodingError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.camera import Camera
|
||||
from homeassistant.const import CONF_FILE_PATH, CONF_FILENAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import (
|
||||
ConfigEntryAuthFailed,
|
||||
HomeAssistantError,
|
||||
ServiceValidationError,
|
||||
)
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DEFAULT_BRAND, DOMAIN
|
||||
from .const import (
|
||||
DEFAULT_BRAND,
|
||||
DOMAIN,
|
||||
SERVICE_RECORD,
|
||||
SERVICE_SAVE_RECENT_CLIPS,
|
||||
SERVICE_SAVE_VIDEO,
|
||||
SERVICE_TRIGGER,
|
||||
)
|
||||
from .coordinator import BlinkConfigEntry, BlinkUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTR_VIDEO_CLIP = "video"
|
||||
ATTR_IMAGE = "image"
|
||||
PARALLEL_UPDATES = 1
|
||||
|
||||
|
||||
@@ -44,6 +56,20 @@ async def async_setup_entry(
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
platform.async_register_entity_service(SERVICE_RECORD, None, "record")
|
||||
platform.async_register_entity_service(SERVICE_TRIGGER, None, "trigger_camera")
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SAVE_RECENT_CLIPS,
|
||||
{vol.Required(CONF_FILE_PATH): cv.string},
|
||||
"save_recent_clips",
|
||||
)
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SAVE_VIDEO,
|
||||
{vol.Required(CONF_FILENAME): cv.string},
|
||||
"save_video",
|
||||
)
|
||||
|
||||
|
||||
class BlinkCamera(CoordinatorEntity[BlinkUpdateCoordinator], Camera):
|
||||
"""An implementation of a Blink Camera."""
|
||||
|
||||
@@ -20,6 +20,11 @@ TYPE_TEMPERATURE = "temperature"
|
||||
TYPE_BATTERY = "battery"
|
||||
TYPE_WIFI_STRENGTH = "wifi_strength"
|
||||
|
||||
SERVICE_RECORD = "record"
|
||||
SERVICE_TRIGGER = "trigger_camera"
|
||||
SERVICE_SAVE_VIDEO = "save_video"
|
||||
SERVICE_SAVE_RECENT_CLIPS = "save_recent_clips"
|
||||
SERVICE_SEND_PIN = "send_pin"
|
||||
|
||||
PLATFORMS = [
|
||||
Platform.ALARM_CONTROL_PANEL,
|
||||
|
||||
@@ -4,27 +4,13 @@ from __future__ import annotations
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
|
||||
from homeassistant.const import (
|
||||
ATTR_CONFIG_ENTRY_ID,
|
||||
CONF_FILE_PATH,
|
||||
CONF_FILENAME,
|
||||
CONF_PIN,
|
||||
)
|
||||
from homeassistant.const import ATTR_CONFIG_ENTRY_ID, CONF_PIN
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv, issue_registry as ir, service
|
||||
from homeassistant.helpers import config_validation as cv, issue_registry as ir
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import DOMAIN, SERVICE_SEND_PIN
|
||||
|
||||
SERVICE_RECORD = "record"
|
||||
SERVICE_TRIGGER = "trigger_camera"
|
||||
SERVICE_SAVE_VIDEO = "save_video"
|
||||
SERVICE_SAVE_RECENT_CLIPS = "save_recent_clips"
|
||||
|
||||
|
||||
# Deprecated
|
||||
SERVICE_SEND_PIN = "send_pin"
|
||||
SERVICE_SEND_PIN_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_CONFIG_ENTRY_ID): vol.All(cv.ensure_list, [cv.string]),
|
||||
@@ -66,36 +52,3 @@ def async_setup_services(hass: HomeAssistant) -> None:
|
||||
_send_pin,
|
||||
schema=SERVICE_SEND_PIN_SCHEMA,
|
||||
)
|
||||
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_RECORD,
|
||||
entity_domain=CAMERA_DOMAIN,
|
||||
schema=None,
|
||||
func="record",
|
||||
)
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_TRIGGER,
|
||||
entity_domain=CAMERA_DOMAIN,
|
||||
schema=None,
|
||||
func="trigger_camera",
|
||||
)
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_SAVE_RECENT_CLIPS,
|
||||
entity_domain=CAMERA_DOMAIN,
|
||||
schema={vol.Required(CONF_FILE_PATH): cv.string},
|
||||
func="save_recent_clips",
|
||||
)
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_SAVE_VIDEO,
|
||||
entity_domain=CAMERA_DOMAIN,
|
||||
schema={vol.Required(CONF_FILENAME): cv.string},
|
||||
func="save_video",
|
||||
)
|
||||
|
||||
@@ -16,17 +16,14 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import config_validation as cv, device_registry as dr
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.entity import SLOW_UPDATE_WARNING
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import BRIDGE_MAKE, DOMAIN
|
||||
from .models import BondData
|
||||
from .services import async_setup_services
|
||||
from .utils import BondHub
|
||||
|
||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
PLATFORMS = [
|
||||
Platform.BUTTON,
|
||||
Platform.COVER,
|
||||
@@ -41,12 +38,6 @@ _LOGGER = logging.getLogger(__name__)
|
||||
type BondConfigEntry = ConfigEntry[BondData]
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the component."""
|
||||
async_setup_services(hass)
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: BondConfigEntry) -> bool:
|
||||
"""Set up Bond from a config entry."""
|
||||
host = entry.data[CONF_HOST]
|
||||
|
||||
@@ -5,3 +5,10 @@ BRIDGE_MAKE = "Olibra"
|
||||
DOMAIN = "bond"
|
||||
|
||||
CONF_BOND_ID: str = "bond_id"
|
||||
|
||||
|
||||
SERVICE_SET_FAN_SPEED_TRACKED_STATE = "set_fan_speed_tracked_state"
|
||||
SERVICE_SET_POWER_TRACKED_STATE = "set_switch_power_tracked_state"
|
||||
SERVICE_SET_LIGHT_POWER_TRACKED_STATE = "set_light_power_tracked_state"
|
||||
SERVICE_SET_LIGHT_BRIGHTNESS_TRACKED_STATE = "set_light_brightness_tracked_state"
|
||||
ATTR_POWER_STATE = "power_state"
|
||||
|
||||
@@ -8,6 +8,7 @@ from typing import Any
|
||||
|
||||
from aiohttp.client_exceptions import ClientResponseError
|
||||
from bond_async import Action, DeviceType, Direction
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.fan import (
|
||||
DIRECTION_FORWARD,
|
||||
@@ -17,6 +18,7 @@ from homeassistant.components.fan import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_platform
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.util.percentage import (
|
||||
percentage_to_ranged_value,
|
||||
@@ -25,6 +27,7 @@ from homeassistant.util.percentage import (
|
||||
from homeassistant.util.scaling import int_states_in_range
|
||||
|
||||
from . import BondConfigEntry
|
||||
from .const import SERVICE_SET_FAN_SPEED_TRACKED_STATE
|
||||
from .entity import BondEntity
|
||||
from .models import BondData
|
||||
from .utils import BondDevice
|
||||
@@ -41,6 +44,12 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Set up Bond fan devices."""
|
||||
data = entry.runtime_data
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SET_FAN_SPEED_TRACKED_STATE,
|
||||
{vol.Required("speed"): vol.All(vol.Number(scale=0), vol.Range(0, 100))},
|
||||
"async_set_speed_belief",
|
||||
)
|
||||
|
||||
async_add_entities(
|
||||
BondFan(data, device)
|
||||
|
||||
@@ -7,20 +7,37 @@ from typing import Any
|
||||
|
||||
from aiohttp.client_exceptions import ClientResponseError
|
||||
from bond_async import Action, DeviceType
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import BondConfigEntry
|
||||
from .const import (
|
||||
ATTR_POWER_STATE,
|
||||
SERVICE_SET_LIGHT_BRIGHTNESS_TRACKED_STATE,
|
||||
SERVICE_SET_LIGHT_POWER_TRACKED_STATE,
|
||||
)
|
||||
from .entity import BondEntity
|
||||
from .models import BondData
|
||||
from .utils import BondDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SERVICE_START_INCREASING_BRIGHTNESS = "start_increasing_brightness"
|
||||
SERVICE_START_DECREASING_BRIGHTNESS = "start_decreasing_brightness"
|
||||
SERVICE_STOP = "stop"
|
||||
|
||||
ENTITY_SERVICES = [
|
||||
SERVICE_START_INCREASING_BRIGHTNESS,
|
||||
SERVICE_START_DECREASING_BRIGHTNESS,
|
||||
SERVICE_STOP,
|
||||
]
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
@@ -31,6 +48,14 @@ async def async_setup_entry(
|
||||
data = entry.runtime_data
|
||||
hub = data.hub
|
||||
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
for service in ENTITY_SERVICES:
|
||||
platform.async_register_entity_service(
|
||||
service,
|
||||
None,
|
||||
f"async_{service}",
|
||||
)
|
||||
|
||||
fan_lights: list[Entity] = [
|
||||
BondLight(data, device)
|
||||
for device in hub.devices
|
||||
@@ -69,6 +94,22 @@ async def async_setup_entry(
|
||||
if DeviceType.is_light(device.type)
|
||||
]
|
||||
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SET_LIGHT_BRIGHTNESS_TRACKED_STATE,
|
||||
{
|
||||
vol.Required(ATTR_BRIGHTNESS): vol.All(
|
||||
vol.Number(scale=0), vol.Range(0, 255)
|
||||
)
|
||||
},
|
||||
"async_set_brightness_belief",
|
||||
)
|
||||
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SET_LIGHT_POWER_TRACKED_STATE,
|
||||
{vol.Required(ATTR_POWER_STATE): vol.All(cv.boolean)},
|
||||
"async_set_power_belief",
|
||||
)
|
||||
|
||||
async_add_entities(
|
||||
fan_lights + fan_up_lights + fan_down_lights + fireplaces + fp_lights + lights,
|
||||
)
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
"""Support for Bond services."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.fan import DOMAIN as FAN_DOMAIN
|
||||
from homeassistant.components.light import ATTR_BRIGHTNESS, DOMAIN as LIGHT_DOMAIN
|
||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_validation as cv, service
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
ATTR_POWER_STATE = "power_state"
|
||||
|
||||
# Fan
|
||||
SERVICE_SET_FAN_SPEED_TRACKED_STATE = "set_fan_speed_tracked_state"
|
||||
|
||||
# Switch
|
||||
SERVICE_SET_POWER_TRACKED_STATE = "set_switch_power_tracked_state"
|
||||
|
||||
# Light
|
||||
SERVICE_SET_LIGHT_POWER_TRACKED_STATE = "set_light_power_tracked_state"
|
||||
SERVICE_SET_LIGHT_BRIGHTNESS_TRACKED_STATE = "set_light_brightness_tracked_state"
|
||||
SERVICE_START_INCREASING_BRIGHTNESS = "start_increasing_brightness"
|
||||
SERVICE_START_DECREASING_BRIGHTNESS = "start_decreasing_brightness"
|
||||
SERVICE_STOP = "stop"
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_services(hass: HomeAssistant) -> None:
|
||||
"""Home Assistant services."""
|
||||
|
||||
# Fan entity services
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_SET_FAN_SPEED_TRACKED_STATE,
|
||||
entity_domain=FAN_DOMAIN,
|
||||
schema={vol.Required("speed"): vol.All(vol.Number(scale=0), vol.Range(0, 100))},
|
||||
func="async_set_speed_belief",
|
||||
)
|
||||
|
||||
# Light entity services
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_START_INCREASING_BRIGHTNESS,
|
||||
entity_domain=LIGHT_DOMAIN,
|
||||
schema=None,
|
||||
func="async_start_increasing_brightness",
|
||||
)
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_START_DECREASING_BRIGHTNESS,
|
||||
entity_domain=LIGHT_DOMAIN,
|
||||
schema=None,
|
||||
func="async_start_decreasing_brightness",
|
||||
)
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_STOP,
|
||||
entity_domain=LIGHT_DOMAIN,
|
||||
schema=None,
|
||||
func="async_stop",
|
||||
)
|
||||
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_SET_LIGHT_BRIGHTNESS_TRACKED_STATE,
|
||||
entity_domain=LIGHT_DOMAIN,
|
||||
schema={
|
||||
vol.Required(ATTR_BRIGHTNESS): vol.All(
|
||||
vol.Number(scale=0), vol.Range(0, 255)
|
||||
)
|
||||
},
|
||||
func="async_set_brightness_belief",
|
||||
)
|
||||
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_SET_LIGHT_POWER_TRACKED_STATE,
|
||||
entity_domain=LIGHT_DOMAIN,
|
||||
schema={vol.Required(ATTR_POWER_STATE): vol.All(cv.boolean)},
|
||||
func="async_set_power_belief",
|
||||
)
|
||||
|
||||
# Switch entity services
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_SET_POWER_TRACKED_STATE,
|
||||
entity_domain=SWITCH_DOMAIN,
|
||||
schema={vol.Required(ATTR_POWER_STATE): cv.boolean},
|
||||
func="async_set_power_belief",
|
||||
)
|
||||
@@ -6,13 +6,16 @@ from typing import Any
|
||||
|
||||
from aiohttp.client_exceptions import ClientResponseError
|
||||
from bond_async import Action, DeviceType
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import BondConfigEntry
|
||||
from .const import ATTR_POWER_STATE, SERVICE_SET_POWER_TRACKED_STATE
|
||||
from .entity import BondEntity
|
||||
|
||||
|
||||
@@ -23,6 +26,12 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Set up Bond generic devices."""
|
||||
data = entry.runtime_data
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SET_POWER_TRACKED_STATE,
|
||||
{vol.Required(ATTR_POWER_STATE): cv.boolean},
|
||||
"async_set_power_belief",
|
||||
)
|
||||
|
||||
async_add_entities(
|
||||
BondSwitch(data, device)
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
"""Constants for the Bring! integration."""
|
||||
|
||||
from typing import Final
|
||||
|
||||
DOMAIN = "bring"
|
||||
|
||||
ATTR_SENDER: Final = "sender"
|
||||
ATTR_ITEM_NAME: Final = "item"
|
||||
ATTR_NOTIFICATION_TYPE: Final = "message"
|
||||
ATTR_REACTION: Final = "reaction"
|
||||
ATTR_ACTIVITY: Final = "uuid"
|
||||
ATTR_RECEIVER: Final = "publicUserUuid"
|
||||
SERVICE_PUSH_NOTIFICATION = "send_message"
|
||||
SERVICE_ACTIVITY_STREAM_REACTION = "send_reaction"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Actions for Bring! integration."""
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from bring_api import (
|
||||
@@ -12,28 +13,22 @@ from bring_api import (
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.event import ATTR_EVENT_TYPE
|
||||
from homeassistant.components.todo import DOMAIN as TODO_DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
from homeassistant.helpers import (
|
||||
config_validation as cv,
|
||||
entity_registry as er,
|
||||
service,
|
||||
)
|
||||
from homeassistant.helpers import config_validation as cv, entity_registry as er
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import (
|
||||
ATTR_ACTIVITY,
|
||||
ATTR_REACTION,
|
||||
ATTR_RECEIVER,
|
||||
DOMAIN,
|
||||
SERVICE_ACTIVITY_STREAM_REACTION,
|
||||
)
|
||||
from .coordinator import BringConfigEntry
|
||||
|
||||
ATTR_ACTIVITY = "uuid"
|
||||
ATTR_ITEM_NAME = "item"
|
||||
ATTR_NOTIFICATION_TYPE = "message"
|
||||
ATTR_REACTION = "reaction"
|
||||
ATTR_RECEIVER = "publicUserUuid"
|
||||
|
||||
SERVICE_PUSH_NOTIFICATION = "send_message"
|
||||
SERVICE_ACTIVITY_STREAM_REACTION = "send_reaction"
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SERVICE_ACTIVITY_STREAM_REACTION_SCHEMA = vol.Schema(
|
||||
{
|
||||
@@ -59,7 +54,6 @@ def get_config_entry(hass: HomeAssistant, entry_id: str) -> BringConfigEntry:
|
||||
return entry
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_services(hass: HomeAssistant) -> None:
|
||||
"""Set up services for Bring! integration."""
|
||||
|
||||
@@ -114,17 +108,3 @@ def async_setup_services(hass: HomeAssistant) -> None:
|
||||
async_send_activity_stream_reaction,
|
||||
SERVICE_ACTIVITY_STREAM_REACTION_SCHEMA,
|
||||
)
|
||||
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_PUSH_NOTIFICATION,
|
||||
entity_domain=TODO_DOMAIN,
|
||||
schema={
|
||||
vol.Required(ATTR_NOTIFICATION_TYPE): vol.All(
|
||||
vol.Upper, vol.Coerce(BringNotificationType)
|
||||
),
|
||||
vol.Optional(ATTR_ITEM_NAME): cv.string,
|
||||
},
|
||||
func="async_send_message",
|
||||
)
|
||||
|
||||
@@ -13,6 +13,7 @@ from bring_api import (
|
||||
BringNotificationType,
|
||||
BringRequestException,
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.todo import (
|
||||
TodoItem,
|
||||
@@ -22,9 +23,15 @@ from homeassistant.components.todo import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import (
|
||||
ATTR_ITEM_NAME,
|
||||
ATTR_NOTIFICATION_TYPE,
|
||||
DOMAIN,
|
||||
SERVICE_PUSH_NOTIFICATION,
|
||||
)
|
||||
from .coordinator import BringConfigEntry, BringData, BringDataUpdateCoordinator
|
||||
from .entity import BringBaseEntity
|
||||
|
||||
@@ -56,6 +63,19 @@ async def async_setup_entry(
|
||||
coordinator.async_add_listener(add_entities)
|
||||
add_entities()
|
||||
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_PUSH_NOTIFICATION,
|
||||
{
|
||||
vol.Required(ATTR_NOTIFICATION_TYPE): vol.All(
|
||||
vol.Upper, vol.Coerce(BringNotificationType)
|
||||
),
|
||||
vol.Optional(ATTR_ITEM_NAME): cv.string,
|
||||
},
|
||||
"async_send_message",
|
||||
)
|
||||
|
||||
|
||||
class BringTodoListEntity(BringBaseEntity, TodoListEntity):
|
||||
"""A To-do List representation of the Bring! Shopping List."""
|
||||
|
||||
@@ -49,7 +49,6 @@ from .const import ( # noqa: F401
|
||||
ATTR_SWING_HORIZONTAL_MODES,
|
||||
ATTR_SWING_MODE,
|
||||
ATTR_SWING_MODES,
|
||||
ATTR_TARGET_HUMIDITY_STEP,
|
||||
ATTR_TARGET_TEMP_HIGH,
|
||||
ATTR_TARGET_TEMP_LOW,
|
||||
ATTR_TARGET_TEMP_STEP,
|
||||
@@ -235,7 +234,6 @@ CACHED_PROPERTIES_WITH_ATTR_ = {
|
||||
"max_temp",
|
||||
"min_humidity",
|
||||
"max_humidity",
|
||||
"target_humidity_step",
|
||||
}
|
||||
|
||||
|
||||
@@ -251,7 +249,6 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
ATTR_MAX_TEMP,
|
||||
ATTR_MIN_HUMIDITY,
|
||||
ATTR_MAX_HUMIDITY,
|
||||
ATTR_TARGET_HUMIDITY_STEP,
|
||||
ATTR_TARGET_TEMP_STEP,
|
||||
ATTR_PRESET_MODES,
|
||||
}
|
||||
@@ -278,7 +275,6 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
_attr_swing_horizontal_mode: str | None
|
||||
_attr_swing_horizontal_modes: list[str] | None
|
||||
_attr_target_humidity: float | None = None
|
||||
_attr_target_humidity_step: int | None = None
|
||||
_attr_target_temperature_high: float | None
|
||||
_attr_target_temperature_low: float | None
|
||||
_attr_target_temperature_step: float | None = None
|
||||
@@ -327,9 +323,6 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
data[ATTR_MIN_HUMIDITY] = self.min_humidity
|
||||
data[ATTR_MAX_HUMIDITY] = self.max_humidity
|
||||
|
||||
if self.target_humidity_step is not None:
|
||||
data[ATTR_TARGET_HUMIDITY_STEP] = self.target_humidity_step
|
||||
|
||||
if ClimateEntityFeature.FAN_MODE in supported_features:
|
||||
data[ATTR_FAN_MODES] = self.fan_modes
|
||||
|
||||
@@ -735,11 +728,6 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
"""Return the maximum humidity."""
|
||||
return self._attr_max_humidity
|
||||
|
||||
@cached_property
|
||||
def target_humidity_step(self) -> int | None:
|
||||
"""Return the supported step of humidity."""
|
||||
return self._attr_target_humidity_step
|
||||
|
||||
|
||||
async def async_service_humidity_set(
|
||||
entity: ClimateEntity, service_call: ServiceCall
|
||||
|
||||
@@ -114,7 +114,6 @@ ATTR_SWING_MODES = "swing_modes"
|
||||
ATTR_SWING_MODE = "swing_mode"
|
||||
ATTR_SWING_HORIZONTAL_MODE = "swing_horizontal_mode"
|
||||
ATTR_SWING_HORIZONTAL_MODES = "swing_horizontal_modes"
|
||||
ATTR_TARGET_HUMIDITY_STEP = "target_humidity_step"
|
||||
ATTR_TARGET_TEMP_HIGH = "target_temp_high"
|
||||
ATTR_TARGET_TEMP_LOW = "target_temp_low"
|
||||
ATTR_TARGET_TEMP_STEP = "target_temp_step"
|
||||
|
||||
@@ -21,7 +21,7 @@ async def fetch_latest_carbon_intensity(
|
||||
em: ElectricityMaps,
|
||||
config: Mapping[str, Any],
|
||||
) -> HomeAssistantCarbonIntensityResponse:
|
||||
"""Fetch the latest carbon intensity based on zone key or location coordinates."""
|
||||
"""Fetch the latest carbon intensity based on country code or location coordinates."""
|
||||
request: CoordinatesRequest | ZoneRequest = CoordinatesRequest(
|
||||
lat=config.get(CONF_LATITUDE, hass.config.latitude),
|
||||
lon=config.get(CONF_LONGITUDE, hass.config.longitude),
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
},
|
||||
"error": {
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"no_data": "No data is available for the location or zone you have selected.",
|
||||
"no_data": "No data is available for the location you have selected.",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"step": {
|
||||
@@ -17,20 +17,20 @@
|
||||
},
|
||||
"country": {
|
||||
"data": {
|
||||
"country_code": "Zone key"
|
||||
"country_code": "Country code"
|
||||
}
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||
"api_key": "[%key:common::config_flow::data::access_token%]"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]",
|
||||
"api_key": "[%key:common::config_flow::data::access_token%]",
|
||||
"location": "[%key:common::config_flow::data::location%]"
|
||||
},
|
||||
"description": "Visit the [Electricity Maps app]({register_link}) to request an API key."
|
||||
"description": "Visit the [Electricity Maps page]({register_link}) to request a token."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -40,7 +40,7 @@
|
||||
"name": "CO2 intensity",
|
||||
"state_attributes": {
|
||||
"country_code": {
|
||||
"name": "Zone key"
|
||||
"name": "Country code"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -58,7 +58,7 @@
|
||||
"location": {
|
||||
"options": {
|
||||
"specify_coordinates": "Specify coordinates",
|
||||
"specify_country_code": "Specify zone key",
|
||||
"specify_country_code": "Specify country code",
|
||||
"use_home_location": "Use home location"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,6 @@ async def async_setup_entry(
|
||||
target_temp_high=None,
|
||||
target_temp_low=None,
|
||||
hvac_modes=[cls for cls in HVACMode if cls != HVACMode.HEAT_COOL],
|
||||
target_humidity_step=5,
|
||||
),
|
||||
DemoClimate(
|
||||
unique_id="climate_3",
|
||||
@@ -119,7 +118,6 @@ class DemoClimate(ClimateEntity):
|
||||
target_temp_low: float | None,
|
||||
hvac_modes: list[HVACMode],
|
||||
preset_modes: list[str] | None = None,
|
||||
target_humidity_step: int | None = None,
|
||||
) -> None:
|
||||
"""Initialize the climate device."""
|
||||
self._unique_id = unique_id
|
||||
@@ -165,7 +163,6 @@ class DemoClimate(ClimateEntity):
|
||||
identifiers={(DOMAIN, unique_id)},
|
||||
name=device_name,
|
||||
)
|
||||
self._attr_target_humidity_step = target_humidity_step
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
|
||||
@@ -2,23 +2,12 @@
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import ElgatoConfigEntry, ElgatoDataUpdateCoordinator
|
||||
from .services import async_setup_services
|
||||
|
||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
PLATFORMS = [Platform.BUTTON, Platform.LIGHT, Platform.SENSOR, Platform.SWITCH]
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the component."""
|
||||
async_setup_services(hass)
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ElgatoConfigEntry) -> bool:
|
||||
"""Set up Elgato Light from a config entry."""
|
||||
coordinator = ElgatoDataUpdateCoordinator(hass, entry)
|
||||
|
||||
@@ -14,3 +14,6 @@ SCAN_INTERVAL = timedelta(seconds=10)
|
||||
|
||||
# Attributes
|
||||
ATTR_ON = "on"
|
||||
|
||||
# Services
|
||||
SERVICE_IDENTIFY = "identify"
|
||||
|
||||
@@ -15,9 +15,13 @@ from homeassistant.components.light import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.entity_platform import (
|
||||
AddConfigEntryEntitiesCallback,
|
||||
async_get_current_platform,
|
||||
)
|
||||
from homeassistant.util import color as color_util
|
||||
|
||||
from .const import SERVICE_IDENTIFY
|
||||
from .coordinator import ElgatoConfigEntry, ElgatoDataUpdateCoordinator
|
||||
from .entity import ElgatoEntity
|
||||
|
||||
@@ -33,6 +37,13 @@ async def async_setup_entry(
|
||||
coordinator = entry.runtime_data
|
||||
async_add_entities([ElgatoLight(coordinator)])
|
||||
|
||||
platform = async_get_current_platform()
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_IDENTIFY,
|
||||
None,
|
||||
ElgatoLight.async_identify.__name__,
|
||||
)
|
||||
|
||||
|
||||
class ElgatoLight(ElgatoEntity, LightEntity):
|
||||
"""Defines an Elgato Light."""
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
"""Support for Elgato services."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import service
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
SERVICE_IDENTIFY = "identify"
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_services(hass: HomeAssistant) -> None:
|
||||
"""Set up services."""
|
||||
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_IDENTIFY,
|
||||
entity_domain=LIGHT_DOMAIN,
|
||||
schema=None,
|
||||
func="async_identify",
|
||||
)
|
||||
@@ -21,5 +21,5 @@
|
||||
"integration_type": "system",
|
||||
"preview_features": { "winter_mode": {} },
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["home-assistant-frontend==20260128.4"]
|
||||
"requirements": ["home-assistant-frontend==20260128.3"]
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ class GPSLoggerEntity(TrackerEntity, RestoreEntity):
|
||||
)
|
||||
|
||||
@property
|
||||
def battery_level(self) -> int | None:
|
||||
def battery_level(self):
|
||||
"""Return battery value of the device."""
|
||||
return self._battery
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ from .const import (
|
||||
)
|
||||
from .coordinator import GrowattConfigEntry, GrowattCoordinator
|
||||
from .models import GrowattRuntimeData
|
||||
from .services import async_setup_services
|
||||
from .services import async_register_services
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -37,7 +37,7 @@ CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the Growatt Server component."""
|
||||
# Register services
|
||||
async_setup_services(hass)
|
||||
await async_register_services(hass)
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, SupportsResponse, callback
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, SupportsResponse
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
@@ -21,8 +21,7 @@ if TYPE_CHECKING:
|
||||
from .coordinator import GrowattCoordinator
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_services(hass: HomeAssistant) -> None:
|
||||
async def async_register_services(hass: HomeAssistant) -> None:
|
||||
"""Register services for Growatt Server integration."""
|
||||
|
||||
def get_min_coordinators() -> dict[str, GrowattCoordinator]:
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["pyhik"],
|
||||
"quality_scale": "legacy",
|
||||
"requirements": ["pyHik==0.4.2"]
|
||||
"requirements": ["pyHik==0.4.1"]
|
||||
}
|
||||
|
||||
@@ -115,6 +115,7 @@ SENSORS = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
native_unit_of_measurement=UnitOfVolume.MILLILITERS,
|
||||
device_class=SensorDeviceClass.VOLUME,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
translation_key="hot_water_counter",
|
||||
),
|
||||
HomeConnectSensorEntityDescription(
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
"""The Husqvarna Automower integration."""
|
||||
|
||||
import logging
|
||||
|
||||
from aioautomower.session import AutomowerSession
|
||||
from aiohttp import ClientResponseError
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import (
|
||||
aiohttp_client,
|
||||
config_entry_oauth2_flow,
|
||||
config_validation as cv,
|
||||
)
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.helpers import aiohttp_client, config_entry_oauth2_flow
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import api
|
||||
from .const import DOMAIN
|
||||
from .coordinator import AutomowerConfigEntry, AutomowerDataUpdateCoordinator
|
||||
from .services import async_setup_services
|
||||
|
||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORMS: list[Platform] = [
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.BUTTON,
|
||||
@@ -34,12 +30,6 @@ PLATFORMS: list[Platform] = [
|
||||
]
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the component."""
|
||||
async_setup_services(hass)
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: AutomowerConfigEntry) -> bool:
|
||||
"""Set up this integration using UI."""
|
||||
implementation = (
|
||||
|
||||
@@ -142,6 +142,3 @@ ERROR_KEYS = [
|
||||
"wrong_pin_code",
|
||||
"zone_generator_problem",
|
||||
]
|
||||
|
||||
MOW = "mow"
|
||||
PARK = "park"
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
"""Husqvarna Automower lawn mower entity."""
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from aioautomower.model import MowerActivities, MowerStates, WorkArea
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.lawn_mower import (
|
||||
LawnMowerActivity,
|
||||
@@ -12,13 +14,16 @@ from homeassistant.components.lawn_mower import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import AutomowerConfigEntry
|
||||
from .const import DOMAIN, ERROR_STATES, MOW, PARK
|
||||
from .const import DOMAIN, ERROR_STATES
|
||||
from .coordinator import AutomowerDataUpdateCoordinator
|
||||
from .entity import AutomowerBaseEntity, handle_sending_exception
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
|
||||
DOCKED_ACTIVITIES = (MowerActivities.PARKED_IN_CS, MowerActivities.CHARGING)
|
||||
@@ -36,6 +41,9 @@ SUPPORT_STATE_SERVICES = (
|
||||
| LawnMowerEntityFeature.PAUSE
|
||||
| LawnMowerEntityFeature.START_MOWING
|
||||
)
|
||||
MOW = "mow"
|
||||
PARK = "park"
|
||||
OVERRIDE_MODES = [MOW, PARK]
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -54,6 +62,31 @@ async def async_setup_entry(
|
||||
_async_add_new_devices(set(coordinator.data))
|
||||
|
||||
coordinator.new_devices_callbacks.append(_async_add_new_devices)
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
platform.async_register_entity_service(
|
||||
"override_schedule",
|
||||
{
|
||||
vol.Required("override_mode"): vol.In(OVERRIDE_MODES),
|
||||
vol.Required("duration"): vol.All(
|
||||
cv.time_period,
|
||||
cv.positive_timedelta,
|
||||
vol.Range(min=timedelta(minutes=1), max=timedelta(days=42)),
|
||||
),
|
||||
},
|
||||
"async_override_schedule",
|
||||
)
|
||||
platform.async_register_entity_service(
|
||||
"override_schedule_work_area",
|
||||
{
|
||||
vol.Required("work_area_id"): vol.Coerce(int),
|
||||
vol.Required("duration"): vol.All(
|
||||
cv.time_period,
|
||||
cv.positive_timedelta,
|
||||
vol.Range(min=timedelta(minutes=1), max=timedelta(days=42)),
|
||||
),
|
||||
},
|
||||
"async_override_schedule_work_area",
|
||||
)
|
||||
|
||||
|
||||
class AutomowerLawnMowerEntity(AutomowerBaseEntity, LawnMowerEntity):
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
"""Husqvarna Automower services."""
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.lawn_mower import DOMAIN as LAWN_MOWER_DOMAIN
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_validation as cv, service
|
||||
|
||||
from .const import DOMAIN, MOW, PARK
|
||||
|
||||
OVERRIDE_MODES = [MOW, PARK]
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_services(hass: HomeAssistant) -> None:
|
||||
"""Set up services."""
|
||||
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"override_schedule",
|
||||
entity_domain=LAWN_MOWER_DOMAIN,
|
||||
schema={
|
||||
vol.Required("override_mode"): vol.In(OVERRIDE_MODES),
|
||||
vol.Required("duration"): vol.All(
|
||||
cv.time_period,
|
||||
cv.positive_timedelta,
|
||||
vol.Range(min=timedelta(minutes=1), max=timedelta(days=42)),
|
||||
),
|
||||
},
|
||||
func="async_override_schedule",
|
||||
)
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"override_schedule_work_area",
|
||||
entity_domain=LAWN_MOWER_DOMAIN,
|
||||
schema={
|
||||
vol.Required("work_area_id"): vol.Coerce(int),
|
||||
vol.Required("duration"): vol.All(
|
||||
cv.time_period,
|
||||
cv.positive_timedelta,
|
||||
vol.Range(min=timedelta(minutes=1), max=timedelta(days=42)),
|
||||
),
|
||||
},
|
||||
func="async_override_schedule_work_area",
|
||||
)
|
||||
@@ -17,7 +17,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .coordinator import LiebherrConfigEntry, LiebherrCoordinator
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.NUMBER, Platform.SENSOR]
|
||||
PLATFORMS: list[Platform] = [Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: LiebherrConfigEntry) -> bool:
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
"""Number platform for Liebherr integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
|
||||
from pyliebherrhomeapi import (
|
||||
LiebherrConnectionError,
|
||||
LiebherrTimeoutError,
|
||||
TemperatureControl,
|
||||
TemperatureUnit,
|
||||
)
|
||||
|
||||
from homeassistant.components.number import (
|
||||
DEFAULT_MAX_VALUE,
|
||||
DEFAULT_MIN_VALUE,
|
||||
NumberDeviceClass,
|
||||
NumberEntity,
|
||||
NumberEntityDescription,
|
||||
)
|
||||
from homeassistant.const import UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import LiebherrConfigEntry, LiebherrCoordinator
|
||||
from .entity import LiebherrZoneEntity
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class LiebherrNumberEntityDescription(NumberEntityDescription):
|
||||
"""Describes Liebherr number entity."""
|
||||
|
||||
value_fn: Callable[[TemperatureControl], float | None]
|
||||
min_fn: Callable[[TemperatureControl], float | None]
|
||||
max_fn: Callable[[TemperatureControl], float | None]
|
||||
unit_fn: Callable[[TemperatureControl], str]
|
||||
|
||||
|
||||
NUMBER_TYPES: tuple[LiebherrNumberEntityDescription, ...] = (
|
||||
LiebherrNumberEntityDescription(
|
||||
key="setpoint_temperature",
|
||||
translation_key="setpoint_temperature",
|
||||
device_class=NumberDeviceClass.TEMPERATURE,
|
||||
native_step=1,
|
||||
value_fn=lambda control: control.target,
|
||||
min_fn=lambda control: control.min,
|
||||
max_fn=lambda control: control.max,
|
||||
unit_fn=lambda control: (
|
||||
UnitOfTemperature.FAHRENHEIT
|
||||
if control.unit == TemperatureUnit.FAHRENHEIT
|
||||
else UnitOfTemperature.CELSIUS
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: LiebherrConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Liebherr number entities."""
|
||||
coordinators = entry.runtime_data
|
||||
async_add_entities(
|
||||
LiebherrNumber(
|
||||
coordinator=coordinator,
|
||||
zone_id=temp_control.zone_id,
|
||||
description=description,
|
||||
)
|
||||
for coordinator in coordinators.values()
|
||||
for temp_control in coordinator.data.get_temperature_controls().values()
|
||||
for description in NUMBER_TYPES
|
||||
)
|
||||
|
||||
|
||||
class LiebherrNumber(LiebherrZoneEntity, NumberEntity):
|
||||
"""Representation of a Liebherr number entity."""
|
||||
|
||||
entity_description: LiebherrNumberEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: LiebherrCoordinator,
|
||||
zone_id: int,
|
||||
description: LiebherrNumberEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the number entity."""
|
||||
super().__init__(coordinator, zone_id)
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{coordinator.device_id}_{description.key}_{zone_id}"
|
||||
|
||||
# If device has only one zone, use translation key without zone suffix
|
||||
temp_controls = coordinator.data.get_temperature_controls()
|
||||
if len(temp_controls) > 1 and (zone_key := self._get_zone_translation_key()):
|
||||
self._attr_translation_key = f"{description.translation_key}_{zone_key}"
|
||||
|
||||
@property
|
||||
def native_unit_of_measurement(self) -> str | None:
|
||||
"""Return the unit of measurement."""
|
||||
if (temp_control := self.temperature_control) is None:
|
||||
return None
|
||||
return self.entity_description.unit_fn(temp_control)
|
||||
|
||||
@property
|
||||
def native_value(self) -> float | None:
|
||||
"""Return the current value."""
|
||||
# temperature_control is guaranteed to exist when entity is available
|
||||
return self.entity_description.value_fn(
|
||||
self.temperature_control # type: ignore[arg-type]
|
||||
)
|
||||
|
||||
@property
|
||||
def native_min_value(self) -> float:
|
||||
"""Return the minimum value."""
|
||||
if (temp_control := self.temperature_control) is None:
|
||||
return DEFAULT_MIN_VALUE
|
||||
if (min_val := self.entity_description.min_fn(temp_control)) is None:
|
||||
return DEFAULT_MIN_VALUE
|
||||
return min_val
|
||||
|
||||
@property
|
||||
def native_max_value(self) -> float:
|
||||
"""Return the maximum value."""
|
||||
if (temp_control := self.temperature_control) is None:
|
||||
return DEFAULT_MAX_VALUE
|
||||
if (max_val := self.entity_description.max_fn(temp_control)) is None:
|
||||
return DEFAULT_MAX_VALUE
|
||||
return max_val
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if entity is available."""
|
||||
return super().available and self.temperature_control is not None
|
||||
|
||||
async def async_set_native_value(self, value: float) -> None:
|
||||
"""Set new value."""
|
||||
# temperature_control is guaranteed to exist when entity is available
|
||||
temp_control = self.temperature_control
|
||||
|
||||
unit = (
|
||||
TemperatureUnit.FAHRENHEIT
|
||||
if temp_control.unit == TemperatureUnit.FAHRENHEIT # type: ignore[union-attr]
|
||||
else TemperatureUnit.CELSIUS
|
||||
)
|
||||
|
||||
try:
|
||||
await self.coordinator.client.set_temperature(
|
||||
device_id=self.coordinator.device_id,
|
||||
zone_id=self._zone_id,
|
||||
target=int(value),
|
||||
unit=unit,
|
||||
)
|
||||
except (LiebherrConnectionError, LiebherrTimeoutError) as err:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="set_temperature_failed",
|
||||
) from err
|
||||
|
||||
await self.coordinator.async_request_refresh()
|
||||
@@ -55,16 +55,23 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Set up Liebherr sensor entities."""
|
||||
coordinators = entry.runtime_data
|
||||
async_add_entities(
|
||||
LiebherrSensor(
|
||||
coordinator=coordinator,
|
||||
zone_id=temp_control.zone_id,
|
||||
description=description,
|
||||
)
|
||||
for coordinator in coordinators.values()
|
||||
for temp_control in coordinator.data.get_temperature_controls().values()
|
||||
for description in SENSOR_TYPES
|
||||
)
|
||||
entities: list[LiebherrSensor] = []
|
||||
|
||||
for coordinator in coordinators.values():
|
||||
# Get all temperature controls for this device
|
||||
temp_controls = coordinator.data.get_temperature_controls()
|
||||
|
||||
for temp_control in temp_controls.values():
|
||||
entities.extend(
|
||||
LiebherrSensor(
|
||||
coordinator=coordinator,
|
||||
zone_id=temp_control.zone_id,
|
||||
description=description,
|
||||
)
|
||||
for description in SENSOR_TYPES
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class LiebherrSensor(LiebherrZoneEntity, SensorEntity):
|
||||
@@ -101,9 +108,9 @@ class LiebherrSensor(LiebherrZoneEntity, SensorEntity):
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the current value."""
|
||||
# temperature_control is guaranteed to exist when entity is available
|
||||
assert self.temperature_control is not None
|
||||
return self.entity_description.value_fn(self.temperature_control)
|
||||
if (temp_control := self.temperature_control) is None:
|
||||
return None
|
||||
return self.entity_description.value_fn(temp_control)
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
|
||||
@@ -33,20 +33,6 @@
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"number": {
|
||||
"setpoint_temperature": {
|
||||
"name": "Setpoint"
|
||||
},
|
||||
"setpoint_temperature_bottom_zone": {
|
||||
"name": "Bottom zone setpoint"
|
||||
},
|
||||
"setpoint_temperature_middle_zone": {
|
||||
"name": "Middle zone setpoint"
|
||||
},
|
||||
"setpoint_temperature_top_zone": {
|
||||
"name": "Top zone setpoint"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"bottom_zone": {
|
||||
"name": "Bottom zone"
|
||||
@@ -58,10 +44,5 @@
|
||||
"name": "Top zone"
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"set_temperature_failed": {
|
||||
"message": "Failed to set temperature"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,11 @@ import itertools
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.device_registry import DeviceEntry
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import LitterRobotConfigEntry, LitterRobotDataUpdateCoordinator
|
||||
from .services import async_setup_services
|
||||
|
||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
PLATFORMS = [
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.BUTTON,
|
||||
@@ -27,12 +23,6 @@ PLATFORMS = [
|
||||
]
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the component."""
|
||||
async_setup_services(hass)
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: LitterRobotConfigEntry) -> bool:
|
||||
"""Set up Litter-Robot from a config entry."""
|
||||
coordinator = LitterRobotDataUpdateCoordinator(hass, entry)
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
"""Litter-Robot services."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.vacuum import DOMAIN as VACUUM_DOMAIN
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_validation as cv, service
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
SERVICE_SET_SLEEP_MODE = "set_sleep_mode"
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_services(hass: HomeAssistant) -> None:
|
||||
"""Set up services."""
|
||||
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_SET_SLEEP_MODE,
|
||||
entity_domain=VACUUM_DOMAIN,
|
||||
schema={
|
||||
vol.Required("enabled"): cv.boolean,
|
||||
vol.Optional("start_time"): cv.time,
|
||||
},
|
||||
func="async_set_sleep_mode",
|
||||
)
|
||||
@@ -7,6 +7,7 @@ from typing import Any
|
||||
|
||||
from pylitterbot import LitterRobot
|
||||
from pylitterbot.enums import LitterBoxStatus
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.vacuum import (
|
||||
StateVacuumEntity,
|
||||
@@ -15,12 +16,15 @@ from homeassistant.components.vacuum import (
|
||||
VacuumEntityFeature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .coordinator import LitterRobotConfigEntry
|
||||
from .entity import LitterRobotEntity
|
||||
|
||||
SERVICE_SET_SLEEP_MODE = "set_sleep_mode"
|
||||
|
||||
LITTER_BOX_STATUS_STATE_MAP = {
|
||||
LitterBoxStatus.CLEAN_CYCLE: VacuumActivity.CLEANING,
|
||||
LitterBoxStatus.EMPTY_CYCLE: VacuumActivity.CLEANING,
|
||||
@@ -53,6 +57,16 @@ async def async_setup_entry(
|
||||
for robot in coordinator.litter_robots()
|
||||
)
|
||||
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SET_SLEEP_MODE,
|
||||
{
|
||||
vol.Required("enabled"): cv.boolean,
|
||||
vol.Optional("start_time"): cv.time,
|
||||
},
|
||||
"async_set_sleep_mode",
|
||||
)
|
||||
|
||||
|
||||
class LitterRobotCleaner(LitterRobotEntity[LitterRobot], StateVacuumEntity):
|
||||
"""Litter-Robot "Vacuum" Cleaner."""
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from speak2mary import MaryTTS
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -11,7 +9,6 @@ from homeassistant.components.tts import (
|
||||
CONF_LANG,
|
||||
PLATFORM_SCHEMA as TTS_PLATFORM_SCHEMA,
|
||||
Provider,
|
||||
TtsAudioType,
|
||||
)
|
||||
from homeassistant.const import CONF_EFFECT, CONF_HOST, CONF_PORT
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
@@ -69,28 +66,26 @@ class MaryTTSProvider(Provider):
|
||||
self.name = "MaryTTS"
|
||||
|
||||
@property
|
||||
def default_language(self) -> str:
|
||||
def default_language(self):
|
||||
"""Return the default language."""
|
||||
return self._mary.locale
|
||||
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
def supported_languages(self):
|
||||
"""Return list of supported languages."""
|
||||
return SUPPORT_LANGUAGES
|
||||
|
||||
@property
|
||||
def default_options(self) -> dict[str, Any]:
|
||||
def default_options(self):
|
||||
"""Return dict include default options."""
|
||||
return {CONF_EFFECT: self._effects}
|
||||
|
||||
@property
|
||||
def supported_options(self) -> list[str]:
|
||||
def supported_options(self):
|
||||
"""Return a list of supported options."""
|
||||
return SUPPORT_OPTIONS
|
||||
|
||||
def get_tts_audio(
|
||||
self, message: str, language: str, options: dict[str, Any]
|
||||
) -> TtsAudioType:
|
||||
def get_tts_audio(self, message, language, options):
|
||||
"""Load TTS from MaryTTS."""
|
||||
effects = options[CONF_EFFECT]
|
||||
|
||||
|
||||
@@ -1014,6 +1014,7 @@ DISCOVERY_SCHEMAS = [
|
||||
device_to_ha=lambda x: x.energy,
|
||||
),
|
||||
entity_class=MatterSensor,
|
||||
allow_none_value=True,
|
||||
required_attributes=(
|
||||
clusters.ElectricalEnergyMeasurement.Attributes.CumulativeEnergyImported,
|
||||
),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""Support for the Microsoft Cognitive Services text-to-speech service."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from pycsspeechtts import pycsspeechtts
|
||||
from requests.exceptions import HTTPError
|
||||
@@ -11,7 +10,6 @@ from homeassistant.components.tts import (
|
||||
CONF_LANG,
|
||||
PLATFORM_SCHEMA as TTS_PLATFORM_SCHEMA,
|
||||
Provider,
|
||||
TtsAudioType,
|
||||
)
|
||||
from homeassistant.const import CONF_API_KEY, CONF_REGION, CONF_TYPE, PERCENTAGE
|
||||
from homeassistant.generated.microsoft_tts import SUPPORTED_LANGUAGES
|
||||
@@ -91,28 +89,26 @@ class MicrosoftProvider(Provider):
|
||||
self.name = "Microsoft"
|
||||
|
||||
@property
|
||||
def default_language(self) -> str:
|
||||
def default_language(self):
|
||||
"""Return the default language."""
|
||||
return self._lang
|
||||
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
def supported_languages(self):
|
||||
"""Return list of supported languages."""
|
||||
return list(SUPPORTED_LANGUAGES)
|
||||
return SUPPORTED_LANGUAGES
|
||||
|
||||
@property
|
||||
def supported_options(self) -> list[str]:
|
||||
def supported_options(self):
|
||||
"""Return list of supported options like voice, emotion."""
|
||||
return [CONF_GENDER, CONF_TYPE]
|
||||
|
||||
@property
|
||||
def default_options(self) -> dict[str, Any]:
|
||||
def default_options(self):
|
||||
"""Return a dict include default options."""
|
||||
return {CONF_GENDER: self._gender, CONF_TYPE: self._type}
|
||||
|
||||
def get_tts_audio(
|
||||
self, message: str, language: str, options: dict[str, Any]
|
||||
) -> TtsAudioType:
|
||||
def get_tts_audio(self, message, language, options):
|
||||
"""Load TTS from Microsoft."""
|
||||
if language is None:
|
||||
language = self._lang
|
||||
|
||||
@@ -5,7 +5,6 @@ import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -13,7 +12,6 @@ from homeassistant.components.tts import (
|
||||
CONF_LANG,
|
||||
PLATFORM_SCHEMA as TTS_PLATFORM_SCHEMA,
|
||||
Provider,
|
||||
TtsAudioType,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -44,18 +42,16 @@ class PicoProvider(Provider):
|
||||
self.name = "PicoTTS"
|
||||
|
||||
@property
|
||||
def default_language(self) -> str:
|
||||
def default_language(self):
|
||||
"""Return the default language."""
|
||||
return self._lang
|
||||
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
def supported_languages(self):
|
||||
"""Return list of supported languages."""
|
||||
return SUPPORT_LANGUAGES
|
||||
|
||||
def get_tts_audio(
|
||||
self, message: str, language: str, options: dict[str, Any]
|
||||
) -> TtsAudioType:
|
||||
def get_tts_audio(self, message, language, options):
|
||||
"""Load TTS using pico2wave."""
|
||||
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmpf:
|
||||
fname = tmpf.name
|
||||
|
||||
@@ -18,26 +18,19 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import (
|
||||
config_validation as cv,
|
||||
device_registry as dr,
|
||||
entity_registry as er,
|
||||
)
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.helpers.device_registry import format_mac
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import CONF_SERIAL_NUMBER, DOMAIN
|
||||
from .const import CONF_SERIAL_NUMBER
|
||||
from .coordinator import (
|
||||
RainbirdScheduleUpdateCoordinator,
|
||||
RainbirdUpdateCoordinator,
|
||||
async_create_clientsession,
|
||||
)
|
||||
from .services import async_setup_services
|
||||
from .types import RainbirdConfigEntry, RainbirdData
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
PLATFORMS = [
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.CALENDAR,
|
||||
@@ -47,6 +40,9 @@ PLATFORMS = [
|
||||
]
|
||||
|
||||
|
||||
DOMAIN = "rainbird"
|
||||
|
||||
|
||||
def _async_register_clientsession_shutdown(
|
||||
hass: HomeAssistant,
|
||||
entry: RainbirdConfigEntry,
|
||||
@@ -65,12 +61,6 @@ def _async_register_clientsession_shutdown(
|
||||
entry.async_on_unload(_async_close_websession)
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the component."""
|
||||
async_setup_services(hass)
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: RainbirdConfigEntry) -> bool:
|
||||
"""Set up the config entry for Rain Bird."""
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
"""Rain Bird Irrigation system services."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_validation as cv, service
|
||||
from homeassistant.helpers.typing import VolDictType
|
||||
|
||||
from .const import ATTR_DURATION, DOMAIN
|
||||
|
||||
SERVICE_START_IRRIGATION = "start_irrigation"
|
||||
|
||||
SERVICE_SCHEMA_IRRIGATION: VolDictType = {
|
||||
vol.Required(ATTR_DURATION): cv.positive_float,
|
||||
}
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_services(hass: HomeAssistant) -> None:
|
||||
"""Set up services."""
|
||||
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_START_IRRIGATION,
|
||||
entity_domain=SWITCH_DOMAIN,
|
||||
schema=SERVICE_SCHEMA_IRRIGATION,
|
||||
func="async_turn_on",
|
||||
)
|
||||
@@ -6,12 +6,15 @@ import logging
|
||||
from typing import Any
|
||||
|
||||
from pyrainbird.exceptions import RainbirdApiException, RainbirdDeviceBusyException
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.typing import VolDictType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import ATTR_DURATION, CONF_IMPORTED_NAMES, DOMAIN, MANUFACTURER
|
||||
@@ -20,6 +23,12 @@ from .types import RainbirdConfigEntry
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SERVICE_START_IRRIGATION = "start_irrigation"
|
||||
|
||||
SERVICE_SCHEMA_IRRIGATION: VolDictType = {
|
||||
vol.Required(ATTR_DURATION): cv.positive_float,
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
@@ -38,6 +47,13 @@ async def async_setup_entry(
|
||||
for zone in coordinator.data.zones
|
||||
)
|
||||
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_START_IRRIGATION,
|
||||
SERVICE_SCHEMA_IRRIGATION,
|
||||
"async_turn_on",
|
||||
)
|
||||
|
||||
|
||||
class RainBirdSwitch(CoordinatorEntity[RainbirdUpdateCoordinator], SwitchEntity):
|
||||
"""Representation of a Rain Bird switch."""
|
||||
|
||||
@@ -7,17 +7,22 @@ from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from reolink_aio.api import GuardEnum, Host, PtzEnum
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.button import (
|
||||
ButtonDeviceClass,
|
||||
ButtonEntity,
|
||||
ButtonEntityDescription,
|
||||
)
|
||||
from homeassistant.components.camera import CameraEntityFeature
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.entity_platform import (
|
||||
AddConfigEntryEntitiesCallback,
|
||||
async_get_current_platform,
|
||||
)
|
||||
|
||||
from .const import SUPPORT_PTZ_SPEED
|
||||
from .entity import (
|
||||
ReolinkChannelCoordinatorEntity,
|
||||
ReolinkChannelEntityDescription,
|
||||
@@ -27,6 +32,9 @@ from .entity import (
|
||||
from .util import ReolinkConfigEntry, ReolinkData, raise_translated_error
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
ATTR_SPEED = "speed"
|
||||
SUPPORT_PTZ_SPEED = CameraEntityFeature.STREAM
|
||||
SERVICE_PTZ_MOVE = "ptz_move"
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@@ -170,6 +178,14 @@ async def async_setup_entry(
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
platform = async_get_current_platform()
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_PTZ_MOVE,
|
||||
{vol.Required(ATTR_SPEED): cv.positive_int},
|
||||
"async_ptz_move",
|
||||
[SUPPORT_PTZ_SPEED],
|
||||
)
|
||||
|
||||
|
||||
class ReolinkButtonEntity(ReolinkChannelCoordinatorEntity, ButtonEntity):
|
||||
"""Base button entity class for Reolink IP cameras."""
|
||||
@@ -203,8 +219,9 @@ class ReolinkButtonEntity(ReolinkChannelCoordinatorEntity, ButtonEntity):
|
||||
await self.entity_description.method(self._host.api, self._channel)
|
||||
|
||||
@raise_translated_error
|
||||
async def async_ptz_move(self, *, speed: int) -> None:
|
||||
async def async_ptz_move(self, **kwargs: Any) -> None:
|
||||
"""PTZ move with speed."""
|
||||
speed = kwargs[ATTR_SPEED]
|
||||
await self._host.api.set_ptz_command(
|
||||
self._channel, command=self.entity_description.ptz_cmd, speed=speed
|
||||
)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"""Constants for the Reolink Camera integration."""
|
||||
|
||||
from homeassistant.components.camera import CameraEntityFeature
|
||||
|
||||
DOMAIN = "reolink"
|
||||
|
||||
CONF_USE_HTTPS = "use_https"
|
||||
@@ -15,5 +13,3 @@ CONF_FIRMWARE_CHECK_TIME = "firmware_check_time"
|
||||
BATTERY_PASSIVE_WAKE_UPDATE_INTERVAL = 3600 # seconds
|
||||
BATTERY_WAKE_UPDATE_INTERVAL = 6 * BATTERY_PASSIVE_WAKE_UPDATE_INTERVAL
|
||||
BATTERY_ALL_WAKE_UPDATE_INTERVAL = 2 * BATTERY_WAKE_UPDATE_INTERVAL
|
||||
|
||||
SUPPORT_PTZ_SPEED = CameraEntityFeature.STREAM
|
||||
|
||||
@@ -6,24 +6,17 @@ from reolink_aio.api import Chime
|
||||
from reolink_aio.enums import ChimeToneEnum
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import ATTR_DEVICE_ID
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import (
|
||||
config_validation as cv,
|
||||
device_registry as dr,
|
||||
service,
|
||||
)
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
from .const import DOMAIN, SUPPORT_PTZ_SPEED
|
||||
from .const import DOMAIN
|
||||
from .host import ReolinkHost
|
||||
from .util import get_device_uid_and_ch, raise_translated_error
|
||||
|
||||
ATTR_RINGTONE = "ringtone"
|
||||
ATTR_SPEED = "speed"
|
||||
SERVICE_PTZ_MOVE = "ptz_move"
|
||||
|
||||
|
||||
@raise_translated_error
|
||||
@@ -83,12 +76,3 @@ def async_setup_services(hass: HomeAssistant) -> None:
|
||||
}
|
||||
),
|
||||
)
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_PTZ_MOVE,
|
||||
entity_domain=BUTTON_DOMAIN,
|
||||
schema={vol.Required(ATTR_SPEED): cv.positive_int},
|
||||
func="async_ptz_move",
|
||||
required_features=[SUPPORT_PTZ_SPEED],
|
||||
)
|
||||
|
||||
@@ -23,9 +23,8 @@ from roborock.mqtt.session import MqttSessionUnauthorized
|
||||
from homeassistant.const import CONF_USERNAME, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import Event, HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import config_validation as cv, device_registry as dr
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import (
|
||||
CONF_BASE_URL,
|
||||
@@ -48,20 +47,12 @@ from .coordinator import (
|
||||
RoborockWetDryVacUpdateCoordinator,
|
||||
)
|
||||
from .roborock_storage import CacheStore, async_cleanup_map_storage
|
||||
from .services import async_setup_services
|
||||
|
||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the component."""
|
||||
async_setup_services(hass)
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: RoborockConfigEntry) -> bool:
|
||||
"""Set up roborock from a config entry."""
|
||||
await async_cleanup_map_storage(hass, entry.entry_id)
|
||||
|
||||
@@ -52,10 +52,12 @@ IMAGE_CACHE_INTERVAL = timedelta(seconds=30)
|
||||
|
||||
MAP_SLEEP = 3
|
||||
|
||||
|
||||
GET_MAPS_SERVICE_NAME = "get_maps"
|
||||
MAP_SCALE = 4
|
||||
MAP_FILE_FORMAT = "PNG"
|
||||
MAP_FILENAME_SUFFIX = ".png"
|
||||
SET_VACUUM_GOTO_POSITION_SERVICE_NAME = "set_vacuum_goto_position"
|
||||
GET_VACUUM_CURRENT_POSITION_SERVICE_NAME = "get_vacuum_current_position"
|
||||
|
||||
|
||||
A01_UPDATE_INTERVAL = timedelta(minutes=1)
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
"""Roborock services."""
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.vacuum import DOMAIN as VACUUM_DOMAIN
|
||||
from homeassistant.core import HomeAssistant, SupportsResponse, callback
|
||||
from homeassistant.helpers import config_validation as cv, service
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
GET_MAPS_SERVICE_NAME = "get_maps"
|
||||
SET_VACUUM_GOTO_POSITION_SERVICE_NAME = "set_vacuum_goto_position"
|
||||
GET_VACUUM_CURRENT_POSITION_SERVICE_NAME = "get_vacuum_current_position"
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_services(hass: HomeAssistant) -> None:
|
||||
"""Set up services."""
|
||||
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
GET_MAPS_SERVICE_NAME,
|
||||
entity_domain=VACUUM_DOMAIN,
|
||||
schema=None,
|
||||
func="get_maps",
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
GET_VACUUM_CURRENT_POSITION_SERVICE_NAME,
|
||||
entity_domain=VACUUM_DOMAIN,
|
||||
schema=None,
|
||||
func="get_vacuum_current_position",
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SET_VACUUM_GOTO_POSITION_SERVICE_NAME,
|
||||
entity_domain=VACUUM_DOMAIN,
|
||||
schema=cv.make_entity_service_schema(
|
||||
{
|
||||
vol.Required("x"): vol.Coerce(int),
|
||||
vol.Required("y"): vol.Coerce(int),
|
||||
},
|
||||
),
|
||||
func="async_set_vacuum_goto_position",
|
||||
supports_response=SupportsResponse.NONE,
|
||||
)
|
||||
@@ -6,17 +6,24 @@ from typing import Any
|
||||
from roborock.data import RoborockStateCode, SCWindMapping, WorkStatusMapping
|
||||
from roborock.exceptions import RoborockException
|
||||
from roborock.roborock_typing import RoborockCommand
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.vacuum import (
|
||||
StateVacuumEntity,
|
||||
VacuumActivity,
|
||||
VacuumEntityFeature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, ServiceResponse
|
||||
from homeassistant.core import HomeAssistant, ServiceResponse, SupportsResponse
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
GET_MAPS_SERVICE_NAME,
|
||||
GET_VACUUM_CURRENT_POSITION_SERVICE_NAME,
|
||||
SET_VACUUM_GOTO_POSITION_SERVICE_NAME,
|
||||
)
|
||||
from .coordinator import (
|
||||
RoborockB01Q7UpdateCoordinator,
|
||||
RoborockConfigEntry,
|
||||
@@ -85,6 +92,33 @@ async def async_setup_entry(
|
||||
for coordinator in config_entry.runtime_data.b01
|
||||
if isinstance(coordinator, RoborockB01Q7UpdateCoordinator)
|
||||
)
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
|
||||
platform.async_register_entity_service(
|
||||
GET_MAPS_SERVICE_NAME,
|
||||
None,
|
||||
RoborockVacuum.get_maps.__name__,
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
|
||||
platform.async_register_entity_service(
|
||||
GET_VACUUM_CURRENT_POSITION_SERVICE_NAME,
|
||||
None,
|
||||
RoborockVacuum.get_vacuum_current_position.__name__,
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
|
||||
platform.async_register_entity_service(
|
||||
SET_VACUUM_GOTO_POSITION_SERVICE_NAME,
|
||||
cv.make_entity_service_schema(
|
||||
{
|
||||
vol.Required("x"): vol.Coerce(int),
|
||||
vol.Required("y"): vol.Coerce(int),
|
||||
},
|
||||
),
|
||||
RoborockVacuum.async_set_vacuum_goto_position.__name__,
|
||||
supports_response=SupportsResponse.NONE,
|
||||
)
|
||||
|
||||
|
||||
class RoborockVacuum(RoborockCoordinatedEntityV1, StateVacuumEntity):
|
||||
|
||||
@@ -14,7 +14,7 @@ from homeassistant.components.sensor import (
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.const import UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
@@ -81,12 +81,6 @@ class SENZSensor(CoordinatorEntity[SENZDataUpdateCoordinator], SensorEntity):
|
||||
serial_number=thermostat.serial_number,
|
||||
)
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle updated data from the coordinator."""
|
||||
self._thermostat = self.coordinator.data[self._thermostat.serial_number]
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if the thermostat is available."""
|
||||
|
||||
@@ -41,7 +41,7 @@ from .coordinator import (
|
||||
TeslemetryEnergySiteLiveCoordinator,
|
||||
TeslemetryVehicleDataCoordinator,
|
||||
)
|
||||
from .helpers import async_update_device_sw_version, flatten
|
||||
from .helpers import flatten
|
||||
from .models import TeslemetryData, TeslemetryEnergyData, TeslemetryVehicleData
|
||||
from .services import async_setup_services
|
||||
|
||||
@@ -161,16 +161,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) -
|
||||
coordinator = TeslemetryVehicleDataCoordinator(
|
||||
hass, entry, vehicle, product
|
||||
)
|
||||
firmware = vehicle_metadata[vin].get("firmware")
|
||||
device = DeviceInfo(
|
||||
identifiers={(DOMAIN, vin)},
|
||||
manufacturer="Tesla",
|
||||
configuration_url="https://teslemetry.com/console",
|
||||
name=product["display_name"],
|
||||
model=vehicle.model,
|
||||
model_id=vin[3],
|
||||
serial_number=vin,
|
||||
sw_version=firmware,
|
||||
)
|
||||
current_devices.add((DOMAIN, vin))
|
||||
|
||||
@@ -188,6 +185,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) -
|
||||
create_handle_vehicle_stream(vin, coordinator),
|
||||
{"vin": vin},
|
||||
)
|
||||
firmware = vehicle_metadata[vin].get("firmware", "Unknown")
|
||||
stream_vehicle = stream.get_vehicle(vin)
|
||||
poll = vehicle_metadata[vin].get("polling", False)
|
||||
|
||||
@@ -278,20 +276,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) -
|
||||
),
|
||||
)
|
||||
|
||||
# Register listeners for polling vehicle sw_version updates
|
||||
for vehicle_data in vehicles:
|
||||
if vehicle_data.poll:
|
||||
entry.async_on_unload(
|
||||
vehicle_data.coordinator.async_add_listener(
|
||||
create_vehicle_polling_listener(
|
||||
hass, vehicle_data.vin, vehicle_data.coordinator
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# Setup energy devices with models, versions, and listeners
|
||||
# Add energy device models
|
||||
for energysite in energysites:
|
||||
async_setup_energy_device(hass, entry, energysite, device_registry)
|
||||
models = set()
|
||||
for gateway in energysite.info_coordinator.data.get("components_gateways", []):
|
||||
if gateway.get("part_name"):
|
||||
models.add(gateway["part_name"])
|
||||
for battery in energysite.info_coordinator.data.get("components_batteries", []):
|
||||
if battery.get("part_name"):
|
||||
models.add(battery["part_name"])
|
||||
if models:
|
||||
energysite.device["model"] = ", ".join(sorted(models))
|
||||
|
||||
# Create the energy site device regardless of it having entities
|
||||
# This is so users with a Wall Connector but without a Powerwall can still make service calls
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id, **energysite.device
|
||||
)
|
||||
|
||||
# Remove devices that are no longer present
|
||||
for device_entry in dr.async_entries_for_config_entry(
|
||||
@@ -368,40 +369,6 @@ def create_handle_vehicle_stream(vin: str, coordinator) -> Callable[[dict], None
|
||||
return handle_vehicle_stream
|
||||
|
||||
|
||||
def async_setup_energy_device(
|
||||
hass: HomeAssistant,
|
||||
entry: TeslemetryConfigEntry,
|
||||
energysite: TeslemetryEnergyData,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
) -> None:
|
||||
"""Set up energy device with models, versions, and listeners."""
|
||||
data = energysite.info_coordinator.data
|
||||
models = set()
|
||||
for component in (
|
||||
*data.get("components_gateways", []),
|
||||
*data.get("components_batteries", []),
|
||||
):
|
||||
if part_name := component.get("part_name"):
|
||||
models.add(part_name)
|
||||
if models:
|
||||
energysite.device["model"] = ", ".join(sorted(models))
|
||||
|
||||
if version := data.get("version"):
|
||||
energysite.device["sw_version"] = version
|
||||
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id, **energysite.device
|
||||
)
|
||||
|
||||
entry.async_on_unload(
|
||||
energysite.info_coordinator.async_add_listener(
|
||||
create_energy_info_listener(
|
||||
hass, energysite.id, energysite.info_coordinator
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_stream(
|
||||
hass: HomeAssistant, entry: TeslemetryConfigEntry, vehicle: TeslemetryVehicleData
|
||||
):
|
||||
@@ -413,54 +380,3 @@ async def async_setup_stream(
|
||||
vehicle.stream_vehicle.prefer_typed(True),
|
||||
f"Prefer typed for {vehicle.vin}",
|
||||
)
|
||||
|
||||
entry.async_on_unload(
|
||||
vehicle.stream_vehicle.listen_Version(
|
||||
create_vehicle_streaming_listener(hass, vehicle.vin)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def create_vehicle_streaming_listener(
|
||||
hass: HomeAssistant, vin: str
|
||||
) -> Callable[[str | None], None]:
|
||||
"""Create a listener for vehicle streaming version updates."""
|
||||
|
||||
def handle_version(value: str | None) -> None:
|
||||
"""Handle version update from stream."""
|
||||
if value is not None:
|
||||
# Remove build from version (e.g., "2024.44.25 abc123" -> "2024.44.25")
|
||||
sw_version = value.split(" ")[0]
|
||||
async_update_device_sw_version(hass, vin, sw_version)
|
||||
|
||||
return handle_version
|
||||
|
||||
|
||||
def create_vehicle_polling_listener(
|
||||
hass: HomeAssistant, vin: str, coordinator: TeslemetryVehicleDataCoordinator
|
||||
) -> Callable[[], None]:
|
||||
"""Create a listener for vehicle polling coordinator updates."""
|
||||
|
||||
def handle_update() -> None:
|
||||
"""Handle coordinator update."""
|
||||
if version := coordinator.data.get("vehicle_state_car_version"):
|
||||
# Remove build from version (e.g., "2024.44.25 abc123" -> "2024.44.25")
|
||||
sw_version = version.split(" ")[0]
|
||||
async_update_device_sw_version(hass, vin, sw_version)
|
||||
|
||||
return handle_update
|
||||
|
||||
|
||||
def create_energy_info_listener(
|
||||
hass: HomeAssistant,
|
||||
site_id: int,
|
||||
coordinator: TeslemetryEnergySiteInfoCoordinator,
|
||||
) -> Callable[[], None]:
|
||||
"""Create a listener for energy site info coordinator updates."""
|
||||
|
||||
def handle_update() -> None:
|
||||
"""Handle coordinator update."""
|
||||
if version := coordinator.data.get("version"):
|
||||
async_update_device_sw_version(hass, str(site_id), version)
|
||||
|
||||
return handle_update
|
||||
|
||||
@@ -4,9 +4,7 @@ from typing import Any
|
||||
|
||||
from tesla_fleet_api.exceptions import TeslaFleetError
|
||||
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
|
||||
@@ -70,14 +68,3 @@ async def handle_vehicle_command(command) -> Any:
|
||||
)
|
||||
# Response with result of true
|
||||
return result
|
||||
|
||||
|
||||
@callback
|
||||
def async_update_device_sw_version(
|
||||
hass: HomeAssistant, identifier: str, sw_version: str
|
||||
) -> None:
|
||||
"""Update the software version in the device registry."""
|
||||
dev_reg = dr.async_get(hass)
|
||||
if device := dev_reg.async_get_device(identifiers={(DOMAIN, identifier)}):
|
||||
if device.sw_version != sw_version:
|
||||
dev_reg.async_update_device(device.id, sw_version=sw_version)
|
||||
|
||||
@@ -46,7 +46,11 @@ rules:
|
||||
test fixture. Clarify _alt and _noscope fixture purposes. Test error messages in
|
||||
test_service_validation_errors.
|
||||
# Gold
|
||||
devices: done
|
||||
devices:
|
||||
status: todo
|
||||
comment: |
|
||||
Add model id to device info. VIN sensor may be redundant (already serial number in device).
|
||||
Version sensor should be sw_version in device info instead.
|
||||
diagnostics: done
|
||||
discovery:
|
||||
status: exempt
|
||||
|
||||
@@ -1532,6 +1532,10 @@ ENERGY_INFO_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = (
|
||||
device_class=SensorDeviceClass.BATTERY,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="version",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
)
|
||||
|
||||
ENERGY_HISTORY_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = tuple(
|
||||
|
||||
@@ -1032,6 +1032,9 @@
|
||||
"vehicle_state_tpms_pressure_rr": {
|
||||
"name": "Tire pressure rear right"
|
||||
},
|
||||
"version": {
|
||||
"name": "Version"
|
||||
},
|
||||
"vin": {
|
||||
"name": "[%key:component::teslemetry::common::vehicle%]",
|
||||
"state": {
|
||||
|
||||
@@ -132,7 +132,7 @@ class TraccarEntity(TrackerEntity, RestoreEntity):
|
||||
)
|
||||
|
||||
@property
|
||||
def battery_level(self) -> int | None:
|
||||
def battery_level(self):
|
||||
"""Return battery value of the device."""
|
||||
return self._battery
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import asyncio
|
||||
from http import HTTPStatus
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import aiohttp
|
||||
import voluptuous as vol
|
||||
@@ -12,7 +11,6 @@ from homeassistant.components.tts import (
|
||||
CONF_LANG,
|
||||
PLATFORM_SCHEMA as TTS_PLATFORM_SCHEMA,
|
||||
Provider,
|
||||
TtsAudioType,
|
||||
)
|
||||
from homeassistant.const import CONF_API_KEY
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
@@ -184,21 +182,17 @@ class VoiceRSSProvider(Provider):
|
||||
}
|
||||
|
||||
@property
|
||||
def default_language(self) -> str:
|
||||
def default_language(self):
|
||||
"""Return the default language."""
|
||||
return self._lang
|
||||
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
def supported_languages(self):
|
||||
"""Return list of supported languages."""
|
||||
return SUPPORT_LANGUAGES
|
||||
|
||||
async def async_get_tts_audio(
|
||||
self, message: str, language: str, options: dict[str, Any]
|
||||
) -> TtsAudioType:
|
||||
async def async_get_tts_audio(self, message, language, options):
|
||||
"""Load TTS from VoiceRSS."""
|
||||
if TYPE_CHECKING:
|
||||
assert self.hass
|
||||
websession = async_get_clientsession(self.hass)
|
||||
form_data = self._form_data.copy()
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""Support for IBM Watson TTS integration."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator
|
||||
from ibm_watson import TextToSpeechV1
|
||||
@@ -10,7 +9,6 @@ import voluptuous as vol
|
||||
from homeassistant.components.tts import (
|
||||
PLATFORM_SCHEMA as TTS_PLATFORM_SCHEMA,
|
||||
Provider,
|
||||
TtsAudioType,
|
||||
)
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
|
||||
@@ -165,28 +163,26 @@ class WatsonTTSProvider(Provider):
|
||||
self.name = "Watson TTS"
|
||||
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
def supported_languages(self):
|
||||
"""Return a list of supported languages."""
|
||||
return self.supported_langs
|
||||
|
||||
@property
|
||||
def default_language(self) -> str:
|
||||
def default_language(self):
|
||||
"""Return the default language."""
|
||||
return self.default_lang
|
||||
|
||||
@property
|
||||
def default_options(self) -> dict[str, Any]:
|
||||
def default_options(self):
|
||||
"""Return dict include default options."""
|
||||
return {CONF_VOICE: self.default_voice}
|
||||
|
||||
@property
|
||||
def supported_options(self) -> list[str]:
|
||||
def supported_options(self):
|
||||
"""Return a list of supported options."""
|
||||
return [CONF_VOICE]
|
||||
|
||||
def get_tts_audio(
|
||||
self, message: str, language: str, options: dict[str, Any]
|
||||
) -> TtsAudioType:
|
||||
def get_tts_audio(self, message, language, options):
|
||||
"""Request TTS file from Watson TTS."""
|
||||
response = self.service.synthesize(
|
||||
text=message, accept=self.output_format, voice=options[CONF_VOICE]
|
||||
|
||||
@@ -23,7 +23,6 @@ from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import DATA_HASS_CONFIG, DOMAIN, PLATFORMS, WEBOSTV_EXCEPTIONS
|
||||
from .helpers import WebOsTvConfigEntry, update_client_key
|
||||
from .services import async_setup_services
|
||||
|
||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
|
||||
@@ -32,8 +31,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the LG webOS TV platform."""
|
||||
hass.data.setdefault(DOMAIN, {DATA_HASS_CONFIG: config})
|
||||
|
||||
async_setup_services(hass)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -12,12 +12,17 @@ PLATFORMS = [Platform.MEDIA_PLAYER]
|
||||
DATA_HASS_CONFIG = "hass_config"
|
||||
DEFAULT_NAME = "LG webOS TV"
|
||||
|
||||
ATTR_BUTTON = "button"
|
||||
ATTR_PAYLOAD = "payload"
|
||||
ATTR_SOUND_OUTPUT = "sound_output"
|
||||
|
||||
CONF_ON_ACTION = "turn_on_action"
|
||||
CONF_SOURCES = "sources"
|
||||
|
||||
SERVICE_BUTTON = "button"
|
||||
SERVICE_COMMAND = "command"
|
||||
SERVICE_SELECT_SOUND_OUTPUT = "select_sound_output"
|
||||
|
||||
LIVE_TV_APP_ID = "com.webos.app.livetv"
|
||||
|
||||
WEBOSTV_EXCEPTIONS = (
|
||||
|
||||
@@ -12,6 +12,7 @@ import logging
|
||||
from typing import Any, Concatenate, cast
|
||||
|
||||
from aiowebostv import WebOsTvPairError, WebOsTvState
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import util
|
||||
from homeassistant.components.media_player import (
|
||||
@@ -21,21 +22,27 @@ from homeassistant.components.media_player import (
|
||||
MediaPlayerState,
|
||||
MediaType,
|
||||
)
|
||||
from homeassistant.const import ATTR_SUPPORTED_FEATURES
|
||||
from homeassistant.core import HomeAssistant, ServiceResponse
|
||||
from homeassistant.const import ATTR_COMMAND, ATTR_SUPPORTED_FEATURES
|
||||
from homeassistant.core import HomeAssistant, ServiceResponse, SupportsResponse
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from homeassistant.helpers.trigger import PluggableAction
|
||||
from homeassistant.helpers.typing import VolDictType
|
||||
|
||||
from .const import (
|
||||
ATTR_BUTTON,
|
||||
ATTR_PAYLOAD,
|
||||
ATTR_SOUND_OUTPUT,
|
||||
CONF_SOURCES,
|
||||
DOMAIN,
|
||||
LIVE_TV_APP_ID,
|
||||
SERVICE_BUTTON,
|
||||
SERVICE_COMMAND,
|
||||
SERVICE_SELECT_SOUND_OUTPUT,
|
||||
WEBOSTV_EXCEPTIONS,
|
||||
)
|
||||
from .helpers import WebOsTvConfigEntry, update_client_key
|
||||
@@ -63,6 +70,34 @@ MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1)
|
||||
PARALLEL_UPDATES = 0
|
||||
SCAN_INTERVAL = timedelta(seconds=10)
|
||||
|
||||
BUTTON_SCHEMA: VolDictType = {vol.Required(ATTR_BUTTON): cv.string}
|
||||
COMMAND_SCHEMA: VolDictType = {
|
||||
vol.Required(ATTR_COMMAND): cv.string,
|
||||
vol.Optional(ATTR_PAYLOAD): dict,
|
||||
}
|
||||
SOUND_OUTPUT_SCHEMA: VolDictType = {vol.Required(ATTR_SOUND_OUTPUT): cv.string}
|
||||
|
||||
SERVICES = (
|
||||
(
|
||||
SERVICE_BUTTON,
|
||||
BUTTON_SCHEMA,
|
||||
"async_button",
|
||||
SupportsResponse.NONE,
|
||||
),
|
||||
(
|
||||
SERVICE_COMMAND,
|
||||
COMMAND_SCHEMA,
|
||||
"async_command",
|
||||
SupportsResponse.OPTIONAL,
|
||||
),
|
||||
(
|
||||
SERVICE_SELECT_SOUND_OUTPUT,
|
||||
SOUND_OUTPUT_SCHEMA,
|
||||
"async_select_sound_output",
|
||||
SupportsResponse.OPTIONAL,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
@@ -70,6 +105,12 @@ async def async_setup_entry(
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the LG webOS TV platform."""
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
|
||||
for service_name, schema, method, supports_response in SERVICES:
|
||||
platform.async_register_entity_service(
|
||||
service_name, schema, method, supports_response=supports_response
|
||||
)
|
||||
|
||||
async_add_entities([LgWebOSMediaPlayerEntity(entry)])
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
"""LG webOS TV services."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import DOMAIN as MEDIA_PLAYER_DOMAIN
|
||||
from homeassistant.const import ATTR_COMMAND
|
||||
from homeassistant.core import HomeAssistant, SupportsResponse, callback
|
||||
from homeassistant.helpers import config_validation as cv, service
|
||||
from homeassistant.helpers.typing import VolDictType
|
||||
|
||||
from .const import ATTR_PAYLOAD, ATTR_SOUND_OUTPUT, DOMAIN
|
||||
|
||||
ATTR_BUTTON = "button"
|
||||
|
||||
SERVICE_BUTTON = "button"
|
||||
SERVICE_COMMAND = "command"
|
||||
SERVICE_SELECT_SOUND_OUTPUT = "select_sound_output"
|
||||
|
||||
BUTTON_SCHEMA: VolDictType = {vol.Required(ATTR_BUTTON): cv.string}
|
||||
COMMAND_SCHEMA: VolDictType = {
|
||||
vol.Required(ATTR_COMMAND): cv.string,
|
||||
vol.Optional(ATTR_PAYLOAD): dict,
|
||||
}
|
||||
SOUND_OUTPUT_SCHEMA: VolDictType = {vol.Required(ATTR_SOUND_OUTPUT): cv.string}
|
||||
|
||||
SERVICES = (
|
||||
(
|
||||
SERVICE_BUTTON,
|
||||
BUTTON_SCHEMA,
|
||||
"async_button",
|
||||
SupportsResponse.NONE,
|
||||
),
|
||||
(
|
||||
SERVICE_COMMAND,
|
||||
COMMAND_SCHEMA,
|
||||
"async_command",
|
||||
SupportsResponse.OPTIONAL,
|
||||
),
|
||||
(
|
||||
SERVICE_SELECT_SOUND_OUTPUT,
|
||||
SOUND_OUTPUT_SCHEMA,
|
||||
"async_select_sound_output",
|
||||
SupportsResponse.OPTIONAL,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_services(hass: HomeAssistant) -> None:
|
||||
"""Set up services."""
|
||||
|
||||
for service_name, schema, method, supports_response in SERVICES:
|
||||
service.async_register_platform_entity_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
service_name,
|
||||
entity_domain=MEDIA_PLAYER_DOMAIN,
|
||||
schema=schema,
|
||||
func=method,
|
||||
supports_response=supports_response,
|
||||
)
|
||||
@@ -3,7 +3,6 @@
|
||||
import asyncio
|
||||
from http import HTTPStatus
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import aiohttp
|
||||
import voluptuous as vol
|
||||
@@ -12,7 +11,6 @@ from homeassistant.components.tts import (
|
||||
CONF_LANG,
|
||||
PLATFORM_SCHEMA as TTS_PLATFORM_SCHEMA,
|
||||
Provider,
|
||||
TtsAudioType,
|
||||
)
|
||||
from homeassistant.const import CONF_API_KEY
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
@@ -106,26 +104,22 @@ class YandexSpeechKitProvider(Provider):
|
||||
self.name = "YandexTTS"
|
||||
|
||||
@property
|
||||
def default_language(self) -> str:
|
||||
def default_language(self):
|
||||
"""Return the default language."""
|
||||
return self._language
|
||||
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
def supported_languages(self):
|
||||
"""Return list of supported languages."""
|
||||
return SUPPORT_LANGUAGES
|
||||
|
||||
@property
|
||||
def supported_options(self) -> list[str]:
|
||||
def supported_options(self):
|
||||
"""Return list of supported options."""
|
||||
return SUPPORTED_OPTIONS
|
||||
|
||||
async def async_get_tts_audio(
|
||||
self, message: str, language: str, options: dict[str, Any]
|
||||
) -> TtsAudioType:
|
||||
async def async_get_tts_audio(self, message, language, options):
|
||||
"""Load TTS from yandex."""
|
||||
if TYPE_CHECKING:
|
||||
assert self.hass
|
||||
websession = async_get_clientsession(self.hass)
|
||||
actual_language = language
|
||||
|
||||
|
||||
2
homeassistant/generated/labs.py
generated
2
homeassistant/generated/labs.py
generated
@@ -7,7 +7,7 @@ LABS_PREVIEW_FEATURES = {
|
||||
"analytics": {
|
||||
"snapshots": {
|
||||
"feedback_url": "https://forms.gle/GqvRmgmghSDco8M46",
|
||||
"learn_more_url": "https://www.home-assistant.io/blog/2026/02/02/about-device-database/",
|
||||
"learn_more_url": "",
|
||||
"report_issue_url": "https://github.com/OHF-Device-Database/device-database/issues/new",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -39,7 +39,7 @@ habluetooth==5.8.0
|
||||
hass-nabucasa==1.12.0
|
||||
hassil==3.5.0
|
||||
home-assistant-bluetooth==1.13.1
|
||||
home-assistant-frontend==20260128.4
|
||||
home-assistant-frontend==20260128.3
|
||||
home-assistant-intents==2026.1.28
|
||||
httpx==0.28.1
|
||||
ifaddr==0.2.0
|
||||
|
||||
@@ -1484,7 +1484,6 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = {
|
||||
TypeHintMatch(
|
||||
function_name="battery_level",
|
||||
return_type=["int", None],
|
||||
mandatory=True,
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="source_type",
|
||||
@@ -1509,22 +1508,18 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = {
|
||||
TypeHintMatch(
|
||||
function_name="location_name",
|
||||
return_type=["str", None],
|
||||
mandatory=True,
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="latitude",
|
||||
return_type=["float", None],
|
||||
mandatory=True,
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="longitude",
|
||||
return_type=["float", None],
|
||||
mandatory=True,
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="state",
|
||||
return_type=["str", None],
|
||||
mandatory=True,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -1534,17 +1529,14 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = {
|
||||
TypeHintMatch(
|
||||
function_name="ip_address",
|
||||
return_type=["str", None],
|
||||
mandatory=True,
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="mac_address",
|
||||
return_type=["str", None],
|
||||
mandatory=True,
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="hostname",
|
||||
return_type=["str", None],
|
||||
mandatory=True,
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="state",
|
||||
@@ -2710,29 +2702,24 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = {
|
||||
TypeHintMatch(
|
||||
function_name="default_language",
|
||||
return_type=["str", None],
|
||||
mandatory=True,
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="supported_languages",
|
||||
return_type=["list[str]", None],
|
||||
mandatory=True,
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="supported_options",
|
||||
return_type=["list[str]", None],
|
||||
mandatory=True,
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="default_options",
|
||||
return_type=["Mapping[str, Any]", None],
|
||||
mandatory=True,
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="get_tts_audio",
|
||||
arg_types={1: "str", 2: "str", 3: "dict[str, Any]"},
|
||||
return_type="TtsAudioType",
|
||||
has_async_counterpart=True,
|
||||
mandatory=True,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
4
requirements_all.txt
generated
4
requirements_all.txt
generated
@@ -1219,7 +1219,7 @@ hole==0.9.0
|
||||
holidays==0.84
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20260128.4
|
||||
home-assistant-frontend==20260128.3
|
||||
|
||||
# homeassistant.components.conversation
|
||||
home-assistant-intents==2026.1.28
|
||||
@@ -1865,7 +1865,7 @@ pyElectra==1.2.4
|
||||
pyEmby==1.10
|
||||
|
||||
# homeassistant.components.hikvision
|
||||
pyHik==0.4.2
|
||||
pyHik==0.4.1
|
||||
|
||||
# homeassistant.components.homee
|
||||
pyHomee==1.3.8
|
||||
|
||||
4
requirements_test_all.txt
generated
4
requirements_test_all.txt
generated
@@ -1077,7 +1077,7 @@ hole==0.9.0
|
||||
holidays==0.84
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20260128.4
|
||||
home-assistant-frontend==20260128.3
|
||||
|
||||
# homeassistant.components.conversation
|
||||
home-assistant-intents==2026.1.28
|
||||
@@ -1602,7 +1602,7 @@ pyDuotecno==2024.10.1
|
||||
pyElectra==1.2.4
|
||||
|
||||
# homeassistant.components.hikvision
|
||||
pyHik==0.4.2
|
||||
pyHik==0.4.1
|
||||
|
||||
# homeassistant.components.homee
|
||||
pyHomee==1.3.8
|
||||
|
||||
@@ -6,8 +6,7 @@ from aiohttp import ClientError
|
||||
from blinkpy.auth import LoginError
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.blink.const import DOMAIN
|
||||
from homeassistant.components.blink.services import SERVICE_SAVE_VIDEO
|
||||
from homeassistant.components.blink.const import DOMAIN, SERVICE_SAVE_VIDEO
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.blink.const import DOMAIN
|
||||
from homeassistant.components.blink.services import SERVICE_SEND_PIN
|
||||
from homeassistant.components.blink.const import DOMAIN, SERVICE_SEND_PIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import ATTR_CONFIG_ENTRY_ID, CONF_PIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
@@ -10,9 +10,11 @@ import pytest
|
||||
|
||||
from homeassistant import core
|
||||
from homeassistant.components import fan
|
||||
from homeassistant.components.bond.const import DOMAIN
|
||||
from homeassistant.components.bond.const import (
|
||||
DOMAIN,
|
||||
SERVICE_SET_FAN_SPEED_TRACKED_STATE,
|
||||
)
|
||||
from homeassistant.components.bond.fan import PRESET_MODE_BREEZE
|
||||
from homeassistant.components.bond.services import SERVICE_SET_FAN_SPEED_TRACKED_STATE
|
||||
from homeassistant.components.fan import (
|
||||
ATTR_DIRECTION,
|
||||
ATTR_PERCENTAGE,
|
||||
|
||||
@@ -5,11 +5,13 @@ from datetime import timedelta
|
||||
from bond_async import Action, DeviceType
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.bond.const import DOMAIN
|
||||
from homeassistant.components.bond.services import (
|
||||
from homeassistant.components.bond.const import (
|
||||
ATTR_POWER_STATE,
|
||||
DOMAIN,
|
||||
SERVICE_SET_LIGHT_BRIGHTNESS_TRACKED_STATE,
|
||||
SERVICE_SET_LIGHT_POWER_TRACKED_STATE,
|
||||
)
|
||||
from homeassistant.components.bond.light import (
|
||||
SERVICE_START_DECREASING_BRIGHTNESS,
|
||||
SERVICE_START_INCREASING_BRIGHTNESS,
|
||||
SERVICE_STOP,
|
||||
|
||||
@@ -5,9 +5,9 @@ from datetime import timedelta
|
||||
from bond_async import Action, DeviceType
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.bond.const import DOMAIN
|
||||
from homeassistant.components.bond.services import (
|
||||
from homeassistant.components.bond.const import (
|
||||
ATTR_POWER_STATE,
|
||||
DOMAIN,
|
||||
SERVICE_SET_POWER_TRACKED_STATE,
|
||||
)
|
||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||
|
||||
@@ -6,10 +6,10 @@ from unittest.mock import AsyncMock
|
||||
from bring_api import BringNotificationType, BringRequestException
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.bring.const import DOMAIN
|
||||
from homeassistant.components.bring.services import (
|
||||
from homeassistant.components.bring.const import (
|
||||
ATTR_ITEM_NAME,
|
||||
ATTR_NOTIFICATION_TYPE,
|
||||
DOMAIN,
|
||||
SERVICE_PUSH_NOTIFICATION,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
|
||||
@@ -11,9 +11,9 @@ from bring_api import (
|
||||
)
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.bring.const import DOMAIN
|
||||
from homeassistant.components.bring.services import (
|
||||
from homeassistant.components.bring.const import (
|
||||
ATTR_REACTION,
|
||||
DOMAIN,
|
||||
SERVICE_ACTIVITY_STREAM_REACTION,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
|
||||
@@ -24,7 +24,6 @@ from homeassistant.components.climate.const import (
|
||||
ATTR_PRESET_MODE,
|
||||
ATTR_SWING_HORIZONTAL_MODE,
|
||||
ATTR_SWING_MODE,
|
||||
ATTR_TARGET_HUMIDITY_STEP,
|
||||
ATTR_TARGET_TEMP_HIGH,
|
||||
ATTR_TARGET_TEMP_LOW,
|
||||
SERVICE_SET_FAN_MODE,
|
||||
@@ -509,7 +508,6 @@ async def test_humidity_validation(
|
||||
_attr_target_humidity = 50
|
||||
_attr_min_humidity = 50
|
||||
_attr_max_humidity = 60
|
||||
_attr_target_humidity_step = 5
|
||||
|
||||
def set_humidity(self, humidity: int) -> None:
|
||||
"""Set new target humidity."""
|
||||
@@ -528,7 +526,6 @@ async def test_humidity_validation(
|
||||
|
||||
state = hass.states.get("climate.test")
|
||||
assert state.attributes.get(ATTR_HUMIDITY) == 50
|
||||
assert state.attributes.get(ATTR_TARGET_HUMIDITY_STEP) == 5
|
||||
|
||||
with pytest.raises(
|
||||
ServiceValidationError,
|
||||
|
||||
@@ -17,7 +17,6 @@ from homeassistant.components.climate import (
|
||||
ATTR_MIN_TEMP,
|
||||
ATTR_PRESET_MODES,
|
||||
ATTR_SWING_MODES,
|
||||
ATTR_TARGET_HUMIDITY_STEP,
|
||||
ATTR_TARGET_TEMP_STEP,
|
||||
)
|
||||
from homeassistant.components.recorder import Recorder
|
||||
@@ -59,14 +58,13 @@ async def test_exclude_attributes(recorder_mock: Recorder, hass: HomeAssistant)
|
||||
assert len(states) > 1
|
||||
for entity_states in states.values():
|
||||
for state in entity_states:
|
||||
assert ATTR_FAN_MODES not in state.attributes
|
||||
assert ATTR_FRIENDLY_NAME in state.attributes
|
||||
assert ATTR_PRESET_MODES not in state.attributes
|
||||
assert ATTR_HVAC_MODES not in state.attributes
|
||||
assert ATTR_FAN_MODES not in state.attributes
|
||||
assert ATTR_SWING_MODES not in state.attributes
|
||||
assert ATTR_MAX_HUMIDITY not in state.attributes
|
||||
assert ATTR_MIN_TEMP not in state.attributes
|
||||
assert ATTR_MAX_TEMP not in state.attributes
|
||||
assert ATTR_MIN_HUMIDITY not in state.attributes
|
||||
assert ATTR_MIN_TEMP not in state.attributes
|
||||
assert ATTR_PRESET_MODES not in state.attributes
|
||||
assert ATTR_TARGET_HUMIDITY_STEP not in state.attributes
|
||||
assert ATTR_MAX_HUMIDITY not in state.attributes
|
||||
assert ATTR_TARGET_TEMP_STEP not in state.attributes
|
||||
assert ATTR_FRIENDLY_NAME in state.attributes
|
||||
|
||||
@@ -6,8 +6,7 @@ from elgato import ElgatoError
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.elgato.const import DOMAIN
|
||||
from homeassistant.components.elgato.services import SERVICE_IDENTIFY
|
||||
from homeassistant.components.elgato.const import DOMAIN, SERVICE_IDENTIFY
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
ATTR_COLOR_TEMP_KELVIN,
|
||||
|
||||
@@ -36,9 +36,6 @@ MOCK_DEVICE_STATE = DeviceState(
|
||||
name="Fridge",
|
||||
type="fridge",
|
||||
value=5,
|
||||
target=4,
|
||||
min=2,
|
||||
max=8,
|
||||
unit=TemperatureUnit.CELSIUS,
|
||||
),
|
||||
TemperatureControl(
|
||||
@@ -47,9 +44,6 @@ MOCK_DEVICE_STATE = DeviceState(
|
||||
name="Freezer",
|
||||
type="freezer",
|
||||
value=-18,
|
||||
target=-18,
|
||||
min=-24,
|
||||
max=-16,
|
||||
unit=TemperatureUnit.CELSIUS,
|
||||
),
|
||||
],
|
||||
@@ -106,13 +100,11 @@ async def init_integration(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_liebherr_client: MagicMock,
|
||||
platforms: list[Platform],
|
||||
) -> MockConfigEntry:
|
||||
"""Set up the Liebherr integration for testing."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
with patch("homeassistant.components.liebherr.PLATFORMS", platforms):
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return mock_config_entry
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
# serializer version: 1
|
||||
# name: test_numbers[number.test_fridge_bottom_zone_setpoint-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': -16,
|
||||
'min': -24,
|
||||
'mode': <NumberMode.AUTO: 'auto'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': None,
|
||||
'entity_id': 'number.test_fridge_bottom_zone_setpoint',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Bottom zone setpoint',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <NumberDeviceClass.TEMPERATURE: 'temperature'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Bottom zone setpoint',
|
||||
'platform': 'liebherr',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'setpoint_temperature_bottom_zone',
|
||||
'unique_id': 'test_device_id_setpoint_temperature_2',
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[number.test_fridge_bottom_zone_setpoint-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'temperature',
|
||||
'friendly_name': 'Test Fridge Bottom zone setpoint',
|
||||
'max': -16,
|
||||
'min': -24,
|
||||
'mode': <NumberMode.AUTO: 'auto'>,
|
||||
'step': 1,
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.test_fridge_bottom_zone_setpoint',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '-18',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[number.test_fridge_top_zone_setpoint-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 8,
|
||||
'min': 2,
|
||||
'mode': <NumberMode.AUTO: 'auto'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': None,
|
||||
'entity_id': 'number.test_fridge_top_zone_setpoint',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Top zone setpoint',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <NumberDeviceClass.TEMPERATURE: 'temperature'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Top zone setpoint',
|
||||
'platform': 'liebherr',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'setpoint_temperature_top_zone',
|
||||
'unique_id': 'test_device_id_setpoint_temperature_1',
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[number.test_fridge_top_zone_setpoint-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'temperature',
|
||||
'friendly_name': 'Test Fridge Top zone setpoint',
|
||||
'max': 8,
|
||||
'min': 2,
|
||||
'mode': <NumberMode.AUTO: 'auto'>,
|
||||
'step': 1,
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.test_fridge_top_zone_setpoint',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '4',
|
||||
})
|
||||
# ---
|
||||
# name: test_single_zone_number[number.single_zone_fridge_setpoint-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 8,
|
||||
'min': 2,
|
||||
'mode': <NumberMode.AUTO: 'auto'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': None,
|
||||
'entity_id': 'number.single_zone_fridge_setpoint',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Setpoint',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <NumberDeviceClass.TEMPERATURE: 'temperature'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Setpoint',
|
||||
'platform': 'liebherr',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'setpoint_temperature',
|
||||
'unique_id': 'single_zone_id_setpoint_temperature_1',
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_single_zone_number[number.single_zone_fridge_setpoint-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'temperature',
|
||||
'friendly_name': 'Single Zone Fridge Setpoint',
|
||||
'max': 8,
|
||||
'min': 2,
|
||||
'mode': <NumberMode.AUTO: 'auto'>,
|
||||
'step': 1,
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.single_zone_fridge_setpoint',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '4',
|
||||
})
|
||||
# ---
|
||||
@@ -1,321 +0,0 @@
|
||||
"""Test the Liebherr number platform."""
|
||||
|
||||
from datetime import timedelta
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pyliebherrhomeapi import (
|
||||
Device,
|
||||
DeviceState,
|
||||
DeviceType,
|
||||
TemperatureControl,
|
||||
TemperatureUnit,
|
||||
ZonePosition,
|
||||
)
|
||||
from pyliebherrhomeapi.exceptions import LiebherrConnectionError
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.number import (
|
||||
ATTR_VALUE,
|
||||
DEFAULT_MAX_VALUE,
|
||||
DEFAULT_MIN_VALUE,
|
||||
DOMAIN as NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import MOCK_DEVICE
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def platforms() -> list[Platform]:
|
||||
"""Fixture to specify platforms to test."""
|
||||
return [Platform.NUMBER]
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def enable_all_entities(entity_registry_enabled_by_default: None) -> None:
|
||||
"""Make sure all entities are enabled."""
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration")
|
||||
async def test_numbers(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test all number entities with multi-zone device."""
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
|
||||
async def test_single_zone_number(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_liebherr_client: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
platforms: list[Platform],
|
||||
) -> None:
|
||||
"""Test single zone device uses device name without zone suffix."""
|
||||
device = Device(
|
||||
device_id="single_zone_id",
|
||||
nickname="Single Zone Fridge",
|
||||
device_type=DeviceType.FRIDGE,
|
||||
device_name="K2601",
|
||||
)
|
||||
mock_liebherr_client.get_devices.return_value = [device]
|
||||
mock_liebherr_client.get_device_state.return_value = DeviceState(
|
||||
device=device,
|
||||
controls=[
|
||||
TemperatureControl(
|
||||
zone_id=1,
|
||||
zone_position=ZonePosition.TOP,
|
||||
name="Fridge",
|
||||
type="fridge",
|
||||
value=4,
|
||||
target=4,
|
||||
min=2,
|
||||
max=8,
|
||||
unit=TemperatureUnit.CELSIUS,
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
with patch("homeassistant.components.liebherr.PLATFORMS", platforms):
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
|
||||
async def test_multi_zone_with_none_position(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_liebherr_client: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
platforms: list[Platform],
|
||||
) -> None:
|
||||
"""Test multi-zone device with None zone_position falls back to base translation key."""
|
||||
device = Device(
|
||||
device_id="multi_zone_none",
|
||||
nickname="Multi Zone Fridge",
|
||||
device_type=DeviceType.COMBI,
|
||||
device_name="CBNes9999",
|
||||
)
|
||||
mock_liebherr_client.get_devices.return_value = [device]
|
||||
mock_liebherr_client.get_device_state.return_value = DeviceState(
|
||||
device=device,
|
||||
controls=[
|
||||
TemperatureControl(
|
||||
zone_id=1,
|
||||
zone_position=None, # None triggers fallback
|
||||
name="Fridge",
|
||||
type="fridge",
|
||||
value=5,
|
||||
target=4,
|
||||
min=2,
|
||||
max=8,
|
||||
unit=TemperatureUnit.CELSIUS,
|
||||
),
|
||||
TemperatureControl(
|
||||
zone_id=2,
|
||||
zone_position=ZonePosition.BOTTOM,
|
||||
name="Freezer",
|
||||
type="freezer",
|
||||
value=-18,
|
||||
target=-18,
|
||||
min=-24,
|
||||
max=-16,
|
||||
unit=TemperatureUnit.CELSIUS,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
with patch("homeassistant.components.liebherr.PLATFORMS", platforms):
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Zone with None position should have base translation key
|
||||
zone1_entity = entity_registry.async_get("number.multi_zone_fridge_setpoint")
|
||||
assert zone1_entity is not None
|
||||
assert zone1_entity.translation_key == "setpoint_temperature"
|
||||
|
||||
# Zone with valid position should have zone-specific translation key
|
||||
zone2_entity = entity_registry.async_get(
|
||||
"number.multi_zone_fridge_bottom_zone_setpoint"
|
||||
)
|
||||
assert zone2_entity is not None
|
||||
assert zone2_entity.translation_key == "setpoint_temperature_bottom_zone"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration")
|
||||
async def test_set_temperature(
|
||||
hass: HomeAssistant,
|
||||
mock_liebherr_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test setting the temperature."""
|
||||
entity_id = "number.test_fridge_top_zone_setpoint"
|
||||
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_VALUE: 6},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_liebherr_client.set_temperature.assert_called_once_with(
|
||||
device_id="test_device_id",
|
||||
zone_id=1,
|
||||
target=6,
|
||||
unit=TemperatureUnit.CELSIUS,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration")
|
||||
async def test_set_temperature_failure(
|
||||
hass: HomeAssistant,
|
||||
mock_liebherr_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test setting temperature fails gracefully."""
|
||||
entity_id = "number.test_fridge_top_zone_setpoint"
|
||||
|
||||
mock_liebherr_client.set_temperature.side_effect = LiebherrConnectionError(
|
||||
"Connection failed"
|
||||
)
|
||||
|
||||
with pytest.raises(HomeAssistantError, match="Failed to set temperature"):
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_VALUE: 6},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration")
|
||||
async def test_number_update_failure(
|
||||
hass: HomeAssistant,
|
||||
mock_liebherr_client: MagicMock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test number becomes unavailable when coordinator update fails and recovers."""
|
||||
entity_id = "number.test_fridge_top_zone_setpoint"
|
||||
|
||||
# Initial state should be available with value
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == "4"
|
||||
|
||||
# Simulate update error
|
||||
mock_liebherr_client.get_device_state.side_effect = LiebherrConnectionError(
|
||||
"Connection failed"
|
||||
)
|
||||
|
||||
# Advance time to trigger coordinator refresh (60 second interval)
|
||||
freezer.tick(timedelta(seconds=61))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Number should now be unavailable
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
# Simulate recovery
|
||||
mock_liebherr_client.get_device_state.side_effect = None
|
||||
|
||||
freezer.tick(timedelta(seconds=61))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Number should recover
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == "4"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration")
|
||||
async def test_number_when_control_missing(
|
||||
hass: HomeAssistant,
|
||||
mock_liebherr_client: MagicMock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test number entity behavior when temperature control is removed."""
|
||||
entity_id = "number.test_fridge_top_zone_setpoint"
|
||||
|
||||
# Initial values should be from the control
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == "4"
|
||||
assert state.attributes["min"] == 2
|
||||
assert state.attributes["max"] == 8
|
||||
assert state.attributes["unit_of_measurement"] == "°C"
|
||||
|
||||
# Device stops reporting controls
|
||||
mock_liebherr_client.get_device_state.return_value = DeviceState(
|
||||
device=MOCK_DEVICE, controls=[]
|
||||
)
|
||||
|
||||
# Advance time to trigger coordinator refresh
|
||||
freezer.tick(timedelta(seconds=61))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# State should be unavailable
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_number_with_none_min_max(
|
||||
hass: HomeAssistant,
|
||||
mock_liebherr_client: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
platforms: list[Platform],
|
||||
) -> None:
|
||||
"""Test number entity returns defaults when control has None min/max."""
|
||||
device = Device(
|
||||
device_id="none_min_max_device",
|
||||
nickname="Test Fridge",
|
||||
device_type=DeviceType.FRIDGE,
|
||||
device_name="K2601",
|
||||
)
|
||||
mock_liebherr_client.get_devices.return_value = [device]
|
||||
mock_liebherr_client.get_device_state.return_value = DeviceState(
|
||||
device=device,
|
||||
controls=[
|
||||
TemperatureControl(
|
||||
zone_id=1,
|
||||
zone_position=ZonePosition.TOP,
|
||||
name="Fridge",
|
||||
type="fridge",
|
||||
value=4,
|
||||
target=4,
|
||||
min=None, # None min
|
||||
max=None, # None max
|
||||
unit=TemperatureUnit.CELSIUS,
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
with patch("homeassistant.components.liebherr.PLATFORMS", platforms):
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entity_id = "number.test_fridge_setpoint"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
|
||||
# Should return defaults when min/max are None
|
||||
assert state.attributes["min"] == DEFAULT_MIN_VALUE
|
||||
assert state.attributes["max"] == DEFAULT_MAX_VALUE
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Test the Liebherr sensor platform."""
|
||||
|
||||
from datetime import timedelta
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pyliebherrhomeapi import (
|
||||
@@ -22,7 +22,7 @@ from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.liebherr.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import STATE_UNAVAILABLE, Platform
|
||||
from homeassistant.const import STATE_UNAVAILABLE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
@@ -49,7 +49,6 @@ async def test_single_zone_sensor(
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_liebherr_client: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
platforms: list[Platform],
|
||||
) -> None:
|
||||
"""Test single zone device uses device name without zone suffix."""
|
||||
device = Device(
|
||||
@@ -74,9 +73,8 @@ async def test_single_zone_sensor(
|
||||
)
|
||||
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
with patch("homeassistant.components.liebherr.PLATFORMS", platforms):
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
@@ -250,3 +248,9 @@ async def test_sensor_unavailable_when_control_missing(
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
# Verify entity properties return None when control is missing
|
||||
entity = hass.data["entity_components"]["sensor"].get_entity(entity_id)
|
||||
assert entity is not None
|
||||
assert entity.native_value is None
|
||||
assert entity.native_unit_of_measurement is None
|
||||
|
||||
@@ -8,7 +8,7 @@ from unittest.mock import MagicMock
|
||||
from pylitterbot import Robot
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.litterrobot.services import SERVICE_SET_SLEEP_MODE
|
||||
from homeassistant.components.litterrobot.vacuum import SERVICE_SET_SLEEP_MODE
|
||||
from homeassistant.components.vacuum import (
|
||||
DOMAIN as VACUUM_DOMAIN,
|
||||
SERVICE_START,
|
||||
|
||||
@@ -74,6 +74,7 @@ async def integration_fixture(
|
||||
|
||||
@pytest.fixture(
|
||||
params=[
|
||||
"air_purifier",
|
||||
"air_quality_sensor",
|
||||
"aqara_door_window_p2",
|
||||
"aqara_motion_p2",
|
||||
@@ -100,9 +101,7 @@ async def integration_fixture(
|
||||
"ikea_air_quality_monitor",
|
||||
"ikea_scroll_wheel",
|
||||
"inovelli_vtm30",
|
||||
"inovelli_vtm31",
|
||||
"longan_link_thermostat",
|
||||
"mock_air_purifier",
|
||||
"mock_battery_storage",
|
||||
"mock_cooktop",
|
||||
"mock_dimmable_light",
|
||||
@@ -142,6 +141,7 @@ async def integration_fixture(
|
||||
"mock_window_covering_pa_lift",
|
||||
"mock_window_covering_pa_tilt",
|
||||
"mock_window_covering_tilt",
|
||||
"multi_endpoint_light",
|
||||
"onoff_light_with_levelcontrol_present",
|
||||
"resideo_x2s_thermostat",
|
||||
"secuyou_smart_lock",
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"0/40/0": 17,
|
||||
"0/40/1": "TEST_VENDOR",
|
||||
"0/40/2": 65521,
|
||||
"0/40/3": "Mock Air Purifier",
|
||||
"0/40/3": "Air Purifier",
|
||||
"0/40/4": 32769,
|
||||
"0/40/5": "",
|
||||
"0/40/6": "**REDACTED**",
|
||||
@@ -1,4 +1,102 @@
|
||||
# serializer version: 1
|
||||
# name: test_buttons[air_purifier][button.air_purifier_reset_filter_condition-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': None,
|
||||
'entity_id': 'button.air_purifier_reset_filter_condition',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Reset filter condition',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Reset filter condition',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'reset_filter_condition',
|
||||
'unique_id': '00000000000004D2-000000000000008F-MatterNodeDevice-1-HepaFilterMonitoringResetButton-113-65529',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[air_purifier][button.air_purifier_reset_filter_condition-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Air Purifier Reset filter condition',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.air_purifier_reset_filter_condition',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[air_purifier][button.air_purifier_reset_filter_condition_2-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': None,
|
||||
'entity_id': 'button.air_purifier_reset_filter_condition_2',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Reset filter condition',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Reset filter condition',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'reset_filter_condition',
|
||||
'unique_id': '00000000000004D2-000000000000008F-MatterNodeDevice-1-ActivatedCarbonFilterMonitoringResetButton-114-65529',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[air_purifier][button.air_purifier_reset_filter_condition_2-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Air Purifier Reset filter condition',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.air_purifier_reset_filter_condition_2',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[aqara_door_window_p2][button.aqara_door_and_window_sensor_p2_identify-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -1749,306 +1847,6 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[inovelli_vtm31][button.inovelli_identify_1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.inovelli_identify_1',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Identify (1)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Identify (1)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[inovelli_vtm31][button.inovelli_identify_1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': 'Inovelli Identify (1)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.inovelli_identify_1',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[inovelli_vtm31][button.inovelli_identify_2-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.inovelli_identify_2',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Identify (2)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Identify (2)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-2-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[inovelli_vtm31][button.inovelli_identify_2-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': 'Inovelli Identify (2)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.inovelli_identify_2',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[inovelli_vtm31][button.inovelli_identify_6-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.inovelli_identify_6',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Identify (6)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Identify (6)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-6-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[inovelli_vtm31][button.inovelli_identify_6-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': 'Inovelli Identify (6)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.inovelli_identify_6',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[inovelli_vtm31][button.inovelli_identify_config-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.inovelli_identify_config',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Identify (Config)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Identify (Config)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-5-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[inovelli_vtm31][button.inovelli_identify_config-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': 'Inovelli Identify (Config)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.inovelli_identify_config',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[inovelli_vtm31][button.inovelli_identify_down-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.inovelli_identify_down',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Identify (Down)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Identify (Down)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-4-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[inovelli_vtm31][button.inovelli_identify_down-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': 'Inovelli Identify (Down)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.inovelli_identify_down',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[inovelli_vtm31][button.inovelli_identify_up-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.inovelli_identify_up',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Identify (Up)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Identify (Up)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-3-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[inovelli_vtm31][button.inovelli_identify_up-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': 'Inovelli Identify (Up)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.inovelli_identify_up',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[longan_link_thermostat][button.longan_link_hvac_identify-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -2493,104 +2291,6 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[mock_air_purifier][button.mock_air_purifier_reset_filter_condition-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': None,
|
||||
'entity_id': 'button.mock_air_purifier_reset_filter_condition',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Reset filter condition',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Reset filter condition',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'reset_filter_condition',
|
||||
'unique_id': '00000000000004D2-000000000000008F-MatterNodeDevice-1-HepaFilterMonitoringResetButton-113-65529',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[mock_air_purifier][button.mock_air_purifier_reset_filter_condition-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Mock Air Purifier Reset filter condition',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.mock_air_purifier_reset_filter_condition',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[mock_air_purifier][button.mock_air_purifier_reset_filter_condition_2-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': None,
|
||||
'entity_id': 'button.mock_air_purifier_reset_filter_condition_2',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Reset filter condition',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Reset filter condition',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'reset_filter_condition',
|
||||
'unique_id': '00000000000004D2-000000000000008F-MatterNodeDevice-1-ActivatedCarbonFilterMonitoringResetButton-114-65529',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[mock_air_purifier][button.mock_air_purifier_reset_filter_condition_2-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Mock Air Purifier Reset filter condition',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.mock_air_purifier_reset_filter_condition_2',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[mock_lock][button.mock_lock_identify-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -3187,6 +2887,306 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[multi_endpoint_light][button.inovelli_identify_1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.inovelli_identify_1',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Identify (1)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Identify (1)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[multi_endpoint_light][button.inovelli_identify_1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': 'Inovelli Identify (1)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.inovelli_identify_1',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[multi_endpoint_light][button.inovelli_identify_2-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.inovelli_identify_2',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Identify (2)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Identify (2)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-2-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[multi_endpoint_light][button.inovelli_identify_2-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': 'Inovelli Identify (2)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.inovelli_identify_2',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[multi_endpoint_light][button.inovelli_identify_6-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.inovelli_identify_6',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Identify (6)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Identify (6)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-6-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[multi_endpoint_light][button.inovelli_identify_6-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': 'Inovelli Identify (6)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.inovelli_identify_6',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[multi_endpoint_light][button.inovelli_identify_config-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.inovelli_identify_config',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Identify (Config)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Identify (Config)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-5-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[multi_endpoint_light][button.inovelli_identify_config-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': 'Inovelli Identify (Config)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.inovelli_identify_config',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[multi_endpoint_light][button.inovelli_identify_down-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.inovelli_identify_down',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Identify (Down)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Identify (Down)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-4-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[multi_endpoint_light][button.inovelli_identify_down-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': 'Inovelli Identify (Down)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.inovelli_identify_down',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[multi_endpoint_light][button.inovelli_identify_up-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.inovelli_identify_up',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Identify (Up)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Identify (Up)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-3-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[multi_endpoint_light][button.inovelli_identify_up-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': 'Inovelli Identify (Up)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.inovelli_identify_up',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[onoff_light_with_levelcontrol_present][button.d215s_identify-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
@@ -1,4 +1,69 @@
|
||||
# serializer version: 1
|
||||
# name: test_climates[air_purifier][climate.air_purifier-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'hvac_modes': list([
|
||||
<HVACMode.OFF: 'off'>,
|
||||
<HVACMode.HEAT: 'heat'>,
|
||||
]),
|
||||
'max_temp': 30.0,
|
||||
'min_temp': 5.0,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'climate',
|
||||
'entity_category': None,
|
||||
'entity_id': 'climate.air_purifier',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <ClimateEntityFeature: 385>,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-000000000000008F-MatterNodeDevice-5-MatterThermostat-513-0',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_climates[air_purifier][climate.air_purifier-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'current_temperature': 20.0,
|
||||
'friendly_name': 'Air Purifier',
|
||||
'hvac_modes': list([
|
||||
<HVACMode.OFF: 'off'>,
|
||||
<HVACMode.HEAT: 'heat'>,
|
||||
]),
|
||||
'max_temp': 30.0,
|
||||
'min_temp': 5.0,
|
||||
'supported_features': <ClimateEntityFeature: 385>,
|
||||
'temperature': 20.0,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'climate.air_purifier',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_climates[aqara_thermostat_w500][climate.floor_heating_thermostat-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -330,71 +395,6 @@
|
||||
'state': 'cool',
|
||||
})
|
||||
# ---
|
||||
# name: test_climates[mock_air_purifier][climate.mock_air_purifier-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'hvac_modes': list([
|
||||
<HVACMode.OFF: 'off'>,
|
||||
<HVACMode.HEAT: 'heat'>,
|
||||
]),
|
||||
'max_temp': 30.0,
|
||||
'min_temp': 5.0,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'climate',
|
||||
'entity_category': None,
|
||||
'entity_id': 'climate.mock_air_purifier',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <ClimateEntityFeature: 385>,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-000000000000008F-MatterNodeDevice-5-MatterThermostat-513-0',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_climates[mock_air_purifier][climate.mock_air_purifier-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'current_temperature': 20.0,
|
||||
'friendly_name': 'Mock Air Purifier',
|
||||
'hvac_modes': list([
|
||||
<HVACMode.OFF: 'off'>,
|
||||
<HVACMode.HEAT: 'heat'>,
|
||||
]),
|
||||
'max_temp': 30.0,
|
||||
'min_temp': 5.0,
|
||||
'supported_features': <ClimateEntityFeature: 385>,
|
||||
'temperature': 20.0,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'climate.mock_air_purifier',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_climates[mock_room_airconditioner][climate.room_airconditioner-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
@@ -1415,216 +1415,6 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_events[inovelli_vtm31][event.inovelli_button_config-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'event_types': list([
|
||||
'multi_press_1',
|
||||
'multi_press_2',
|
||||
'multi_press_3',
|
||||
'multi_press_4',
|
||||
'multi_press_5',
|
||||
'long_press',
|
||||
'long_release',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'event',
|
||||
'entity_category': None,
|
||||
'entity_id': 'event.inovelli_button_config',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Button (Config)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <EventDeviceClass.BUTTON: 'button'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Button (Config)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'button',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-5-GenericSwitch-59-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_events[inovelli_vtm31][event.inovelli_button_config-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'button',
|
||||
'event_type': None,
|
||||
'event_types': list([
|
||||
'multi_press_1',
|
||||
'multi_press_2',
|
||||
'multi_press_3',
|
||||
'multi_press_4',
|
||||
'multi_press_5',
|
||||
'long_press',
|
||||
'long_release',
|
||||
]),
|
||||
'friendly_name': 'Inovelli Button (Config)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'event.inovelli_button_config',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_events[inovelli_vtm31][event.inovelli_button_down-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'event_types': list([
|
||||
'multi_press_1',
|
||||
'multi_press_2',
|
||||
'multi_press_3',
|
||||
'multi_press_4',
|
||||
'multi_press_5',
|
||||
'long_press',
|
||||
'long_release',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'event',
|
||||
'entity_category': None,
|
||||
'entity_id': 'event.inovelli_button_down',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Button (Down)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <EventDeviceClass.BUTTON: 'button'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Button (Down)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'button',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-4-GenericSwitch-59-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_events[inovelli_vtm31][event.inovelli_button_down-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'button',
|
||||
'event_type': None,
|
||||
'event_types': list([
|
||||
'multi_press_1',
|
||||
'multi_press_2',
|
||||
'multi_press_3',
|
||||
'multi_press_4',
|
||||
'multi_press_5',
|
||||
'long_press',
|
||||
'long_release',
|
||||
]),
|
||||
'friendly_name': 'Inovelli Button (Down)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'event.inovelli_button_down',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_events[inovelli_vtm31][event.inovelli_button_up-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'event_types': list([
|
||||
'multi_press_1',
|
||||
'multi_press_2',
|
||||
'multi_press_3',
|
||||
'multi_press_4',
|
||||
'multi_press_5',
|
||||
'long_press',
|
||||
'long_release',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'event',
|
||||
'entity_category': None,
|
||||
'entity_id': 'event.inovelli_button_up',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Button (Up)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <EventDeviceClass.BUTTON: 'button'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Button (Up)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'button',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-3-GenericSwitch-59-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_events[inovelli_vtm31][event.inovelli_button_up-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'button',
|
||||
'event_type': None,
|
||||
'event_types': list([
|
||||
'multi_press_1',
|
||||
'multi_press_2',
|
||||
'multi_press_3',
|
||||
'multi_press_4',
|
||||
'multi_press_5',
|
||||
'long_press',
|
||||
'long_release',
|
||||
]),
|
||||
'friendly_name': 'Inovelli Button (Up)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'event.inovelli_button_up',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_events[mock_generic_switch][event.mock_generic_switch_button-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -1821,6 +1611,216 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_events[multi_endpoint_light][event.inovelli_button_config-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'event_types': list([
|
||||
'multi_press_1',
|
||||
'multi_press_2',
|
||||
'multi_press_3',
|
||||
'multi_press_4',
|
||||
'multi_press_5',
|
||||
'long_press',
|
||||
'long_release',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'event',
|
||||
'entity_category': None,
|
||||
'entity_id': 'event.inovelli_button_config',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Button (Config)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <EventDeviceClass.BUTTON: 'button'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Button (Config)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'button',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-5-GenericSwitch-59-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_events[multi_endpoint_light][event.inovelli_button_config-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'button',
|
||||
'event_type': None,
|
||||
'event_types': list([
|
||||
'multi_press_1',
|
||||
'multi_press_2',
|
||||
'multi_press_3',
|
||||
'multi_press_4',
|
||||
'multi_press_5',
|
||||
'long_press',
|
||||
'long_release',
|
||||
]),
|
||||
'friendly_name': 'Inovelli Button (Config)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'event.inovelli_button_config',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_events[multi_endpoint_light][event.inovelli_button_down-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'event_types': list([
|
||||
'multi_press_1',
|
||||
'multi_press_2',
|
||||
'multi_press_3',
|
||||
'multi_press_4',
|
||||
'multi_press_5',
|
||||
'long_press',
|
||||
'long_release',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'event',
|
||||
'entity_category': None,
|
||||
'entity_id': 'event.inovelli_button_down',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Button (Down)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <EventDeviceClass.BUTTON: 'button'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Button (Down)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'button',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-4-GenericSwitch-59-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_events[multi_endpoint_light][event.inovelli_button_down-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'button',
|
||||
'event_type': None,
|
||||
'event_types': list([
|
||||
'multi_press_1',
|
||||
'multi_press_2',
|
||||
'multi_press_3',
|
||||
'multi_press_4',
|
||||
'multi_press_5',
|
||||
'long_press',
|
||||
'long_release',
|
||||
]),
|
||||
'friendly_name': 'Inovelli Button (Down)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'event.inovelli_button_down',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_events[multi_endpoint_light][event.inovelli_button_up-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'event_types': list([
|
||||
'multi_press_1',
|
||||
'multi_press_2',
|
||||
'multi_press_3',
|
||||
'multi_press_4',
|
||||
'multi_press_5',
|
||||
'long_press',
|
||||
'long_release',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'event',
|
||||
'entity_category': None,
|
||||
'entity_id': 'event.inovelli_button_up',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Button (Up)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <EventDeviceClass.BUTTON: 'button'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Button (Up)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'button',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-3-GenericSwitch-59-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_events[multi_endpoint_light][event.inovelli_button_up-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'button',
|
||||
'event_type': None,
|
||||
'event_types': list([
|
||||
'multi_press_1',
|
||||
'multi_press_2',
|
||||
'multi_press_3',
|
||||
'multi_press_4',
|
||||
'multi_press_5',
|
||||
'long_press',
|
||||
'long_release',
|
||||
]),
|
||||
'friendly_name': 'Inovelli Button (Up)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'event.inovelli_button_up',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_events[silabs_light_switch][event.light_switch_example_button-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
@@ -1,4 +1,76 @@
|
||||
# serializer version: 1
|
||||
# name: test_fans[air_purifier][fan.air_purifier-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'preset_modes': list([
|
||||
'low',
|
||||
'medium',
|
||||
'high',
|
||||
'auto',
|
||||
'natural_wind',
|
||||
'sleep_wind',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'fan',
|
||||
'entity_category': None,
|
||||
'entity_id': 'fan.air_purifier',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <FanEntityFeature: 63>,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-000000000000008F-MatterNodeDevice-1-MatterFan-514-0',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_fans[air_purifier][fan.air_purifier-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'direction': 'forward',
|
||||
'friendly_name': 'Air Purifier',
|
||||
'oscillating': False,
|
||||
'percentage': None,
|
||||
'percentage_step': 10.0,
|
||||
'preset_mode': 'auto',
|
||||
'preset_modes': list([
|
||||
'low',
|
||||
'medium',
|
||||
'high',
|
||||
'auto',
|
||||
'natural_wind',
|
||||
'sleep_wind',
|
||||
]),
|
||||
'supported_features': <FanEntityFeature: 63>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'fan.air_purifier',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_fans[longan_link_thermostat][fan.longan_link_hvac-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -63,78 +135,6 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_fans[mock_air_purifier][fan.mock_air_purifier-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'preset_modes': list([
|
||||
'low',
|
||||
'medium',
|
||||
'high',
|
||||
'auto',
|
||||
'natural_wind',
|
||||
'sleep_wind',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'fan',
|
||||
'entity_category': None,
|
||||
'entity_id': 'fan.mock_air_purifier',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <FanEntityFeature: 63>,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-000000000000008F-MatterNodeDevice-1-MatterFan-514-0',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_fans[mock_air_purifier][fan.mock_air_purifier-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'direction': 'forward',
|
||||
'friendly_name': 'Mock Air Purifier',
|
||||
'oscillating': False,
|
||||
'percentage': None,
|
||||
'percentage_step': 10.0,
|
||||
'preset_mode': 'auto',
|
||||
'preset_modes': list([
|
||||
'low',
|
||||
'medium',
|
||||
'high',
|
||||
'auto',
|
||||
'natural_wind',
|
||||
'sleep_wind',
|
||||
]),
|
||||
'supported_features': <FanEntityFeature: 63>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'fan.mock_air_purifier',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_fans[mock_extractor_hood][fan.mock_extractor_hood-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
@@ -243,141 +243,6 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_lights[inovelli_vtm31][light.inovelli_light_1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'supported_color_modes': list([
|
||||
<ColorMode.BRIGHTNESS: 'brightness'>,
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'light',
|
||||
'entity_category': None,
|
||||
'entity_id': 'light.inovelli_light_1',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Light (1)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Light (1)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <LightEntityFeature: 32>,
|
||||
'translation_key': 'light',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-MatterLight-6-0',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_lights[inovelli_vtm31][light.inovelli_light_1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'brightness': None,
|
||||
'color_mode': None,
|
||||
'friendly_name': 'Inovelli Light (1)',
|
||||
'supported_color_modes': list([
|
||||
<ColorMode.BRIGHTNESS: 'brightness'>,
|
||||
]),
|
||||
'supported_features': <LightEntityFeature: 32>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'light.inovelli_light_1',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_lights[inovelli_vtm31][light.inovelli_light_6-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max_color_temp_kelvin': 6535,
|
||||
'max_mireds': 500,
|
||||
'min_color_temp_kelvin': 2000,
|
||||
'min_mireds': 153,
|
||||
'supported_color_modes': list([
|
||||
<ColorMode.COLOR_TEMP: 'color_temp'>,
|
||||
<ColorMode.HS: 'hs'>,
|
||||
<ColorMode.XY: 'xy'>,
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'light',
|
||||
'entity_category': None,
|
||||
'entity_id': 'light.inovelli_light_6',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Light (6)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Light (6)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <LightEntityFeature: 32>,
|
||||
'translation_key': 'light',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-6-MatterLight-6-0',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_lights[inovelli_vtm31][light.inovelli_light_6-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'brightness': None,
|
||||
'color_mode': None,
|
||||
'color_temp': None,
|
||||
'color_temp_kelvin': None,
|
||||
'friendly_name': 'Inovelli Light (6)',
|
||||
'hs_color': None,
|
||||
'max_color_temp_kelvin': 6535,
|
||||
'max_mireds': 500,
|
||||
'min_color_temp_kelvin': 2000,
|
||||
'min_mireds': 153,
|
||||
'rgb_color': None,
|
||||
'supported_color_modes': list([
|
||||
<ColorMode.COLOR_TEMP: 'color_temp'>,
|
||||
<ColorMode.HS: 'hs'>,
|
||||
<ColorMode.XY: 'xy'>,
|
||||
]),
|
||||
'supported_features': <LightEntityFeature: 32>,
|
||||
'xy_color': None,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'light.inovelli_light_6',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_lights[mock_dimmable_light][light.mock_dimmable_light-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -763,6 +628,141 @@
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_lights[multi_endpoint_light][light.inovelli_light_1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'supported_color_modes': list([
|
||||
<ColorMode.BRIGHTNESS: 'brightness'>,
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'light',
|
||||
'entity_category': None,
|
||||
'entity_id': 'light.inovelli_light_1',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Light (1)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Light (1)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <LightEntityFeature: 32>,
|
||||
'translation_key': 'light',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-MatterLight-6-0',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_lights[multi_endpoint_light][light.inovelli_light_1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'brightness': None,
|
||||
'color_mode': None,
|
||||
'friendly_name': 'Inovelli Light (1)',
|
||||
'supported_color_modes': list([
|
||||
<ColorMode.BRIGHTNESS: 'brightness'>,
|
||||
]),
|
||||
'supported_features': <LightEntityFeature: 32>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'light.inovelli_light_1',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_lights[multi_endpoint_light][light.inovelli_light_6-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max_color_temp_kelvin': 6535,
|
||||
'max_mireds': 500,
|
||||
'min_color_temp_kelvin': 2000,
|
||||
'min_mireds': 153,
|
||||
'supported_color_modes': list([
|
||||
<ColorMode.COLOR_TEMP: 'color_temp'>,
|
||||
<ColorMode.HS: 'hs'>,
|
||||
<ColorMode.XY: 'xy'>,
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'light',
|
||||
'entity_category': None,
|
||||
'entity_id': 'light.inovelli_light_6',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Light (6)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Light (6)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <LightEntityFeature: 32>,
|
||||
'translation_key': 'light',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-6-MatterLight-6-0',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_lights[multi_endpoint_light][light.inovelli_light_6-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'brightness': None,
|
||||
'color_mode': None,
|
||||
'color_temp': None,
|
||||
'color_temp_kelvin': None,
|
||||
'friendly_name': 'Inovelli Light (6)',
|
||||
'hs_color': None,
|
||||
'max_color_temp_kelvin': 6535,
|
||||
'max_mireds': 500,
|
||||
'min_color_temp_kelvin': 2000,
|
||||
'min_mireds': 153,
|
||||
'rgb_color': None,
|
||||
'supported_color_modes': list([
|
||||
<ColorMode.COLOR_TEMP: 'color_temp'>,
|
||||
<ColorMode.HS: 'hs'>,
|
||||
<ColorMode.XY: 'xy'>,
|
||||
]),
|
||||
'supported_features': <LightEntityFeature: 32>,
|
||||
'xy_color': None,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'light.inovelli_light_6',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_lights[onoff_light_with_levelcontrol_present][light.d215s-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
@@ -1116,415 +1116,6 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[inovelli_vtm31][number.inovelli_led_off_intensity-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 75,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.inovelli_led_off_intensity',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'LED off intensity',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'LED off intensity',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'led_indicator_intensity_off',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-InovelliLEDIndicatorIntensityOff-305134641-305070178',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[inovelli_vtm31][number.inovelli_led_off_intensity-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Inovelli LED off intensity',
|
||||
'max': 75,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.inovelli_led_off_intensity',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[inovelli_vtm31][number.inovelli_led_on_intensity-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 75,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.inovelli_led_on_intensity',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'LED on intensity',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'LED on intensity',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'led_indicator_intensity_on',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-InovelliLEDIndicatorIntensityOn-305134641-305070177',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[inovelli_vtm31][number.inovelli_led_on_intensity-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Inovelli LED on intensity',
|
||||
'max': 75,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.inovelli_led_on_intensity',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '33',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[inovelli_vtm31][number.inovelli_off_transition_time-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 65534,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 0.1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.inovelli_off_transition_time',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Off transition time',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Off transition time',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'off_transition_time',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-off_transition_time-8-19',
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[inovelli_vtm31][number.inovelli_off_transition_time-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Inovelli Off transition time',
|
||||
'max': 65534,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 0.1,
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.inovelli_off_transition_time',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '0.5',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[inovelli_vtm31][number.inovelli_on_level_1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 255,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.inovelli_on_level_1',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'On level (1)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'On level (1)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'on_level',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-on_level-8-17',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[inovelli_vtm31][number.inovelli_on_level_1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Inovelli On level (1)',
|
||||
'max': 255,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.inovelli_on_level_1',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '137',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[inovelli_vtm31][number.inovelli_on_level_6-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 255,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.inovelli_on_level_6',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'On level (6)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'On level (6)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'on_level',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-6-on_level-8-17',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[inovelli_vtm31][number.inovelli_on_level_6-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Inovelli On level (6)',
|
||||
'max': 255,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.inovelli_on_level_6',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '254',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[inovelli_vtm31][number.inovelli_on_off_transition_time-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 65534,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 0.1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.inovelli_on_off_transition_time',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'On/Off transition time',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'On/Off transition time',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'on_off_transition_time',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-on_off_transition_time-8-16',
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[inovelli_vtm31][number.inovelli_on_off_transition_time-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Inovelli On/Off transition time',
|
||||
'max': 65534,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 0.1,
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.inovelli_on_off_transition_time',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '0.5',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[inovelli_vtm31][number.inovelli_on_transition_time-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 65534,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 0.1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.inovelli_on_transition_time',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'On transition time',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'On transition time',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'on_transition_time',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-on_transition_time-8-18',
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[inovelli_vtm31][number.inovelli_on_transition_time-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Inovelli On transition time',
|
||||
'max': 65534,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 0.1,
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.inovelli_on_transition_time',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1.5',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[mock_dimmable_light][number.mock_dimmable_light_off_transition_time-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -3522,6 +3113,415 @@
|
||||
'state': '0',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[multi_endpoint_light][number.inovelli_led_off_intensity-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 75,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.inovelli_led_off_intensity',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'LED off intensity',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'LED off intensity',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'led_indicator_intensity_off',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-InovelliLEDIndicatorIntensityOff-305134641-305070178',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[multi_endpoint_light][number.inovelli_led_off_intensity-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Inovelli LED off intensity',
|
||||
'max': 75,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.inovelli_led_off_intensity',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[multi_endpoint_light][number.inovelli_led_on_intensity-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 75,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.inovelli_led_on_intensity',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'LED on intensity',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'LED on intensity',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'led_indicator_intensity_on',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-InovelliLEDIndicatorIntensityOn-305134641-305070177',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[multi_endpoint_light][number.inovelli_led_on_intensity-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Inovelli LED on intensity',
|
||||
'max': 75,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.inovelli_led_on_intensity',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '33',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[multi_endpoint_light][number.inovelli_off_transition_time-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 65534,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 0.1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.inovelli_off_transition_time',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Off transition time',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Off transition time',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'off_transition_time',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-off_transition_time-8-19',
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[multi_endpoint_light][number.inovelli_off_transition_time-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Inovelli Off transition time',
|
||||
'max': 65534,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 0.1,
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.inovelli_off_transition_time',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '0.5',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[multi_endpoint_light][number.inovelli_on_level_1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 255,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.inovelli_on_level_1',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'On level (1)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'On level (1)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'on_level',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-on_level-8-17',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[multi_endpoint_light][number.inovelli_on_level_1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Inovelli On level (1)',
|
||||
'max': 255,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.inovelli_on_level_1',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '137',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[multi_endpoint_light][number.inovelli_on_level_6-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 255,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.inovelli_on_level_6',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'On level (6)',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'On level (6)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'on_level',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-6-on_level-8-17',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[multi_endpoint_light][number.inovelli_on_level_6-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Inovelli On level (6)',
|
||||
'max': 255,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.inovelli_on_level_6',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '254',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[multi_endpoint_light][number.inovelli_on_off_transition_time-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 65534,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 0.1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.inovelli_on_off_transition_time',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'On/Off transition time',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'On/Off transition time',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'on_off_transition_time',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-on_off_transition_time-8-16',
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[multi_endpoint_light][number.inovelli_on_off_transition_time-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Inovelli On/Off transition time',
|
||||
'max': 65534,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 0.1,
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.inovelli_on_off_transition_time',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '0.5',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[multi_endpoint_light][number.inovelli_on_transition_time-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 65534,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 0.1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.inovelli_on_transition_time',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'On transition time',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'On transition time',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'on_transition_time',
|
||||
'unique_id': '00000000000004D2-00000000000000C5-MatterNodeDevice-1-on_transition_time-8-18',
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[multi_endpoint_light][number.inovelli_on_transition_time-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Inovelli On transition time',
|
||||
'max': 65534,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 0.1,
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.inovelli_on_transition_time',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1.5',
|
||||
})
|
||||
# ---
|
||||
# name: test_numbers[onoff_light_with_levelcontrol_present][number.d215s_on_level-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user