mirror of
https://github.com/home-assistant/core.git
synced 2025-12-01 21:48:03 +00:00
Compare commits
1 Commits
remote_sen
...
services_f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bd5eed295 |
@@ -231,7 +231,7 @@ jobs:
|
||||
- name: Detect duplicates using AI
|
||||
id: ai_detection
|
||||
if: steps.extract.outputs.should_continue == 'true' && steps.fetch_similar.outputs.has_similar == 'true'
|
||||
uses: actions/ai-inference@334892bb203895caaed82ec52d23c1ed9385151e # v2.0.4
|
||||
uses: actions/ai-inference@02c6cc30ae592ce65ee356387748dfc2fd5f7993 # v2.0.3
|
||||
with:
|
||||
model: openai/gpt-4o
|
||||
system-prompt: |
|
||||
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
- name: Detect language using AI
|
||||
id: ai_language_detection
|
||||
if: steps.detect_language.outputs.should_continue == 'true'
|
||||
uses: actions/ai-inference@334892bb203895caaed82ec52d23c1ed9385151e # v2.0.4
|
||||
uses: actions/ai-inference@02c6cc30ae592ce65ee356387748dfc2fd5f7993 # v2.0.3
|
||||
with:
|
||||
model: openai/gpt-4o-mini
|
||||
system-prompt: |
|
||||
|
||||
@@ -160,6 +160,7 @@
|
||||
"triggers": {
|
||||
"armed": {
|
||||
"description": "Triggers when an alarm is armed.",
|
||||
"description_configured": "[%key:component::alarm_control_panel::triggers::armed::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::alarm_control_panel::common::trigger_behavior_description%]",
|
||||
@@ -170,6 +171,7 @@
|
||||
},
|
||||
"armed_away": {
|
||||
"description": "Triggers when an alarm is armed away.",
|
||||
"description_configured": "[%key:component::alarm_control_panel::triggers::armed_away::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::alarm_control_panel::common::trigger_behavior_description%]",
|
||||
@@ -180,6 +182,7 @@
|
||||
},
|
||||
"armed_home": {
|
||||
"description": "Triggers when an alarm is armed home.",
|
||||
"description_configured": "[%key:component::alarm_control_panel::triggers::armed_home::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::alarm_control_panel::common::trigger_behavior_description%]",
|
||||
@@ -190,6 +193,7 @@
|
||||
},
|
||||
"armed_night": {
|
||||
"description": "Triggers when an alarm is armed night.",
|
||||
"description_configured": "[%key:component::alarm_control_panel::triggers::armed_night::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::alarm_control_panel::common::trigger_behavior_description%]",
|
||||
@@ -200,6 +204,7 @@
|
||||
},
|
||||
"armed_vacation": {
|
||||
"description": "Triggers when an alarm is armed vacation.",
|
||||
"description_configured": "[%key:component::alarm_control_panel::triggers::armed_vacation::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::alarm_control_panel::common::trigger_behavior_description%]",
|
||||
@@ -210,6 +215,7 @@
|
||||
},
|
||||
"disarmed": {
|
||||
"description": "Triggers when an alarm is disarmed.",
|
||||
"description_configured": "[%key:component::alarm_control_panel::triggers::disarmed::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::alarm_control_panel::common::trigger_behavior_description%]",
|
||||
@@ -220,6 +226,7 @@
|
||||
},
|
||||
"triggered": {
|
||||
"description": "Triggers when an alarm is triggered.",
|
||||
"description_configured": "[%key:component::alarm_control_panel::triggers::triggered::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::alarm_control_panel::common::trigger_behavior_description%]",
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from aiohttp import CookieJar
|
||||
from pyanglianwater import AnglianWater
|
||||
from pyanglianwater.auth import MSOB2CAuth
|
||||
from pyanglianwater.exceptions import (
|
||||
@@ -19,7 +18,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryError
|
||||
from homeassistant.helpers.aiohttp_client import async_create_clientsession
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import CONF_ACCOUNT_NUMBER, DOMAIN
|
||||
from .coordinator import AnglianWaterConfigEntry, AnglianWaterUpdateCoordinator
|
||||
@@ -34,10 +33,7 @@ async def async_setup_entry(
|
||||
auth = MSOB2CAuth(
|
||||
username=entry.data[CONF_USERNAME],
|
||||
password=entry.data[CONF_PASSWORD],
|
||||
session=async_create_clientsession(
|
||||
hass,
|
||||
cookie_jar=CookieJar(quote_cookie=False),
|
||||
),
|
||||
session=async_get_clientsession(hass),
|
||||
refresh_token=entry.data[CONF_ACCESS_TOKEN],
|
||||
account_number=entry.data[CONF_ACCOUNT_NUMBER],
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"data_description": {
|
||||
"account_number": "Your account number found on your latest bill.",
|
||||
"password": "Your password",
|
||||
"username": "Username or email used to log in to the Anglian Water website."
|
||||
"username": "Username or email used to login to the Anglian Water website."
|
||||
},
|
||||
"description": "Enter your Anglian Water account credentials to connect to Home Assistant."
|
||||
}
|
||||
|
||||
@@ -421,8 +421,6 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
|
||||
)
|
||||
if short_form.search(model_alias):
|
||||
model_alias += "-0"
|
||||
if model_alias.endswith(("haiku", "opus", "sonnet")):
|
||||
model_alias += "-latest"
|
||||
model_options.append(
|
||||
SelectOptionDict(
|
||||
label=model_info.display_name,
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/anthropic",
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"requirements": ["anthropic==0.75.0"]
|
||||
"requirements": ["anthropic==0.73.0"]
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
"triggers": {
|
||||
"idle": {
|
||||
"description": "Triggers when an Assist satellite becomes idle.",
|
||||
"description_configured": "[%key:component::assist_satellite::triggers::idle::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::assist_satellite::common::trigger_behavior_description%]",
|
||||
@@ -123,6 +124,7 @@
|
||||
},
|
||||
"listening": {
|
||||
"description": "Triggers when an Assist satellite starts listening.",
|
||||
"description_configured": "[%key:component::assist_satellite::triggers::listening::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::assist_satellite::common::trigger_behavior_description%]",
|
||||
@@ -133,6 +135,7 @@
|
||||
},
|
||||
"processing": {
|
||||
"description": "Triggers when an Assist satellite is processing.",
|
||||
"description_configured": "[%key:component::assist_satellite::triggers::processing::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::assist_satellite::common::trigger_behavior_description%]",
|
||||
@@ -143,6 +146,7 @@
|
||||
},
|
||||
"responding": {
|
||||
"description": "Triggers when an Assist satellite is responding.",
|
||||
"description_configured": "[%key:component::assist_satellite::triggers::responding::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::assist_satellite::common::trigger_behavior_description%]",
|
||||
|
||||
@@ -68,9 +68,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: BoschAlarmConfigEntry) -
|
||||
config_entry_id=entry.entry_id,
|
||||
connections={(CONNECTION_NETWORK_MAC, mac)} if mac else set(),
|
||||
identifiers={(DOMAIN, entry.unique_id or entry.entry_id)},
|
||||
name=f"Bosch {panel.model.name}",
|
||||
name=f"Bosch {panel.model}",
|
||||
manufacturer="Bosch Security Systems",
|
||||
model=panel.model.name,
|
||||
model=panel.model,
|
||||
sw_version=panel.firmware_version,
|
||||
)
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
@@ -83,7 +83,7 @@ async def try_connect(
|
||||
finally:
|
||||
await panel.disconnect()
|
||||
|
||||
return (panel.model.name, panel.serial_number)
|
||||
return (panel.model, panel.serial_number)
|
||||
|
||||
|
||||
class BoschAlarmConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
@@ -20,8 +20,7 @@ async def async_get_config_entry_diagnostics(
|
||||
return {
|
||||
"entry_data": async_redact_data(entry.data, TO_REDACT),
|
||||
"data": {
|
||||
"model": entry.runtime_data.model.name,
|
||||
"family": entry.runtime_data.model.family.name,
|
||||
"model": entry.runtime_data.model,
|
||||
"serial_number": entry.runtime_data.serial_number,
|
||||
"protocol_version": entry.runtime_data.protocol_version,
|
||||
"firmware_version": entry.runtime_data.firmware_version,
|
||||
|
||||
@@ -26,7 +26,7 @@ class BoschAlarmEntity(Entity):
|
||||
self._attr_should_poll = False
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, unique_id)},
|
||||
name=f"Bosch {panel.model.name}",
|
||||
name=f"Bosch {panel.model}",
|
||||
manufacturer="Bosch Security Systems",
|
||||
)
|
||||
|
||||
|
||||
@@ -12,5 +12,5 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["bosch-alarm-mode2==0.4.10"]
|
||||
"requirements": ["bosch-alarm-mode2==0.4.6"]
|
||||
}
|
||||
|
||||
@@ -300,6 +300,7 @@
|
||||
"triggers": {
|
||||
"started_cooling": {
|
||||
"description": "Triggers when a climate started cooling.",
|
||||
"description_configured": "[%key:component::climate::triggers::started_cooling::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::climate::common::trigger_behavior_description%]",
|
||||
@@ -310,6 +311,7 @@
|
||||
},
|
||||
"started_drying": {
|
||||
"description": "Triggers when a climate started drying.",
|
||||
"description_configured": "[%key:component::climate::triggers::started_drying::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::climate::common::trigger_behavior_description%]",
|
||||
@@ -320,6 +322,7 @@
|
||||
},
|
||||
"started_heating": {
|
||||
"description": "Triggers when a climate starts to heat.",
|
||||
"description_configured": "[%key:component::climate::triggers::started_heating::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::climate::common::trigger_behavior_description%]",
|
||||
@@ -330,6 +333,7 @@
|
||||
},
|
||||
"turned_off": {
|
||||
"description": "Triggers when a climate is turned off.",
|
||||
"description_configured": "[%key:component::climate::triggers::turned_off::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::climate::common::trigger_behavior_description%]",
|
||||
@@ -340,6 +344,7 @@
|
||||
},
|
||||
"turned_on": {
|
||||
"description": "Triggers when a climate is turned on.",
|
||||
"description_configured": "[%key:component::climate::triggers::turned_on::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::climate::common::trigger_behavior_description%]",
|
||||
|
||||
@@ -108,5 +108,34 @@
|
||||
"toggle_cover_tilt": {
|
||||
"service": "mdi:arrow-top-right-bottom-left"
|
||||
}
|
||||
},
|
||||
"triggers": {
|
||||
"awning_opened": {
|
||||
"trigger": "mdi:awning-outline"
|
||||
},
|
||||
"blind_opened": {
|
||||
"trigger": "mdi:blinds-horizontal"
|
||||
},
|
||||
"curtain_opened": {
|
||||
"trigger": "mdi:curtains"
|
||||
},
|
||||
"door_opened": {
|
||||
"trigger": "mdi:door-open"
|
||||
},
|
||||
"garage_opened": {
|
||||
"trigger": "mdi:garage-open"
|
||||
},
|
||||
"gate_opened": {
|
||||
"trigger": "mdi:gate-open"
|
||||
},
|
||||
"shade_opened": {
|
||||
"trigger": "mdi:roller-shade"
|
||||
},
|
||||
"shutter_opened": {
|
||||
"trigger": "mdi:window-shutter-open"
|
||||
},
|
||||
"window_opened": {
|
||||
"trigger": "mdi:window-open"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
{
|
||||
"common": {
|
||||
"trigger_behavior_description_awning": "The behavior of the targeted awnings to trigger on.",
|
||||
"trigger_behavior_description_blind": "The behavior of the targeted blinds to trigger on.",
|
||||
"trigger_behavior_description_curtain": "The behavior of the targeted curtains to trigger on.",
|
||||
"trigger_behavior_description_door": "The behavior of the targeted doors to trigger on.",
|
||||
"trigger_behavior_description_garage": "The behavior of the targeted garage doors to trigger on.",
|
||||
"trigger_behavior_description_gate": "The behavior of the targeted gates to trigger on.",
|
||||
"trigger_behavior_description_shade": "The behavior of the targeted shades to trigger on.",
|
||||
"trigger_behavior_description_shutter": "The behavior of the targeted shutters to trigger on.",
|
||||
"trigger_behavior_description_window": "The behavior of the targeted windows to trigger on.",
|
||||
"trigger_behavior_name": "Behavior"
|
||||
},
|
||||
"device_automation": {
|
||||
"action_type": {
|
||||
"close": "Close {entity_name}",
|
||||
@@ -82,6 +94,15 @@
|
||||
"name": "Window"
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
"trigger_behavior": {
|
||||
"options": {
|
||||
"any": "Any",
|
||||
"first": "First",
|
||||
"last": "Last"
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"close_cover": {
|
||||
"description": "Closes a cover.",
|
||||
@@ -136,5 +157,142 @@
|
||||
"name": "Toggle tilt"
|
||||
}
|
||||
},
|
||||
"title": "Cover"
|
||||
"title": "Cover",
|
||||
"triggers": {
|
||||
"awning_opened": {
|
||||
"description": "Triggers when an awning opens.",
|
||||
"description_configured": "[%key:component::cover::triggers::awning_opened::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::cover::common::trigger_behavior_description_awning%]",
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"fully_opened": {
|
||||
"description": "Require the awnings to be fully opened before triggering.",
|
||||
"name": "Fully opened"
|
||||
}
|
||||
},
|
||||
"name": "When an awning opens"
|
||||
},
|
||||
"blind_opened": {
|
||||
"description": "Triggers when a blind opens.",
|
||||
"description_configured": "[%key:component::cover::triggers::blind_opened::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::cover::common::trigger_behavior_description_blind%]",
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"fully_opened": {
|
||||
"description": "Require the blinds to be fully opened before triggering.",
|
||||
"name": "Fully opened"
|
||||
}
|
||||
},
|
||||
"name": "When a blind opens"
|
||||
},
|
||||
"curtain_opened": {
|
||||
"description": "Triggers when a curtain opens.",
|
||||
"description_configured": "[%key:component::cover::triggers::curtain_opened::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::cover::common::trigger_behavior_description_curtain%]",
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"fully_opened": {
|
||||
"description": "Require the curtains to be fully opened before triggering.",
|
||||
"name": "Fully opened"
|
||||
}
|
||||
},
|
||||
"name": "When a curtain opens"
|
||||
},
|
||||
"door_opened": {
|
||||
"description": "Triggers when a door opens.",
|
||||
"description_configured": "[%key:component::cover::triggers::door_opened::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::cover::common::trigger_behavior_description_door%]",
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"fully_opened": {
|
||||
"description": "Require the doors to be fully opened before triggering.",
|
||||
"name": "Fully opened"
|
||||
}
|
||||
},
|
||||
"name": "When a door opens"
|
||||
},
|
||||
"garage_opened": {
|
||||
"description": "Triggers when a garage door opens.",
|
||||
"description_configured": "[%key:component::cover::triggers::garage_opened::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::cover::common::trigger_behavior_description_garage%]",
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"fully_opened": {
|
||||
"description": "Require the garage doors to be fully opened before triggering.",
|
||||
"name": "Fully opened"
|
||||
}
|
||||
},
|
||||
"name": "When a garage door opens"
|
||||
},
|
||||
"gate_opened": {
|
||||
"description": "Triggers when a gate opens.",
|
||||
"description_configured": "[%key:component::cover::triggers::gate_opened::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::cover::common::trigger_behavior_description_gate%]",
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"fully_opened": {
|
||||
"description": "Require the gates to be fully opened before triggering.",
|
||||
"name": "Fully opened"
|
||||
}
|
||||
},
|
||||
"name": "When a gate opens"
|
||||
},
|
||||
"shade_opened": {
|
||||
"description": "Triggers when a shade opens.",
|
||||
"description_configured": "[%key:component::cover::triggers::shade_opened::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::cover::common::trigger_behavior_description_shade%]",
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"fully_opened": {
|
||||
"description": "Require the shades to be fully opened before triggering.",
|
||||
"name": "Fully opened"
|
||||
}
|
||||
},
|
||||
"name": "When a shade opens"
|
||||
},
|
||||
"shutter_opened": {
|
||||
"description": "Triggers when a shutter opens.",
|
||||
"description_configured": "[%key:component::cover::triggers::shutter_opened::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::cover::common::trigger_behavior_description_shutter%]",
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"fully_opened": {
|
||||
"description": "Require the shutters to be fully opened before triggering.",
|
||||
"name": "Fully opened"
|
||||
}
|
||||
},
|
||||
"name": "When a shutter opens"
|
||||
},
|
||||
"window_opened": {
|
||||
"description": "Triggers when a window opens.",
|
||||
"description_configured": "[%key:component::cover::triggers::window_opened::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::cover::common::trigger_behavior_description_window%]",
|
||||
"name": "[%key:component::cover::common::trigger_behavior_name%]"
|
||||
},
|
||||
"fully_opened": {
|
||||
"description": "Require the windows to be fully opened before triggering.",
|
||||
"name": "Fully opened"
|
||||
}
|
||||
},
|
||||
"name": "When a window opens"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
116
homeassistant/components/cover/trigger.py
Normal file
116
homeassistant/components/cover/trigger.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""Provides triggers for covers."""
|
||||
|
||||
from typing import Final
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONF_OPTIONS
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity import get_device_class
|
||||
from homeassistant.helpers.trigger import (
|
||||
ENTITY_STATE_TRIGGER_SCHEMA_FIRST_LAST,
|
||||
EntityTriggerBase,
|
||||
Trigger,
|
||||
TriggerConfig,
|
||||
)
|
||||
from homeassistant.helpers.typing import UNDEFINED, UndefinedType
|
||||
|
||||
from . import ATTR_CURRENT_POSITION, CoverDeviceClass, CoverState
|
||||
from .const import DOMAIN
|
||||
|
||||
ATTR_FULLY_OPENED: Final = "fully_opened"
|
||||
|
||||
COVER_OPENED_TRIGGER_SCHEMA = ENTITY_STATE_TRIGGER_SCHEMA_FIRST_LAST.extend(
|
||||
{
|
||||
vol.Required(CONF_OPTIONS): {
|
||||
vol.Required(ATTR_FULLY_OPENED, default=False): bool,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def get_device_class_or_undefined(
|
||||
hass: HomeAssistant, entity_id: str
|
||||
) -> str | None | UndefinedType:
|
||||
"""Get the device class of an entity or UNDEFINED if not found."""
|
||||
try:
|
||||
return get_device_class(hass, entity_id)
|
||||
except HomeAssistantError:
|
||||
return UNDEFINED
|
||||
|
||||
|
||||
class CoverOpenedClosedTrigger(EntityTriggerBase):
|
||||
"""Class for cover opened and closed triggers."""
|
||||
|
||||
_attribute: str = ATTR_CURRENT_POSITION
|
||||
_attribute_value: int | None = None
|
||||
_device_class: CoverDeviceClass | None
|
||||
_domain: str = DOMAIN
|
||||
_to_states: set[str]
|
||||
|
||||
def is_to_state(self, state: State) -> bool:
|
||||
"""Check if the state matches the target state."""
|
||||
if state.state not in self._to_states:
|
||||
return False
|
||||
if (
|
||||
self._attribute_value is not None
|
||||
and (value := state.attributes.get(self._attribute)) is not None
|
||||
and value != self._attribute_value
|
||||
):
|
||||
return False
|
||||
return True
|
||||
|
||||
def entity_filter(self, entities: set[str]) -> set[str]:
|
||||
"""Filter entities of this domain."""
|
||||
entities = super().entity_filter(entities)
|
||||
return {
|
||||
entity_id
|
||||
for entity_id in entities
|
||||
if get_device_class_or_undefined(self._hass, entity_id)
|
||||
== self._device_class
|
||||
}
|
||||
|
||||
|
||||
class CoverOpenedTrigger(CoverOpenedClosedTrigger):
|
||||
"""Class for cover opened triggers."""
|
||||
|
||||
_schema = COVER_OPENED_TRIGGER_SCHEMA
|
||||
_to_states = {CoverState.OPEN, CoverState.OPENING}
|
||||
|
||||
def __init__(self, hass: HomeAssistant, config: TriggerConfig) -> None:
|
||||
"""Initialize the state trigger."""
|
||||
super().__init__(hass, config)
|
||||
if self._options.get(ATTR_FULLY_OPENED):
|
||||
self._attribute_value = 100
|
||||
|
||||
|
||||
def make_cover_opened_trigger(
|
||||
device_class: CoverDeviceClass | None,
|
||||
) -> type[CoverOpenedTrigger]:
|
||||
"""Create an entity state attribute trigger class."""
|
||||
|
||||
class CustomTrigger(CoverOpenedTrigger):
|
||||
"""Trigger for entity state changes."""
|
||||
|
||||
_device_class = device_class
|
||||
|
||||
return CustomTrigger
|
||||
|
||||
|
||||
TRIGGERS: dict[str, type[Trigger]] = {
|
||||
"awning_opened": make_cover_opened_trigger(CoverDeviceClass.AWNING),
|
||||
"blind_opened": make_cover_opened_trigger(CoverDeviceClass.BLIND),
|
||||
"curtain_opened": make_cover_opened_trigger(CoverDeviceClass.CURTAIN),
|
||||
"door_opened": make_cover_opened_trigger(CoverDeviceClass.DOOR),
|
||||
"garage_opened": make_cover_opened_trigger(CoverDeviceClass.GARAGE),
|
||||
"gate_opened": make_cover_opened_trigger(CoverDeviceClass.GATE),
|
||||
"shade_opened": make_cover_opened_trigger(CoverDeviceClass.SHADE),
|
||||
"shutter_opened": make_cover_opened_trigger(CoverDeviceClass.SHUTTER),
|
||||
"window_opened": make_cover_opened_trigger(CoverDeviceClass.WINDOW),
|
||||
}
|
||||
|
||||
|
||||
async def async_get_triggers(hass: HomeAssistant) -> dict[str, type[Trigger]]:
|
||||
"""Return the triggers for covers."""
|
||||
return TRIGGERS
|
||||
79
homeassistant/components/cover/triggers.yaml
Normal file
79
homeassistant/components/cover/triggers.yaml
Normal file
@@ -0,0 +1,79 @@
|
||||
.trigger_common_fields: &trigger_common_fields
|
||||
behavior:
|
||||
required: true
|
||||
default: any
|
||||
selector:
|
||||
select:
|
||||
translation_key: trigger_behavior
|
||||
options:
|
||||
- first
|
||||
- last
|
||||
- any
|
||||
fully_opened:
|
||||
required: true
|
||||
default: false
|
||||
selector:
|
||||
boolean:
|
||||
|
||||
awning_opened:
|
||||
fields: *trigger_common_fields
|
||||
target:
|
||||
entity:
|
||||
domain: cover
|
||||
device_class: awning
|
||||
|
||||
blind_opened:
|
||||
fields: *trigger_common_fields
|
||||
target:
|
||||
entity:
|
||||
domain: cover
|
||||
device_class: blind
|
||||
|
||||
curtain_opened:
|
||||
fields: *trigger_common_fields
|
||||
target:
|
||||
entity:
|
||||
domain: cover
|
||||
device_class: curtain
|
||||
|
||||
door_opened:
|
||||
fields: *trigger_common_fields
|
||||
target:
|
||||
entity:
|
||||
domain: cover
|
||||
device_class: door
|
||||
|
||||
garage_opened:
|
||||
fields: *trigger_common_fields
|
||||
target:
|
||||
entity:
|
||||
domain: cover
|
||||
device_class: garage
|
||||
|
||||
gate_opened:
|
||||
fields: *trigger_common_fields
|
||||
target:
|
||||
entity:
|
||||
domain: cover
|
||||
device_class: gate
|
||||
|
||||
shade_opened:
|
||||
fields: *trigger_common_fields
|
||||
target:
|
||||
entity:
|
||||
domain: cover
|
||||
device_class: shade
|
||||
|
||||
shutter_opened:
|
||||
fields: *trigger_common_fields
|
||||
target:
|
||||
entity:
|
||||
domain: cover
|
||||
device_class: shutter
|
||||
|
||||
window_opened:
|
||||
fields: *trigger_common_fields
|
||||
target:
|
||||
entity:
|
||||
domain: cover
|
||||
device_class: window
|
||||
@@ -17,7 +17,7 @@ DEFAULT_NEW_CONFIG_ALLOW_ALLOW_SERVICE_CALLS = False
|
||||
|
||||
DEFAULT_PORT: Final = 6053
|
||||
|
||||
STABLE_BLE_VERSION_STR = "2025.11.0"
|
||||
STABLE_BLE_VERSION_STR = "2025.8.0"
|
||||
STABLE_BLE_VERSION = AwesomeVersion(STABLE_BLE_VERSION_STR)
|
||||
PROJECT_URLS = {
|
||||
"esphome.bluetooth-proxy": "https://esphome.github.io/bluetooth-proxies/",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"mqtt": ["esphome/discover/#"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": [
|
||||
"aioesphomeapi==42.9.0",
|
||||
"aioesphomeapi==42.8.0",
|
||||
"esphome-dashboard-api==1.3.0",
|
||||
"bleak-esphome==3.4.0"
|
||||
],
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
"title": "[%key:component::assist_pipeline::issues::assist_in_progress_deprecated::title%]"
|
||||
},
|
||||
"ble_firmware_outdated": {
|
||||
"description": "ESPHome {version} introduces ultra-low latency event processing, reducing BLE event delays from 0-16 milliseconds to approximately 12 microseconds. This resolves stability issues when pairing, connecting, or handshaking with devices that require low latency, and makes Bluetooth proxy operations rival or exceed local adapters. We highly recommend updating {name} to take advantage of these improvements.",
|
||||
"description": "To improve Bluetooth reliability and performance, we highly recommend updating {name} with ESPHome {version} or later. When updating the device from ESPHome earlier than 2022.12.0, it is recommended to use a serial cable instead of an over-the-air update to take advantage of the new partition scheme.",
|
||||
"title": "Update {name} with ESPHome {version} or later"
|
||||
},
|
||||
"device_conflict": {
|
||||
|
||||
@@ -167,6 +167,7 @@
|
||||
"triggers": {
|
||||
"turned_off": {
|
||||
"description": "Triggers when a fan is turned off.",
|
||||
"description_configured": "[%key:component::fan::triggers::turned_off::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::fan::common::trigger_behavior_description%]",
|
||||
@@ -177,6 +178,7 @@
|
||||
},
|
||||
"turned_on": {
|
||||
"description": "Triggers when a fan is turned on.",
|
||||
"description_configured": "[%key:component::fan::triggers::turned_on::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::fan::common::trigger_behavior_description%]",
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["google_air_quality_api"],
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["google_air_quality_api==1.1.3"]
|
||||
"requirements": ["google_air_quality_api==1.1.2"]
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"not_implemented": "This integration can only be set up via discovery."
|
||||
"not_implemented": "This integration can only be setup via discovery."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"triggers": {
|
||||
"docked": {
|
||||
"description": "Triggers when a lawn mower has docked.",
|
||||
"description_configured": "[%key:component::lawn_mower::triggers::docked::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::lawn_mower::common::trigger_behavior_description%]",
|
||||
@@ -52,6 +53,7 @@
|
||||
},
|
||||
"errored": {
|
||||
"description": "Triggers when a lawn mower has errored.",
|
||||
"description_configured": "[%key:component::lawn_mower::triggers::errored::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::lawn_mower::common::trigger_behavior_description%]",
|
||||
@@ -62,6 +64,7 @@
|
||||
},
|
||||
"paused_mowing": {
|
||||
"description": "Triggers when a lawn mower has paused mowing.",
|
||||
"description_configured": "[%key:component::lawn_mower::triggers::paused_mowing::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::lawn_mower::common::trigger_behavior_description%]",
|
||||
@@ -72,6 +75,7 @@
|
||||
},
|
||||
"started_mowing": {
|
||||
"description": "Triggers when a lawn mower has started mowing.",
|
||||
"description_configured": "[%key:component::lawn_mower::triggers::started_mowing::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::lawn_mower::common::trigger_behavior_description%]",
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/lg_thinq",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["thinqconnect"],
|
||||
"requirements": ["thinqconnect==1.0.9"]
|
||||
"requirements": ["thinqconnect==1.0.8"]
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"conditions": {
|
||||
"is_off": {
|
||||
"description": "Test if a light is off.",
|
||||
"description_configured": "[%key:component::light::conditions::is_off::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::light::common::condition_behavior_description%]",
|
||||
@@ -53,6 +54,7 @@
|
||||
},
|
||||
"is_on": {
|
||||
"description": "Test if a light is on.",
|
||||
"description_configured": "[%key:component::light::conditions::is_on::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::light::common::condition_behavior_description%]",
|
||||
@@ -511,6 +513,7 @@
|
||||
"triggers": {
|
||||
"turned_off": {
|
||||
"description": "Triggers when a light is turned off.",
|
||||
"description_configured": "[%key:component::light::triggers::turned_off::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::light::common::trigger_behavior_description%]",
|
||||
@@ -521,6 +524,7 @@
|
||||
},
|
||||
"turned_on": {
|
||||
"description": "Triggers when a light is turned on.",
|
||||
"description_configured": "[%key:component::light::triggers::turned_on::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::light::common::trigger_behavior_description%]",
|
||||
|
||||
@@ -499,53 +499,4 @@ DISCOVERY_SCHEMAS = [
|
||||
entity_class=MatterBinarySensor,
|
||||
required_attributes=(clusters.WindowCovering.Attributes.ConfigStatus,),
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.BINARY_SENSOR,
|
||||
entity_description=MatterBinarySensorEntityDescription(
|
||||
key="ThermostatRemoteSensing_LocalTemperature",
|
||||
translation_key="thermostat_remote_sensing_local_temperature",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
# LocalTemperature bit from RemoteSensing attribute
|
||||
device_to_ha=lambda x: bool(
|
||||
x
|
||||
& clusters.Thermostat.Bitmaps.RemoteSensingBitmap.kLocalTemperature # Calculated Local Temperature is derived from a remote node
|
||||
),
|
||||
),
|
||||
entity_class=MatterBinarySensor,
|
||||
required_attributes=(clusters.Thermostat.Attributes.RemoteSensing,),
|
||||
allow_multi=True,
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.BINARY_SENSOR,
|
||||
entity_description=MatterBinarySensorEntityDescription(
|
||||
key="ThermostatRemoteSensing_OutdoorTemperature",
|
||||
translation_key="thermostat_remote_sensing_outdoor_temperature",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
# OutdoorTemperature bit from RemoteSensing attribute
|
||||
device_to_ha=lambda x: bool(
|
||||
x
|
||||
& clusters.Thermostat.Bitmaps.RemoteSensingBitmap.kOutdoorTemperature # OutdoorTemperature is derived from a remote node
|
||||
),
|
||||
),
|
||||
entity_class=MatterBinarySensor,
|
||||
required_attributes=(clusters.Thermostat.Attributes.RemoteSensing,),
|
||||
allow_multi=True,
|
||||
),
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.BINARY_SENSOR,
|
||||
entity_description=MatterBinarySensorEntityDescription(
|
||||
key="ThermostatRemoteSensing_Occupancy",
|
||||
translation_key="thermostat_remote_sensing_occupancy",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
# Occupancy bit from RemoteSensing attribute
|
||||
device_to_ha=lambda x: bool(
|
||||
x
|
||||
& clusters.Thermostat.Bitmaps.RemoteSensingBitmap.kOccupancy # Occupancy is derived from a remote node
|
||||
),
|
||||
),
|
||||
entity_class=MatterBinarySensor,
|
||||
required_attributes=(clusters.Thermostat.Attributes.RemoteSensing,),
|
||||
featuremap_contains=clusters.Thermostat.Bitmaps.Feature.kOccupancy,
|
||||
allow_multi=True,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -89,15 +89,6 @@
|
||||
"test_in_progress": {
|
||||
"name": "Test in progress"
|
||||
},
|
||||
"thermostat_remote_sensing_local_temperature": {
|
||||
"name": "Local temperature remote sensing"
|
||||
},
|
||||
"thermostat_remote_sensing_occupancy": {
|
||||
"name": "Occupancy remote sensing"
|
||||
},
|
||||
"thermostat_remote_sensing_outdoor_temperature": {
|
||||
"name": "Outdoor temperature remote sensing"
|
||||
},
|
||||
"valve_fault_blocked": {
|
||||
"name": "Valve blocked"
|
||||
},
|
||||
|
||||
@@ -382,6 +382,7 @@
|
||||
"triggers": {
|
||||
"stopped_playing": {
|
||||
"description": "Triggers when a media player stops playing.",
|
||||
"description_configured": "[%key:component::media_player::triggers::stopped_playing::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::media_player::common::trigger_behavior_description%]",
|
||||
|
||||
@@ -1486,7 +1486,6 @@ class MqttEntity(
|
||||
entity_registry.async_update_entity(
|
||||
self.entity_id, new_entity_id=self._update_registry_entity_id
|
||||
)
|
||||
self._update_registry_entity_id = None
|
||||
|
||||
await super().async_added_to_hass()
|
||||
self._subscriptions = {}
|
||||
|
||||
@@ -729,8 +729,8 @@
|
||||
"data_description": {
|
||||
"payload_reset_percentage": "A special payload that resets the fan speed percentage state attribute to unknown when received at the percentage state topic.",
|
||||
"percentage_command_template": "A [template]({command_templating_url}) to compose the payload to be published at the percentage command topic.",
|
||||
"percentage_command_topic": "The MQTT topic to publish commands to change the fan speed state based on a percentage setting. The value shall be in the range from \"speed range min\" to \"speed range max\". [Learn more.]({url}#percentage_command_topic)",
|
||||
"percentage_state_topic": "The MQTT topic subscribed to receive fan speed state. This is a value in the range from \"speed range min\" to \"speed range max\". [Learn more.]({url}#percentage_state_topic)",
|
||||
"percentage_command_topic": "The MQTT topic to publish commands to change the fan speed state based on a percentage. [Learn more.]({url}#percentage_command_topic)",
|
||||
"percentage_state_topic": "The MQTT topic subscribed to receive fan speed based on percentage. [Learn more.]({url}#percentage_state_topic)",
|
||||
"percentage_value_template": "Defines a [template]({value_templating_url}) to extract the speed percentage value.",
|
||||
"speed_range_max": "The maximum of numeric output range (representing 100 %). The percentage step is 100 / number of speeds within the \"speed range\".",
|
||||
"speed_range_min": "The minimum of numeric output range (off not included, so speed_range_min - 1 represents 0 %). The percentage step is 100 / the number of speeds within the \"speed range\"."
|
||||
@@ -1563,6 +1563,7 @@
|
||||
"triggers": {
|
||||
"_": {
|
||||
"description": "When a specific message is received on a given MQTT topic.",
|
||||
"description_configured": "When an MQTT message has been received",
|
||||
"fields": {
|
||||
"payload": {
|
||||
"description": "The payload to trigger on.",
|
||||
|
||||
@@ -19,5 +19,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/nest",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["google_nest_sdm"],
|
||||
"requirements": ["google-nest-sdm==9.1.1"]
|
||||
"requirements": ["google-nest-sdm==9.1.0"]
|
||||
}
|
||||
|
||||
@@ -2,20 +2,17 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.const import UnitOfTemperature, UnitOfVolume, UnitOfVolumeFlowRate
|
||||
from homeassistant.const import UnitOfTemperature, UnitOfVolumeFlowRate
|
||||
|
||||
DOMAIN = "pooldose"
|
||||
MANUFACTURER = "SEKO"
|
||||
|
||||
# Mapping of device units (upper case) to Home Assistant units
|
||||
# Mapping of device units to Home Assistant units
|
||||
UNIT_MAPPING: dict[str, str] = {
|
||||
# Temperature units
|
||||
"°C": UnitOfTemperature.CELSIUS,
|
||||
"°F": UnitOfTemperature.FAHRENHEIT,
|
||||
# Volume flow rate units
|
||||
"M3/H": UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR,
|
||||
"L/S": UnitOfVolumeFlowRate.LITERS_PER_SECOND,
|
||||
# Volume units
|
||||
"L": UnitOfVolume.LITERS,
|
||||
"M3": UnitOfVolume.CUBIC_METERS,
|
||||
"m3/h": UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR,
|
||||
"L/s": UnitOfVolumeFlowRate.LITERS_PER_SECOND,
|
||||
}
|
||||
|
||||
@@ -119,9 +119,6 @@
|
||||
},
|
||||
"ph_type_dosing": {
|
||||
"default": "mdi:beaker"
|
||||
},
|
||||
"water_meter_total_permanent": {
|
||||
"default": "mdi:counter"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/pooldose",
|
||||
"iot_class": "local_polling",
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["python-pooldose==0.8.1"]
|
||||
"requirements": ["python-pooldose==0.8.0"]
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
@@ -59,13 +58,6 @@ SENSOR_DESCRIPTIONS: tuple[PooldoseSensorEntityDescription, ...] = (
|
||||
device_class=SensorDeviceClass.VOLUME_FLOW_RATE,
|
||||
use_dynamic_unit=True,
|
||||
),
|
||||
PooldoseSensorEntityDescription(
|
||||
key="water_meter_total_permanent",
|
||||
translation_key="water_meter_total_permanent",
|
||||
device_class=SensorDeviceClass.VOLUME,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
use_dynamic_unit=True,
|
||||
),
|
||||
PooldoseSensorEntityDescription(
|
||||
key="ph_type_dosing",
|
||||
translation_key="ph_type_dosing",
|
||||
@@ -231,8 +223,8 @@ class PooldoseSensor(PooldoseEntity, SensorEntity):
|
||||
and (data := self.get_data()) is not None
|
||||
and (device_unit := data.get("unit"))
|
||||
):
|
||||
# Map device unit (upper case) to Home Assistant unit, return None if unknown
|
||||
return UNIT_MAPPING.get(device_unit.upper())
|
||||
# Map device unit to Home Assistant unit, return None if unknown
|
||||
return UNIT_MAPPING.get(device_unit)
|
||||
|
||||
# Fall back to static unit from entity description
|
||||
return super().native_unit_of_measurement
|
||||
|
||||
@@ -160,9 +160,6 @@
|
||||
"acid": "pH-",
|
||||
"alcalyne": "pH+"
|
||||
}
|
||||
},
|
||||
"water_meter_total_permanent": {
|
||||
"name": "Totalizer"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"loggers": ["roborock"],
|
||||
"quality_scale": "silver",
|
||||
"requirements": [
|
||||
"python-roborock==3.8.4",
|
||||
"python-roborock==3.8.1",
|
||||
"vacuum-map-parser-roborock==0.1.4"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ from .entity import (
|
||||
ShellyRpcAttributeEntity,
|
||||
ShellySleepingBlockAttributeEntity,
|
||||
ShellySleepingRpcAttributeEntity,
|
||||
async_setup_entry_block,
|
||||
async_setup_entry_attribute_entities,
|
||||
async_setup_entry_rest,
|
||||
async_setup_entry_rpc,
|
||||
)
|
||||
@@ -127,7 +127,7 @@ class RpcBluTrvBinarySensor(RpcBinarySensor):
|
||||
)
|
||||
|
||||
|
||||
BLOCK_SENSORS: dict[tuple[str, str], BlockBinarySensorDescription] = {
|
||||
SENSORS: dict[tuple[str, str], BlockBinarySensorDescription] = {
|
||||
("device", "overtemp"): BlockBinarySensorDescription(
|
||||
key="device|overtemp",
|
||||
translation_key="overheating",
|
||||
@@ -372,19 +372,19 @@ def _async_setup_block_entry(
|
||||
) -> None:
|
||||
"""Set up entities for BLOCK device."""
|
||||
if config_entry.data[CONF_SLEEP_PERIOD]:
|
||||
async_setup_entry_block(
|
||||
async_setup_entry_attribute_entities(
|
||||
hass,
|
||||
config_entry,
|
||||
async_add_entities,
|
||||
BLOCK_SENSORS,
|
||||
SENSORS,
|
||||
BlockSleepingBinarySensor,
|
||||
)
|
||||
else:
|
||||
async_setup_entry_block(
|
||||
async_setup_entry_attribute_entities(
|
||||
hass,
|
||||
config_entry,
|
||||
async_add_entities,
|
||||
BLOCK_SENSORS,
|
||||
SENSORS,
|
||||
BlockBinarySensor,
|
||||
)
|
||||
async_setup_entry_rest(
|
||||
|
||||
@@ -341,5 +341,3 @@ MODEL_TOP_EV_CHARGER_EVE01 = "EVE01"
|
||||
MODEL_FRANKEVER_IRRIGATION_CONTROLLER = "Irrigation"
|
||||
|
||||
ROLE_GENERIC = "generic"
|
||||
|
||||
TRV_CHANNEL = 0
|
||||
|
||||
@@ -27,7 +27,7 @@ from .entity import (
|
||||
RpcEntityDescription,
|
||||
ShellyBlockAttributeEntity,
|
||||
ShellyRpcAttributeEntity,
|
||||
async_setup_entry_block,
|
||||
async_setup_entry_attribute_entities,
|
||||
async_setup_entry_rpc,
|
||||
rpc_call,
|
||||
)
|
||||
@@ -81,7 +81,7 @@ def _async_setup_block_entry(
|
||||
coordinator = config_entry.runtime_data.block
|
||||
assert coordinator
|
||||
|
||||
async_setup_entry_block(
|
||||
async_setup_entry_attribute_entities(
|
||||
hass, config_entry, async_add_entities, BLOCK_COVERS, BlockShellyCover
|
||||
)
|
||||
|
||||
|
||||
@@ -34,14 +34,14 @@ from .utils import (
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_entry_block(
|
||||
def async_setup_entry_attribute_entities(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ShellyConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
sensors: Mapping[tuple[str, str], BlockEntityDescription],
|
||||
sensor_class: Callable,
|
||||
) -> None:
|
||||
"""Set up block entities."""
|
||||
"""Set up entities for attributes."""
|
||||
coordinator = config_entry.runtime_data.block
|
||||
assert coordinator
|
||||
if coordinator.device.initialized:
|
||||
@@ -150,7 +150,7 @@ def async_setup_entry_rpc(
|
||||
sensors: Mapping[str, RpcEntityDescription],
|
||||
sensor_class: Callable,
|
||||
) -> None:
|
||||
"""Set up RPC entities."""
|
||||
"""Set up entities for RPC sensors."""
|
||||
coordinator = config_entry.runtime_data.rpc
|
||||
assert coordinator
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ from homeassistant.components.event import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import (
|
||||
BASIC_INPUTS_EVENTS_TYPES,
|
||||
@@ -25,7 +26,7 @@ from .const import (
|
||||
SHIX3_1_INPUTS_EVENTS_TYPES,
|
||||
)
|
||||
from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator
|
||||
from .entity import ShellyBlockEntity, ShellyRpcEntity
|
||||
from .entity import ShellyBlockEntity, get_entity_rpc_device_info
|
||||
from .utils import (
|
||||
async_remove_orphaned_entities,
|
||||
async_remove_shelly_entity,
|
||||
@@ -135,7 +136,7 @@ def _async_setup_rpc_entry(
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up entities for RPC device."""
|
||||
entities: list[ShellyRpcEvent | ShellyRpcScriptEvent] = []
|
||||
entities: list[ShellyRpcEvent] = []
|
||||
|
||||
coordinator = config_entry.runtime_data.rpc
|
||||
if TYPE_CHECKING:
|
||||
@@ -161,9 +162,7 @@ def _async_setup_rpc_entry(
|
||||
continue
|
||||
|
||||
if script_events and (event_types := script_events[get_rpc_key_id(script)]):
|
||||
entities.append(
|
||||
ShellyRpcScriptEvent(coordinator, script, SCRIPT_EVENT, event_types)
|
||||
)
|
||||
entities.append(ShellyRpcScriptEvent(coordinator, script, event_types))
|
||||
|
||||
# If a script is removed, from the device configuration, we need to remove orphaned entities
|
||||
async_remove_orphaned_entities(
|
||||
@@ -228,7 +227,7 @@ class ShellyBlockEvent(ShellyBlockEntity, EventEntity):
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
||||
class ShellyRpcEvent(ShellyRpcEntity, EventEntity):
|
||||
class ShellyRpcEvent(CoordinatorEntity[ShellyRpcCoordinator], EventEntity):
|
||||
"""Represent RPC event entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
@@ -241,19 +240,25 @@ class ShellyRpcEvent(ShellyRpcEntity, EventEntity):
|
||||
description: ShellyRpcEventDescription,
|
||||
) -> None:
|
||||
"""Initialize Shelly entity."""
|
||||
super().__init__(coordinator, key)
|
||||
super().__init__(coordinator)
|
||||
self._attr_device_info = get_entity_rpc_device_info(coordinator, key)
|
||||
self._attr_unique_id = f"{coordinator.mac}-{key}"
|
||||
self.entity_description = description
|
||||
|
||||
_, component, component_id = get_rpc_key(key)
|
||||
if custom_name := get_rpc_custom_name(coordinator.device, key):
|
||||
self._attr_name = custom_name
|
||||
else:
|
||||
self._attr_translation_placeholders = {
|
||||
"input_number": component_id
|
||||
if get_rpc_number_of_channels(coordinator.device, component) > 1
|
||||
else ""
|
||||
}
|
||||
self.event_id = int(component_id)
|
||||
if description.key == "input":
|
||||
_, component, component_id = get_rpc_key(key)
|
||||
if custom_name := get_rpc_custom_name(coordinator.device, key):
|
||||
self._attr_name = custom_name
|
||||
else:
|
||||
self._attr_translation_placeholders = {
|
||||
"input_number": component_id
|
||||
if get_rpc_number_of_channels(coordinator.device, component) > 1
|
||||
else ""
|
||||
}
|
||||
self.event_id = int(component_id)
|
||||
elif description.key == "script":
|
||||
self._attr_name = get_rpc_custom_name(coordinator.device, key)
|
||||
self.event_id = get_rpc_key_id(key)
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""When entity is added to hass."""
|
||||
@@ -265,36 +270,30 @@ class ShellyRpcEvent(ShellyRpcEntity, EventEntity):
|
||||
|
||||
@callback
|
||||
def _async_handle_event(self, event: dict[str, Any]) -> None:
|
||||
"""Handle the event."""
|
||||
"""Handle the demo button event."""
|
||||
if event["id"] == self.event_id:
|
||||
self._trigger_event(event["event"])
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
||||
class ShellyRpcScriptEvent(ShellyRpcEntity, EventEntity):
|
||||
class ShellyRpcScriptEvent(ShellyRpcEvent):
|
||||
"""Represent RPC script event entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
entity_description: ShellyRpcEventDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: ShellyRpcCoordinator,
|
||||
key: str,
|
||||
description: ShellyRpcEventDescription,
|
||||
event_types: list[str],
|
||||
) -> None:
|
||||
"""Initialize Shelly script event entity."""
|
||||
super().__init__(coordinator, key)
|
||||
self.entity_description = description
|
||||
self._attr_event_types = event_types
|
||||
super().__init__(coordinator, key, SCRIPT_EVENT)
|
||||
|
||||
self._attr_name = get_rpc_custom_name(coordinator.device, key)
|
||||
self.event_id = get_rpc_key_id(key)
|
||||
self.component = key
|
||||
self._attr_event_types = event_types
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""When entity is added to hass."""
|
||||
await super().async_added_to_hass()
|
||||
await super(CoordinatorEntity, self).async_added_to_hass()
|
||||
|
||||
self.async_on_remove(
|
||||
self.coordinator.async_subscribe_events(self._async_handle_event)
|
||||
@@ -303,7 +302,7 @@ class ShellyRpcScriptEvent(ShellyRpcEntity, EventEntity):
|
||||
@callback
|
||||
def _async_handle_event(self, event: dict[str, Any]) -> None:
|
||||
"""Handle script event."""
|
||||
if event.get("component") == self.key:
|
||||
if event.get("component") == self.component:
|
||||
event_type = event.get("event")
|
||||
if event_type not in self.event_types:
|
||||
# This can happen if we didn't find this event type in the script
|
||||
|
||||
@@ -44,7 +44,7 @@ from .entity import (
|
||||
RpcEntityDescription,
|
||||
ShellyBlockAttributeEntity,
|
||||
ShellyRpcAttributeEntity,
|
||||
async_setup_entry_block,
|
||||
async_setup_entry_attribute_entities,
|
||||
async_setup_entry_rpc,
|
||||
)
|
||||
from .utils import (
|
||||
@@ -101,7 +101,7 @@ def _async_setup_block_entry(
|
||||
coordinator = config_entry.runtime_data.block
|
||||
assert coordinator
|
||||
|
||||
async_setup_entry_block(
|
||||
async_setup_entry_attribute_entities(
|
||||
hass, config_entry, async_add_entities, BLOCK_LIGHTS, BlockShellyLight
|
||||
)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aioshelly"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["aioshelly==13.22.0"],
|
||||
"requirements": ["aioshelly==13.21.0"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"name": "shelly*",
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Final, cast
|
||||
from typing import TYPE_CHECKING, Any, Final, cast
|
||||
|
||||
from aioshelly.block_device import Block
|
||||
from aioshelly.const import RPC_GENERATIONS
|
||||
@@ -34,7 +34,6 @@ from .const import (
|
||||
MODEL_LINKEDGO_ST1820_THERMOSTAT,
|
||||
MODEL_TOP_EV_CHARGER_EVE01,
|
||||
ROLE_GENERIC,
|
||||
TRV_CHANNEL,
|
||||
VIRTUAL_NUMBER_MODE_MAP,
|
||||
)
|
||||
from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator
|
||||
@@ -43,7 +42,7 @@ from .entity import (
|
||||
RpcEntityDescription,
|
||||
ShellyRpcAttributeEntity,
|
||||
ShellySleepingBlockAttributeEntity,
|
||||
async_setup_entry_block,
|
||||
async_setup_entry_attribute_entities,
|
||||
async_setup_entry_rpc,
|
||||
rpc_call,
|
||||
)
|
||||
@@ -63,6 +62,9 @@ PARALLEL_UPDATES = 0
|
||||
class BlockNumberDescription(BlockEntityDescription, NumberEntityDescription):
|
||||
"""Class to describe a BLOCK sensor."""
|
||||
|
||||
rest_path: str = ""
|
||||
rest_arg: str = ""
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class RpcNumberDescription(RpcEntityDescription, NumberEntityDescription):
|
||||
@@ -176,7 +178,7 @@ class RpcBluTrvExtTempNumber(RpcBluTrvNumber):
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
||||
BLOCK_NUMBERS: dict[tuple[str, str], BlockNumberDescription] = {
|
||||
NUMBERS: dict[tuple[str, str], BlockNumberDescription] = {
|
||||
("device", "valvePos"): BlockNumberDescription(
|
||||
key="device|valvepos",
|
||||
translation_key="valve_position",
|
||||
@@ -187,6 +189,8 @@ BLOCK_NUMBERS: dict[tuple[str, str], BlockNumberDescription] = {
|
||||
native_max_value=100,
|
||||
native_step=1,
|
||||
mode=NumberMode.SLIDER,
|
||||
rest_path="thermostat/0",
|
||||
rest_arg="pos",
|
||||
),
|
||||
}
|
||||
|
||||
@@ -349,11 +353,11 @@ def _async_setup_block_entry(
|
||||
) -> None:
|
||||
"""Set up entities for BLOCK device."""
|
||||
if config_entry.data[CONF_SLEEP_PERIOD]:
|
||||
async_setup_entry_block(
|
||||
async_setup_entry_attribute_entities(
|
||||
hass,
|
||||
config_entry,
|
||||
async_add_entities,
|
||||
BLOCK_NUMBERS,
|
||||
NUMBERS,
|
||||
BlockSleepingNumber,
|
||||
)
|
||||
|
||||
@@ -422,11 +426,18 @@ class BlockSleepingNumber(ShellySleepingBlockAttributeEntity, RestoreNumber):
|
||||
|
||||
async def async_set_native_value(self, value: float) -> None:
|
||||
"""Set value."""
|
||||
LOGGER.debug(
|
||||
"Setting thermostat position for entity %s to %s", self.name, value
|
||||
# Example for Shelly Valve: http://192.168.188.187/thermostat/0?pos=13.0
|
||||
await self._set_state_full_path(
|
||||
self.entity_description.rest_path,
|
||||
{self.entity_description.rest_arg: value},
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def _set_state_full_path(self, path: str, params: Any) -> Any:
|
||||
"""Set block state (HTTP request)."""
|
||||
LOGGER.debug("Setting state for entity %s, state: %s", self.name, params)
|
||||
try:
|
||||
await self.coordinator.device.set_thermostat_state(TRV_CHANNEL, pos=value)
|
||||
return await self.coordinator.device.http_request("get", path, params)
|
||||
except DeviceConnectionError as err:
|
||||
self.coordinator.last_update_success = False
|
||||
raise HomeAssistantError(
|
||||
@@ -439,4 +450,3 @@ class BlockSleepingNumber(ShellySleepingBlockAttributeEntity, RestoreNumber):
|
||||
) from err
|
||||
except InvalidAuthError:
|
||||
await self.coordinator.async_shutdown_device_and_start_reauth()
|
||||
self.async_write_ha_state()
|
||||
|
||||
@@ -53,7 +53,7 @@ from .entity import (
|
||||
ShellyRpcAttributeEntity,
|
||||
ShellySleepingBlockAttributeEntity,
|
||||
ShellySleepingRpcAttributeEntity,
|
||||
async_setup_entry_block,
|
||||
async_setup_entry_attribute_entities,
|
||||
async_setup_entry_rest,
|
||||
async_setup_entry_rpc,
|
||||
get_entity_rpc_device_info,
|
||||
@@ -198,7 +198,7 @@ class RpcBluTrvSensor(RpcSensor):
|
||||
)
|
||||
|
||||
|
||||
BLOCK_SENSORS: dict[tuple[str, str], BlockSensorDescription] = {
|
||||
SENSORS: dict[tuple[str, str], BlockSensorDescription] = {
|
||||
("device", "battery"): BlockSensorDescription(
|
||||
key="device|battery",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
@@ -525,6 +525,7 @@ RPC_SENSORS: Final = {
|
||||
"power_rgbcct": RpcSensorDescription(
|
||||
key="rgbcct",
|
||||
sub_key="apower",
|
||||
name="Power",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -962,6 +963,7 @@ RPC_SENSORS: Final = {
|
||||
"energy_rgbcct": RpcSensorDescription(
|
||||
key="rgbcct",
|
||||
sub_key="aenergy",
|
||||
name="Energy",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
value=lambda status, _: status["total"],
|
||||
@@ -1734,19 +1736,19 @@ def _async_setup_block_entry(
|
||||
) -> None:
|
||||
"""Set up entities for BLOCK device."""
|
||||
if config_entry.data[CONF_SLEEP_PERIOD]:
|
||||
async_setup_entry_block(
|
||||
async_setup_entry_attribute_entities(
|
||||
hass,
|
||||
config_entry,
|
||||
async_add_entities,
|
||||
BLOCK_SENSORS,
|
||||
SENSORS,
|
||||
BlockSleepingSensor,
|
||||
)
|
||||
else:
|
||||
async_setup_entry_block(
|
||||
async_setup_entry_attribute_entities(
|
||||
hass,
|
||||
config_entry,
|
||||
async_add_entities,
|
||||
BLOCK_SENSORS,
|
||||
SENSORS,
|
||||
BlockSensor,
|
||||
)
|
||||
async_setup_entry_rest(
|
||||
|
||||
@@ -36,7 +36,7 @@ from .entity import (
|
||||
ShellyBlockAttributeEntity,
|
||||
ShellyRpcAttributeEntity,
|
||||
ShellySleepingBlockAttributeEntity,
|
||||
async_setup_entry_block,
|
||||
async_setup_entry_attribute_entities,
|
||||
async_setup_entry_rpc,
|
||||
rpc_call,
|
||||
)
|
||||
@@ -337,11 +337,11 @@ def _async_setup_block_entry(
|
||||
coordinator = config_entry.runtime_data.block
|
||||
assert coordinator
|
||||
|
||||
async_setup_entry_block(
|
||||
async_setup_entry_attribute_entities(
|
||||
hass, config_entry, async_add_entities, BLOCK_RELAY_SWITCHES, BlockRelaySwitch
|
||||
)
|
||||
|
||||
async_setup_entry_block(
|
||||
async_setup_entry_attribute_entities(
|
||||
hass,
|
||||
config_entry,
|
||||
async_add_entities,
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
},
|
||||
"solar_elevation": {
|
||||
"default": "mdi:theme-light-dark"
|
||||
},
|
||||
"solar_rising": {
|
||||
"default": "mdi:sun-clock"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,11 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.issue_registry import (
|
||||
IssueSeverity,
|
||||
async_create_issue,
|
||||
async_delete_issue,
|
||||
)
|
||||
from homeassistant.helpers.typing import StateType
|
||||
|
||||
from .const import DOMAIN, SIGNAL_EVENTS_CHANGED, SIGNAL_POSITION_CHANGED
|
||||
@@ -95,6 +100,13 @@ SENSOR_TYPES: tuple[SunSensorEntityDescription, ...] = (
|
||||
native_unit_of_measurement=DEGREE,
|
||||
signal=SIGNAL_POSITION_CHANGED,
|
||||
),
|
||||
SunSensorEntityDescription(
|
||||
key="solar_rising",
|
||||
translation_key="solar_rising",
|
||||
value_fn=lambda data: data.rising,
|
||||
entity_registry_enabled_default=False,
|
||||
signal=SIGNAL_EVENTS_CHANGED,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -143,6 +155,20 @@ class SunSensor(SensorEntity):
|
||||
"""Register signal listener when added to hass."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
if self.entity_description.key == "solar_rising":
|
||||
async_create_issue(
|
||||
self.hass,
|
||||
DOMAIN,
|
||||
"deprecated_sun_solar_rising",
|
||||
breaks_in_ha_version="2026.1.0",
|
||||
is_fixable=False,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_sun_solar_rising",
|
||||
translation_placeholders={
|
||||
"entity": self.entity_id,
|
||||
},
|
||||
)
|
||||
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
@@ -150,3 +176,9 @@ class SunSensor(SensorEntity):
|
||||
self.async_write_ha_state,
|
||||
)
|
||||
)
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Call when entity will be removed from hass."""
|
||||
await super().async_will_remove_from_hass()
|
||||
if self.entity_description.key == "solar_rising":
|
||||
async_delete_issue(self.hass, DOMAIN, "deprecated_sun_solar_rising")
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
"next_rising": { "name": "Next rising" },
|
||||
"next_setting": { "name": "Next setting" },
|
||||
"solar_azimuth": { "name": "Solar azimuth" },
|
||||
"solar_elevation": { "name": "Solar elevation" }
|
||||
"solar_elevation": { "name": "Solar elevation" },
|
||||
"solar_rising": { "name": "Solar rising" }
|
||||
}
|
||||
},
|
||||
"entity_component": {
|
||||
@@ -36,5 +37,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"deprecated_sun_solar_rising": {
|
||||
"description": "The 'Solar rising' sensor of the Sun integration is being deprecated; an equivalent 'Solar rising' binary sensor has been made available as a replacement. To resolve this issue, disable {entity}.",
|
||||
"title": "Deprecated 'Solar rising' sensor"
|
||||
}
|
||||
},
|
||||
"title": "Sun"
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"triggers": {
|
||||
"changed": {
|
||||
"description": "Triggers when the text changes.",
|
||||
"description_configured": "[%key:component::text::triggers::changed::description%]",
|
||||
"name": "When the text changes"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ from uiprotect.exceptions import BadRequest, ClientError, NotAuthorized
|
||||
# diagnostics module will not be imported in the executor.
|
||||
from uiprotect.test_util.anonymize import anonymize_data # noqa: F401
|
||||
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
from homeassistant.const import CONF_API_KEY, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import (
|
||||
@@ -208,7 +208,7 @@ async def async_remove_config_entry_device(
|
||||
return True
|
||||
|
||||
|
||||
async def async_migrate_entry(hass: HomeAssistant, entry: UFPConfigEntry) -> bool:
|
||||
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Migrate entry."""
|
||||
_LOGGER.debug("Migrating configuration from version %s", entry.version)
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ from .entity import (
|
||||
)
|
||||
|
||||
_KEY_DOOR = "door"
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True, kw_only=True)
|
||||
|
||||
@@ -33,7 +33,6 @@ from .entity import (
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
|
||||
@@ -32,7 +32,6 @@ from .entity import ProtectDeviceEntity
|
||||
from .utils import get_camera_base_name
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
@callback
|
||||
@@ -92,11 +91,7 @@ def _get_camera_channels(
|
||||
|
||||
# no RTSP enabled use first channel with no stream
|
||||
if is_default and not camera.is_third_party_camera:
|
||||
# Only create repair issue if RTSP is not disabled globally
|
||||
if not data.disable_stream:
|
||||
_create_rtsp_repair(hass, entry, data, camera)
|
||||
else:
|
||||
ir.async_delete_issue(hass, DOMAIN, f"rtsp_disabled_{camera.id}")
|
||||
_create_rtsp_repair(hass, entry, data, camera)
|
||||
yield camera, camera.channels[0], True
|
||||
else:
|
||||
ir.async_delete_issue(hass, DOMAIN, f"rtsp_disabled_{camera.id}")
|
||||
|
||||
@@ -16,6 +16,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import (
|
||||
SOURCE_IGNORE,
|
||||
ConfigEntry,
|
||||
ConfigEntryState,
|
||||
ConfigFlow,
|
||||
ConfigFlowResult,
|
||||
@@ -54,7 +55,7 @@ from .const import (
|
||||
MIN_REQUIRED_PROTECT_V,
|
||||
OUTDATED_LOG_MESSAGE,
|
||||
)
|
||||
from .data import UFPConfigEntry, async_last_update_was_successful
|
||||
from .data import async_last_update_was_successful
|
||||
from .discovery import async_start_discovery
|
||||
from .utils import _async_resolve, _async_short_mac, _async_unifi_mac_from_hass
|
||||
|
||||
@@ -79,7 +80,7 @@ def _host_is_direct_connect(host: str) -> bool:
|
||||
|
||||
async def _async_console_is_offline(
|
||||
hass: HomeAssistant,
|
||||
entry: UFPConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
) -> bool:
|
||||
"""Check if a console is offline.
|
||||
|
||||
@@ -223,7 +224,7 @@ class ProtectFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
config_entry: UFPConfigEntry,
|
||||
config_entry: ConfigEntry,
|
||||
) -> OptionsFlowHandler:
|
||||
"""Get the options flow for this handler."""
|
||||
return OptionsFlowHandler()
|
||||
|
||||
@@ -40,8 +40,6 @@ from .data import (
|
||||
)
|
||||
from .entity import EventEntityMixin, ProtectDeviceEntity, ProtectEventMixin
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
# Select best thumbnail
|
||||
# Prefer thumbnails with LPR data, sorted by confidence
|
||||
|
||||
@@ -6,9 +6,8 @@ import logging
|
||||
from typing import Any
|
||||
|
||||
from uiprotect.data import Light, ModelType, ProtectAdoptableDeviceModel
|
||||
from uiprotect.data.devices import LightDeviceSettings
|
||||
|
||||
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
|
||||
from homeassistant.components.light import ColorMode, LightEntity
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
@@ -16,7 +15,6 @@ from .data import ProtectDeviceType, UFPConfigEntry
|
||||
from .entity import ProtectDeviceEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -73,36 +71,10 @@ class ProtectLight(ProtectDeviceEntity, LightEntity):
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the light on."""
|
||||
brightness = kwargs.get(ATTR_BRIGHTNESS)
|
||||
led_level: int | None = None
|
||||
if brightness is not None:
|
||||
led_level = hass_to_unifi_brightness(brightness)
|
||||
_LOGGER.debug(
|
||||
"Turning on light with brightness %s (led_level=%s)",
|
||||
brightness,
|
||||
led_level,
|
||||
)
|
||||
else:
|
||||
_LOGGER.debug("Turning on light")
|
||||
|
||||
await self.device.api.update_light_public(
|
||||
self.device.id,
|
||||
is_light_force_enabled=True,
|
||||
light_device_settings=(
|
||||
LightDeviceSettings(
|
||||
is_indicator_enabled=self.device.light_device_settings.is_indicator_enabled,
|
||||
led_level=led_level,
|
||||
pir_duration=self.device.light_device_settings.pir_duration,
|
||||
pir_sensitivity=self.device.light_device_settings.pir_sensitivity,
|
||||
)
|
||||
if led_level is not None
|
||||
else None
|
||||
),
|
||||
)
|
||||
_LOGGER.debug("Turning on light")
|
||||
await self.device.api.set_light_is_led_force_on(self.device.id, True)
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the light off."""
|
||||
_LOGGER.debug("Turning off light")
|
||||
await self.device.api.update_light_public(
|
||||
self.device.id, is_light_force_enabled=False
|
||||
)
|
||||
await self.device.api.set_light_is_led_force_on(self.device.id, False)
|
||||
|
||||
@@ -20,7 +20,6 @@ from .data import ProtectDeviceType, UFPConfigEntry
|
||||
from .entity import ProtectDeviceEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["uiprotect", "unifi_discovery"],
|
||||
"requirements": ["uiprotect==7.31.0", "unifi-discovery==1.2.0"],
|
||||
"requirements": ["uiprotect==7.29.0", "unifi-discovery==1.2.0"],
|
||||
"ssdp": [
|
||||
{
|
||||
"manufacturer": "Ubiquiti Networks",
|
||||
|
||||
@@ -23,12 +23,10 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .data import ProtectDeviceType, UFPConfigEntry
|
||||
from .entity import ProtectDeviceEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
_SPEAKER_DESCRIPTION = MediaPlayerEntityDescription(
|
||||
key="speaker",
|
||||
@@ -79,13 +77,7 @@ class ProtectMediaPlayer(ProtectDeviceEntity, MediaPlayerEntity):
|
||||
def _async_update_device_from_protect(self, device: ProtectDeviceType) -> None:
|
||||
super()._async_update_device_from_protect(device)
|
||||
updated_device = self.device
|
||||
speaker_settings = updated_device.speaker_settings
|
||||
volume = (
|
||||
speaker_settings.speaker_volume
|
||||
if speaker_settings.speaker_volume is not None
|
||||
else speaker_settings.volume
|
||||
)
|
||||
self._attr_volume_level = float(volume / 100)
|
||||
self._attr_volume_level = float(updated_device.speaker_settings.volume / 100)
|
||||
|
||||
if (
|
||||
updated_device.talkback_stream is not None
|
||||
@@ -130,10 +122,7 @@ class ProtectMediaPlayer(ProtectDeviceEntity, MediaPlayerEntity):
|
||||
media_id = async_process_play_media_url(self.hass, play_item.url)
|
||||
|
||||
if media_type != MediaType.MUSIC:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="only_music_supported",
|
||||
)
|
||||
raise HomeAssistantError("Only music media type is supported")
|
||||
|
||||
_LOGGER.debug(
|
||||
"Playing Media %s for %s Speaker", media_id, self.device.display_name
|
||||
@@ -142,11 +131,7 @@ class ProtectMediaPlayer(ProtectDeviceEntity, MediaPlayerEntity):
|
||||
try:
|
||||
await self.device.play_audio(media_id, blocking=False)
|
||||
except StreamError as err:
|
||||
_LOGGER.debug("Error playing audio: %s", err)
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="stream_error",
|
||||
) from err
|
||||
raise HomeAssistantError(err) from err
|
||||
|
||||
# update state after starting player
|
||||
self._async_updated_event(self.device)
|
||||
|
||||
@@ -29,8 +29,6 @@ from .entity import (
|
||||
async_all_device_entities,
|
||||
)
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class ProtectNumberEntityDescription(
|
||||
@@ -92,36 +90,6 @@ CAMERA_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = (
|
||||
ufp_set_method="set_mic_volume",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
ProtectNumberEntityDescription(
|
||||
key="system_sounds_volume",
|
||||
translation_key="system_sounds_volume",
|
||||
icon="mdi:volume-high",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
ufp_min=0,
|
||||
ufp_max=100,
|
||||
ufp_step=1,
|
||||
ufp_required_field="feature_flags.has_speaker",
|
||||
ufp_value="speaker_settings.volume",
|
||||
ufp_enabled="feature_flags.has_speaker",
|
||||
ufp_set_method="set_volume",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
ProtectNumberEntityDescription(
|
||||
key="doorbell_ring_volume",
|
||||
translation_key="doorbell_ring_volume",
|
||||
icon="mdi:bell-ring",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
ufp_min=0,
|
||||
ufp_max=100,
|
||||
ufp_step=1,
|
||||
ufp_required_field="feature_flags.is_doorbell",
|
||||
ufp_value="speaker_settings.ring_volume",
|
||||
ufp_enabled="feature_flags.is_doorbell",
|
||||
ufp_set_method="set_ring_volume",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
ProtectNumberEntityDescription(
|
||||
key="zoom_position",
|
||||
translation_key="zoom_level",
|
||||
|
||||
@@ -10,6 +10,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant import data_entry_flow
|
||||
from homeassistant.components.repairs import ConfirmRepairFlow, RepairsFlow
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
|
||||
@@ -164,7 +165,7 @@ class RTSPRepair(ProtectRepair):
|
||||
|
||||
@callback
|
||||
def _async_get_or_create_api_client(
|
||||
hass: HomeAssistant, entry: UFPConfigEntry
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
) -> ProtectApiClient:
|
||||
"""Get or create an API client."""
|
||||
if data := async_get_data_for_entry_id(hass, entry.entry_id):
|
||||
|
||||
@@ -45,7 +45,6 @@ from .utils import async_get_light_motion_current
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_KEY_LIGHT_MOTION = "light_motion"
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
HDR_MODES = [
|
||||
{"id": "always", "name": "Always On"},
|
||||
|
||||
@@ -55,7 +55,6 @@ from .utils import async_get_light_motion_current
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
OBJECT_TYPE_NONE = "none"
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
|
||||
from pydantic import ValidationError
|
||||
@@ -46,8 +45,6 @@ from .const import (
|
||||
)
|
||||
from .data import async_ufp_instance_for_config_entry_ids
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SERVICE_ADD_DOORBELL_TEXT = "add_doorbell_text"
|
||||
SERVICE_REMOVE_DOORBELL_TEXT = "remove_doorbell_text"
|
||||
SERVICE_SET_PRIVACY_ZONE = "set_privacy_zone"
|
||||
@@ -95,11 +92,7 @@ GET_USER_KEYRING_INFO_SCHEMA = vol.Schema(
|
||||
def _async_get_ufp_instance(hass: HomeAssistant, device_id: str) -> ProtectApiClient:
|
||||
device_registry = dr.async_get(hass)
|
||||
if not (device_entry := device_registry.async_get(device_id)):
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="device_not_found",
|
||||
translation_placeholders={"device_id": device_id},
|
||||
)
|
||||
raise HomeAssistantError(f"No device found for device id: {device_id}")
|
||||
|
||||
if device_entry.via_device_id is not None:
|
||||
return _async_get_ufp_instance(hass, device_entry.via_device_id)
|
||||
@@ -108,11 +101,7 @@ def _async_get_ufp_instance(hass: HomeAssistant, device_id: str) -> ProtectApiCl
|
||||
if ufp_instance := async_ufp_instance_for_config_entry_ids(hass, config_entry_ids):
|
||||
return ufp_instance
|
||||
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="device_not_found",
|
||||
translation_placeholders={"device_id": device_id},
|
||||
)
|
||||
raise HomeAssistantError(f"No device found for device id: {device_id}")
|
||||
|
||||
|
||||
@callback
|
||||
@@ -152,11 +141,7 @@ async def _async_service_call_nvr(
|
||||
*(getattr(i.bootstrap.nvr, method)(*args, **kwargs) for i in instances)
|
||||
)
|
||||
except (ClientError, ValidationError) as err:
|
||||
_LOGGER.debug("Error calling UniFi Protect service: %s", err)
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="service_error",
|
||||
) from err
|
||||
raise HomeAssistantError(str(err)) from err
|
||||
|
||||
|
||||
async def add_doorbell_text(call: ServiceCall) -> None:
|
||||
@@ -185,12 +170,7 @@ async def remove_privacy_zone(call: ServiceCall) -> None:
|
||||
|
||||
if remove_index is None:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="privacy_zone_not_found",
|
||||
translation_placeholders={
|
||||
"zone_name": name,
|
||||
"camera_name": camera.display_name,
|
||||
},
|
||||
f"Could not find privacy zone with name {name} on camera {camera.display_name}."
|
||||
)
|
||||
|
||||
def remove_zone() -> None:
|
||||
@@ -250,10 +230,7 @@ async def get_user_keyring_info(call: ServiceCall) -> ServiceResponse:
|
||||
camera = _async_get_ufp_camera(call)
|
||||
ulp_users = camera.api.bootstrap.ulp_users.as_list()
|
||||
if not ulp_users:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="no_users_found",
|
||||
)
|
||||
raise HomeAssistantError("No users found, please check Protect permissions.")
|
||||
|
||||
user_keyrings: list[JsonValueType] = [
|
||||
{
|
||||
|
||||
@@ -20,9 +20,7 @@
|
||||
"username": "[%key:common::config_flow::data::username%]"
|
||||
},
|
||||
"data_description": {
|
||||
"api_key": "[%key:component::unifiprotect::config::step::user::data_description::api_key%]",
|
||||
"password": "[%key:component::unifiprotect::config::step::user::data_description::password%]",
|
||||
"username": "[%key:component::unifiprotect::config::step::user::data_description::username%]"
|
||||
"api_key": "API key for your local user account."
|
||||
},
|
||||
"description": "Do you want to set up {name} ({ip_address})? You will need a local user created in your UniFi OS Console to log in with. Ubiquiti Cloud users will not work. For more information: {local_user_documentation_url}",
|
||||
"title": "UniFi Protect discovered"
|
||||
@@ -36,11 +34,8 @@
|
||||
"username": "[%key:common::config_flow::data::username%]"
|
||||
},
|
||||
"data_description": {
|
||||
"api_key": "[%key:component::unifiprotect::config::step::user::data_description::api_key%]",
|
||||
"host": "[%key:component::unifiprotect::config::step::user::data_description::host%]",
|
||||
"password": "[%key:component::unifiprotect::config::step::user::data_description::password%]",
|
||||
"port": "[%key:component::unifiprotect::config::step::user::data_description::port%]",
|
||||
"username": "[%key:component::unifiprotect::config::step::user::data_description::username%]"
|
||||
"api_key": "API key for your local user account.",
|
||||
"username": "Username for your local (not cloud) user account."
|
||||
},
|
||||
"description": "Your credentials or API key seem to be missing or invalid. For instructions on how to create a local user or generate a new API key, please refer to the documentation: {local_user_documentation_url}",
|
||||
"title": "UniFi Protect reauth"
|
||||
@@ -56,11 +51,7 @@
|
||||
},
|
||||
"data_description": {
|
||||
"api_key": "API key for your local user account.",
|
||||
"host": "Hostname or IP address of your UniFi Protect device.",
|
||||
"password": "Password for your local user account.",
|
||||
"port": "Port of your UniFi Protect device.",
|
||||
"username": "Username for your local (not cloud) user account.",
|
||||
"verify_ssl": "Verify SSL certificate of the UniFi Protect device."
|
||||
"host": "Hostname or IP address of your UniFi Protect device."
|
||||
},
|
||||
"description": "You will need a local user created in your UniFi OS Console to log in with. Ubiquiti Cloud users will not work. For more information: {local_user_documentation_url}",
|
||||
"title": "UniFi Protect setup"
|
||||
@@ -289,9 +280,6 @@
|
||||
"chime_duration": {
|
||||
"name": "Chime duration"
|
||||
},
|
||||
"doorbell_ring_volume": {
|
||||
"name": "Doorbell ring volume"
|
||||
},
|
||||
"infrared_custom_lux_trigger": {
|
||||
"name": "Infrared custom lux trigger"
|
||||
},
|
||||
@@ -301,9 +289,6 @@
|
||||
"motion_sensitivity": {
|
||||
"name": "Motion sensitivity"
|
||||
},
|
||||
"system_sounds_volume": {
|
||||
"name": "System sounds volume"
|
||||
},
|
||||
"volume": {
|
||||
"name": "[%key:component::sensor::entity_component::volume::name%]"
|
||||
},
|
||||
@@ -582,26 +567,8 @@
|
||||
"api_key_required": {
|
||||
"message": "API key is required. Please reauthenticate this integration to provide an API key."
|
||||
},
|
||||
"device_not_found": {
|
||||
"message": "No device found for device id: {device_id}"
|
||||
},
|
||||
"no_users_found": {
|
||||
"message": "No users found, please check Protect permissions"
|
||||
},
|
||||
"only_music_supported": {
|
||||
"message": "Only music media type is supported"
|
||||
},
|
||||
"privacy_zone_not_found": {
|
||||
"message": "Could not find privacy zone with name {zone_name} on camera {camera_name}"
|
||||
},
|
||||
"protect_version": {
|
||||
"message": "Your UniFi Protect version ({current_version}) is too old. Minimum required: {min_version}"
|
||||
},
|
||||
"service_error": {
|
||||
"message": "Error calling UniFi Protect service, check the logs for more details"
|
||||
},
|
||||
"stream_error": {
|
||||
"message": "Error playing audio, check the logs for more details"
|
||||
"message": "Your UniFi Protect version ({current_version}) is too old. Minimum required: {min_version}."
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
@@ -660,12 +627,6 @@
|
||||
"max_media": "Max number of event to load for Media Browser (increases RAM usage)",
|
||||
"override_connection_host": "Override connection host"
|
||||
},
|
||||
"data_description": {
|
||||
"all_updates": "Enable realtime metrics updates. Only use if you have enabled diagnostic sensors and want them updated in realtime.",
|
||||
"disable_rtsp": "Disable the RTSP stream for all cameras. Use this if you don't need live video feeds.",
|
||||
"max_media": "Maximum number of events to load in the Media Browser. Higher values use more RAM.",
|
||||
"override_connection_host": "Override the connection host for the UniFi Protect device."
|
||||
},
|
||||
"description": "Realtime metrics option should only be enabled if you have enabled the diagnostics sensors and want them updated in realtime. If not enabled, they will only update once every 15 minutes.",
|
||||
"title": "UniFi Protect options"
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ from .entity import (
|
||||
|
||||
ATTR_PREV_MIC = "prev_mic_level"
|
||||
ATTR_PREV_RECORD = "prev_record_mode"
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
|
||||
@@ -27,8 +27,6 @@ from .entity import (
|
||||
async_all_device_entities,
|
||||
)
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class ProtectTextEntityDescription(ProtectSetableKeysMixin[T], TextEntityDescription):
|
||||
|
||||
@@ -114,6 +114,7 @@
|
||||
"triggers": {
|
||||
"docked": {
|
||||
"description": "Triggers when a vacuum cleaner has docked.",
|
||||
"description_configured": "[%key:component::vacuum::triggers::docked::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::vacuum::common::trigger_behavior_description%]",
|
||||
@@ -124,6 +125,7 @@
|
||||
},
|
||||
"errored": {
|
||||
"description": "Triggers when a vacuum cleaner has errored.",
|
||||
"description_configured": "[%key:component::vacuum::triggers::errored::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::vacuum::common::trigger_behavior_description%]",
|
||||
@@ -134,6 +136,7 @@
|
||||
},
|
||||
"paused_cleaning": {
|
||||
"description": "Triggers when a vacuum cleaner has paused cleaning.",
|
||||
"description_configured": "[%key:component::vacuum::triggers::paused_cleaning::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::vacuum::common::trigger_behavior_description%]",
|
||||
@@ -144,6 +147,7 @@
|
||||
},
|
||||
"started_cleaning": {
|
||||
"description": "Triggers when a vacuum cleaner has started cleaning.",
|
||||
"description_configured": "[%key:component::vacuum::triggers::started_cleaning::description%]",
|
||||
"fields": {
|
||||
"behavior": {
|
||||
"description": "[%key:component::vacuum::common::trigger_behavior_description%]",
|
||||
|
||||
@@ -13,5 +13,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/vesync",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pyvesync"],
|
||||
"requirements": ["pyvesync==3.3.2"]
|
||||
"requirements": ["pyvesync==3.2.2"]
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ from homeassistant.core import (
|
||||
ServiceResponse,
|
||||
SupportsResponse,
|
||||
)
|
||||
from homeassistant.helpers.httpx_client import get_async_client
|
||||
from homeassistant.helpers.location import find_coordinates
|
||||
from homeassistant.helpers.selector import (
|
||||
BooleanSelector,
|
||||
@@ -47,6 +46,7 @@ from .const import (
|
||||
VEHICLE_TYPES,
|
||||
)
|
||||
from .coordinator import WazeTravelTimeCoordinator, async_get_travel_times
|
||||
from .httpx_client import create_httpx_client
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
@@ -106,7 +106,8 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
if SEMAPHORE not in hass.data.setdefault(DOMAIN, {}):
|
||||
hass.data.setdefault(DOMAIN, {})[SEMAPHORE] = asyncio.Semaphore(1)
|
||||
|
||||
httpx_client = get_async_client(hass)
|
||||
httpx_client = await create_httpx_client(hass)
|
||||
|
||||
client = WazeRouteCalculator(
|
||||
region=config_entry.data[CONF_REGION].upper(), client=httpx_client
|
||||
)
|
||||
@@ -119,7 +120,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
|
||||
async def async_get_travel_times_service(service: ServiceCall) -> ServiceResponse:
|
||||
httpx_client = get_async_client(hass)
|
||||
httpx_client = await create_httpx_client(hass)
|
||||
client = WazeRouteCalculator(
|
||||
region=service.data[CONF_REGION].upper(), client=httpx_client
|
||||
)
|
||||
|
||||
26
homeassistant/components/waze_travel_time/httpx_client.py
Normal file
26
homeassistant/components/waze_travel_time/httpx_client.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""Special httpx client for Waze Travel Time integration."""
|
||||
|
||||
import httpx
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.httpx_client import create_async_httpx_client
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
DATA_HTTPX_ASYNC_CLIENT: HassKey[httpx.AsyncClient] = HassKey("httpx_async_client")
|
||||
|
||||
|
||||
def create_transport() -> httpx.AsyncHTTPTransport:
|
||||
"""Create a httpx transport which enforces the use of IPv4."""
|
||||
return httpx.AsyncHTTPTransport(local_address="0.0.0.0")
|
||||
|
||||
|
||||
async def create_httpx_client(hass: HomeAssistant) -> httpx.AsyncClient:
|
||||
"""Create a httpx client which enforces the use of IPv4."""
|
||||
if (client := hass.data[DOMAIN].get(DATA_HTTPX_ASYNC_CLIENT)) is None:
|
||||
transport = await hass.async_add_executor_job(create_transport)
|
||||
client = hass.data[DOMAIN][DATA_HTTPX_ASYNC_CLIENT] = create_async_httpx_client(
|
||||
hass, transport=transport
|
||||
)
|
||||
return client
|
||||
@@ -24,9 +24,14 @@ from homeassistant.helpers.trigger import (
|
||||
async_get_all_descriptions as async_get_all_trigger_descriptions,
|
||||
)
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
FLAT_SERVICE_DESCRIPTIONS_CACHE: HassKey[
|
||||
tuple[dict[str, dict[str, Any]], dict[str, dict[str, Any] | None]]
|
||||
] = HassKey("websocket_automation_flat_service_description_cache")
|
||||
|
||||
|
||||
@dataclass(slots=True, kw_only=True)
|
||||
class _EntityFilter:
|
||||
@@ -217,12 +222,29 @@ async def async_get_services_for_target(
|
||||
) -> set[str]:
|
||||
"""Get services for a target."""
|
||||
descriptions = await async_get_all_service_descriptions(hass)
|
||||
# Flatten dicts to be keyed by domain.name to match trigger/condition format
|
||||
descriptions_flatten = {
|
||||
f"{domain}.{service_name}": desc
|
||||
for domain, services in descriptions.items()
|
||||
for service_name, desc in services.items()
|
||||
}
|
||||
|
||||
def get_flattened_service_descriptions() -> dict[str, dict[str, Any] | None]:
|
||||
"""Get flattened service descriptions, with caching."""
|
||||
if FLAT_SERVICE_DESCRIPTIONS_CACHE in hass.data:
|
||||
cached_descriptions, cached_flat_descriptions = hass.data[
|
||||
FLAT_SERVICE_DESCRIPTIONS_CACHE
|
||||
]
|
||||
# If the descriptions are the same, return the cached flattened version
|
||||
if cached_descriptions is descriptions:
|
||||
return cached_flat_descriptions
|
||||
|
||||
# Flatten dicts to be keyed by domain.name to match trigger/condition format
|
||||
flat_descriptions = {
|
||||
f"{domain}.{service_name}": desc
|
||||
for domain, services in descriptions.items()
|
||||
for service_name, desc in services.items()
|
||||
}
|
||||
hass.data[FLAT_SERVICE_DESCRIPTIONS_CACHE] = (
|
||||
descriptions,
|
||||
flat_descriptions,
|
||||
)
|
||||
return flat_descriptions
|
||||
|
||||
return _async_get_automation_components_for_target(
|
||||
hass, target_selector, expand_group, descriptions_flatten
|
||||
hass, target_selector, expand_group, get_flattened_service_descriptions()
|
||||
)
|
||||
|
||||
@@ -1553,9 +1553,7 @@ class Entity(
|
||||
# Clear the remove future to handle entity added again after entity id change
|
||||
self.__remove_future = None
|
||||
self._platform_state = EntityPlatformState.NOT_ADDED
|
||||
await self.platform.async_add_entities(
|
||||
[self], config_subentry_id=registry_entry.config_subentry_id
|
||||
)
|
||||
await self.platform.async_add_entities([self])
|
||||
|
||||
@callback
|
||||
def _async_unsubscribe_device_updates(self) -> None:
|
||||
|
||||
22
requirements_all.txt
generated
22
requirements_all.txt
generated
@@ -252,7 +252,7 @@ aioelectricitymaps==1.1.1
|
||||
aioemonitor==1.0.5
|
||||
|
||||
# homeassistant.components.esphome
|
||||
aioesphomeapi==42.9.0
|
||||
aioesphomeapi==42.8.0
|
||||
|
||||
# homeassistant.components.matrix
|
||||
# homeassistant.components.slack
|
||||
@@ -393,7 +393,7 @@ aioruuvigateway==0.1.0
|
||||
aiosenz==1.0.0
|
||||
|
||||
# homeassistant.components.shelly
|
||||
aioshelly==13.22.0
|
||||
aioshelly==13.21.0
|
||||
|
||||
# homeassistant.components.skybell
|
||||
aioskybell==22.7.0
|
||||
@@ -504,7 +504,7 @@ anova-wifi==0.17.0
|
||||
anthemav==1.4.1
|
||||
|
||||
# homeassistant.components.anthropic
|
||||
anthropic==0.75.0
|
||||
anthropic==0.73.0
|
||||
|
||||
# homeassistant.components.mcp_server
|
||||
anyio==4.10.0
|
||||
@@ -675,7 +675,7 @@ bluetooth-data-tools==1.28.4
|
||||
bond-async==0.2.1
|
||||
|
||||
# homeassistant.components.bosch_alarm
|
||||
bosch-alarm-mode2==0.4.10
|
||||
bosch-alarm-mode2==0.4.6
|
||||
|
||||
# homeassistant.components.bosch_shc
|
||||
boschshcpy==0.2.107
|
||||
@@ -1087,13 +1087,13 @@ google-genai==1.38.0
|
||||
google-maps-routing==0.6.15
|
||||
|
||||
# homeassistant.components.nest
|
||||
google-nest-sdm==9.1.1
|
||||
google-nest-sdm==9.1.0
|
||||
|
||||
# homeassistant.components.google_photos
|
||||
google-photos-library-api==0.12.1
|
||||
|
||||
# homeassistant.components.google_air_quality
|
||||
google_air_quality_api==1.1.3
|
||||
google_air_quality_api==1.1.2
|
||||
|
||||
# homeassistant.components.slide
|
||||
# homeassistant.components.slide_local
|
||||
@@ -2551,7 +2551,7 @@ python-overseerr==0.7.1
|
||||
python-picnic-api2==1.3.1
|
||||
|
||||
# homeassistant.components.pooldose
|
||||
python-pooldose==0.8.1
|
||||
python-pooldose==0.8.0
|
||||
|
||||
# homeassistant.components.rabbitair
|
||||
python-rabbitair==0.0.8
|
||||
@@ -2560,7 +2560,7 @@ python-rabbitair==0.0.8
|
||||
python-ripple-api==0.0.3
|
||||
|
||||
# homeassistant.components.roborock
|
||||
python-roborock==3.8.4
|
||||
python-roborock==3.8.1
|
||||
|
||||
# homeassistant.components.smarttub
|
||||
python-smarttub==0.0.45
|
||||
@@ -2630,7 +2630,7 @@ pyvera==0.3.16
|
||||
pyversasense==0.0.6
|
||||
|
||||
# homeassistant.components.vesync
|
||||
pyvesync==3.3.2
|
||||
pyvesync==3.2.2
|
||||
|
||||
# homeassistant.components.vizio
|
||||
pyvizio==0.1.61
|
||||
@@ -2987,7 +2987,7 @@ thermopro-ble==1.1.2
|
||||
thingspeak==1.0.0
|
||||
|
||||
# homeassistant.components.lg_thinq
|
||||
thinqconnect==1.0.9
|
||||
thinqconnect==1.0.8
|
||||
|
||||
# homeassistant.components.tikteck
|
||||
tikteck==0.4
|
||||
@@ -3053,7 +3053,7 @@ typedmonarchmoney==0.4.4
|
||||
uasiren==0.0.1
|
||||
|
||||
# homeassistant.components.unifiprotect
|
||||
uiprotect==7.31.0
|
||||
uiprotect==7.29.0
|
||||
|
||||
# homeassistant.components.landisgyr_heat_meter
|
||||
ultraheat-api==0.5.7
|
||||
|
||||
22
requirements_test_all.txt
generated
22
requirements_test_all.txt
generated
@@ -243,7 +243,7 @@ aioelectricitymaps==1.1.1
|
||||
aioemonitor==1.0.5
|
||||
|
||||
# homeassistant.components.esphome
|
||||
aioesphomeapi==42.9.0
|
||||
aioesphomeapi==42.8.0
|
||||
|
||||
# homeassistant.components.matrix
|
||||
# homeassistant.components.slack
|
||||
@@ -378,7 +378,7 @@ aioruuvigateway==0.1.0
|
||||
aiosenz==1.0.0
|
||||
|
||||
# homeassistant.components.shelly
|
||||
aioshelly==13.22.0
|
||||
aioshelly==13.21.0
|
||||
|
||||
# homeassistant.components.skybell
|
||||
aioskybell==22.7.0
|
||||
@@ -480,7 +480,7 @@ anova-wifi==0.17.0
|
||||
anthemav==1.4.1
|
||||
|
||||
# homeassistant.components.anthropic
|
||||
anthropic==0.75.0
|
||||
anthropic==0.73.0
|
||||
|
||||
# homeassistant.components.mcp_server
|
||||
anyio==4.10.0
|
||||
@@ -609,7 +609,7 @@ bluetooth-data-tools==1.28.4
|
||||
bond-async==0.2.1
|
||||
|
||||
# homeassistant.components.bosch_alarm
|
||||
bosch-alarm-mode2==0.4.10
|
||||
bosch-alarm-mode2==0.4.6
|
||||
|
||||
# homeassistant.components.bosch_shc
|
||||
boschshcpy==0.2.107
|
||||
@@ -963,13 +963,13 @@ google-genai==1.38.0
|
||||
google-maps-routing==0.6.15
|
||||
|
||||
# homeassistant.components.nest
|
||||
google-nest-sdm==9.1.1
|
||||
google-nest-sdm==9.1.0
|
||||
|
||||
# homeassistant.components.google_photos
|
||||
google-photos-library-api==0.12.1
|
||||
|
||||
# homeassistant.components.google_air_quality
|
||||
google_air_quality_api==1.1.3
|
||||
google_air_quality_api==1.1.2
|
||||
|
||||
# homeassistant.components.slide
|
||||
# homeassistant.components.slide_local
|
||||
@@ -2132,13 +2132,13 @@ python-overseerr==0.7.1
|
||||
python-picnic-api2==1.3.1
|
||||
|
||||
# homeassistant.components.pooldose
|
||||
python-pooldose==0.8.1
|
||||
python-pooldose==0.8.0
|
||||
|
||||
# homeassistant.components.rabbitair
|
||||
python-rabbitair==0.0.8
|
||||
|
||||
# homeassistant.components.roborock
|
||||
python-roborock==3.8.4
|
||||
python-roborock==3.8.1
|
||||
|
||||
# homeassistant.components.smarttub
|
||||
python-smarttub==0.0.45
|
||||
@@ -2196,7 +2196,7 @@ pyuptimerobot==22.2.0
|
||||
pyvera==0.3.16
|
||||
|
||||
# homeassistant.components.vesync
|
||||
pyvesync==3.3.2
|
||||
pyvesync==3.2.2
|
||||
|
||||
# homeassistant.components.vizio
|
||||
pyvizio==0.1.61
|
||||
@@ -2481,7 +2481,7 @@ thermobeacon-ble==0.10.0
|
||||
thermopro-ble==1.1.2
|
||||
|
||||
# homeassistant.components.lg_thinq
|
||||
thinqconnect==1.0.9
|
||||
thinqconnect==1.0.8
|
||||
|
||||
# homeassistant.components.tilt_ble
|
||||
tilt-ble==1.0.1
|
||||
@@ -2538,7 +2538,7 @@ typedmonarchmoney==0.4.4
|
||||
uasiren==0.0.1
|
||||
|
||||
# homeassistant.components.unifiprotect
|
||||
uiprotect==7.31.0
|
||||
uiprotect==7.29.0
|
||||
|
||||
# homeassistant.components.landisgyr_heat_meter
|
||||
ultraheat-api==0.5.7
|
||||
|
||||
@@ -475,6 +475,7 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema:
|
||||
{
|
||||
vol.Required("name"): translation_value_validator,
|
||||
vol.Required("description"): translation_value_validator,
|
||||
vol.Required("description_configured"): translation_value_validator,
|
||||
vol.Optional("fields"): cv.schema_with_slug_keys(
|
||||
{
|
||||
vol.Required("name"): str,
|
||||
@@ -490,6 +491,7 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema:
|
||||
{
|
||||
vol.Required("name"): translation_value_validator,
|
||||
vol.Required("description"): translation_value_validator,
|
||||
vol.Required("description_configured"): translation_value_validator,
|
||||
vol.Optional("fields"): cv.schema_with_slug_keys(
|
||||
{
|
||||
vol.Required("name"): str,
|
||||
|
||||
@@ -128,12 +128,6 @@ async def mock_init_component(
|
||||
"""Initialize integration."""
|
||||
model_list = AsyncPage(
|
||||
data=[
|
||||
ModelInfo(
|
||||
id="claude-opus-4-5-20251101",
|
||||
created_at=datetime.datetime(2025, 11, 1, 0, 0, tzinfo=datetime.UTC),
|
||||
display_name="Claude Opus 4.5",
|
||||
type="model",
|
||||
),
|
||||
ModelInfo(
|
||||
id="claude-haiku-4-5-20251001",
|
||||
created_at=datetime.datetime(2025, 10, 15, 0, 0, tzinfo=datetime.UTC),
|
||||
|
||||
@@ -357,10 +357,6 @@ async def test_model_list(
|
||||
assert options["type"] == FlowResultType.FORM
|
||||
assert options["step_id"] == "advanced"
|
||||
assert options["data_schema"].schema["chat_model"].config["options"] == [
|
||||
{
|
||||
"label": "Claude Opus 4.5",
|
||||
"value": "claude-opus-4-5",
|
||||
},
|
||||
{
|
||||
"label": "Claude Haiku 4.5",
|
||||
"value": "claude-haiku-4-5",
|
||||
@@ -383,11 +379,11 @@ async def test_model_list(
|
||||
},
|
||||
{
|
||||
"label": "Claude Sonnet 3.7",
|
||||
"value": "claude-3-7-sonnet-latest",
|
||||
"value": "claude-3-7-sonnet",
|
||||
},
|
||||
{
|
||||
"label": "Claude Haiku 3.5",
|
||||
"value": "claude-3-5-haiku-latest",
|
||||
"value": "claude-3-5-haiku",
|
||||
},
|
||||
{
|
||||
"label": "Claude Haiku 3",
|
||||
|
||||
@@ -4,7 +4,6 @@ from collections.abc import Generator
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from bosch_alarm_mode2.const import PANEL_FAMILY, PanelModel
|
||||
from bosch_alarm_mode2.panel import Area, Door, Output, Point
|
||||
from bosch_alarm_mode2.utils import Observable
|
||||
import pytest
|
||||
@@ -40,10 +39,10 @@ def model(request: pytest.FixtureRequest) -> Generator[str]:
|
||||
|
||||
@pytest.fixture
|
||||
def extra_config_entry_data(
|
||||
model: str, panel_model: PanelModel, config_flow_data: dict[str, Any]
|
||||
model: str, model_name: str, config_flow_data: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
"""Return extra config entry data."""
|
||||
return {CONF_MODEL: panel_model.name} | config_flow_data
|
||||
return {CONF_MODEL: model_name} | config_flow_data
|
||||
|
||||
|
||||
@pytest.fixture(params=[None])
|
||||
@@ -65,12 +64,12 @@ def config_flow_data(model: str) -> dict[str, Any]:
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def panel_model(model: str) -> PanelModel | None:
|
||||
def model_name(model: str) -> str | None:
|
||||
"""Return extra config entry data."""
|
||||
return {
|
||||
"solution_3000": PanelModel("Solution 3000", PANEL_FAMILY.SOLUTION),
|
||||
"amax_3000": PanelModel("AMAX 3000", PANEL_FAMILY.AMAX),
|
||||
"b5512": PanelModel("B5512 (US1B)", PANEL_FAMILY.BG_SERIES),
|
||||
"solution_3000": "Solution 3000",
|
||||
"amax_3000": "AMAX 3000",
|
||||
"b5512": "B5512 (US1B)",
|
||||
}.get(model)
|
||||
|
||||
|
||||
@@ -167,7 +166,7 @@ def mock_panel(
|
||||
door: AsyncMock,
|
||||
output: AsyncMock,
|
||||
points: dict[int, AsyncMock],
|
||||
panel_model: str,
|
||||
model_name: str,
|
||||
serial_number: str | None,
|
||||
) -> Generator[AsyncMock]:
|
||||
"""Define a fixture to set up Bosch Alarm."""
|
||||
@@ -182,7 +181,7 @@ def mock_panel(
|
||||
client.doors = {1: door}
|
||||
client.outputs = {1: output}
|
||||
client.points = points
|
||||
client.model = panel_model
|
||||
client.model = model_name
|
||||
client.faults = []
|
||||
client.events = []
|
||||
client.panel_faults_ids = []
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
'open': False,
|
||||
}),
|
||||
]),
|
||||
'family': 'AMAX',
|
||||
'firmware_version': '1.0.0',
|
||||
'history_events': list([
|
||||
]),
|
||||
@@ -125,7 +124,6 @@
|
||||
'open': False,
|
||||
}),
|
||||
]),
|
||||
'family': 'BG_SERIES',
|
||||
'firmware_version': '1.0.0',
|
||||
'history_events': list([
|
||||
]),
|
||||
@@ -221,7 +219,6 @@
|
||||
'open': False,
|
||||
}),
|
||||
]),
|
||||
'family': 'SOLUTION',
|
||||
'firmware_version': '1.0.0',
|
||||
'history_events': list([
|
||||
]),
|
||||
|
||||
@@ -4,7 +4,6 @@ import asyncio
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from bosch_alarm_mode2.const import PANEL_FAMILY, PanelModel
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.bosch_alarm.const import DOMAIN
|
||||
@@ -23,7 +22,7 @@ async def test_form_user(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_panel: AsyncMock,
|
||||
panel_model: PanelModel,
|
||||
model_name: str,
|
||||
serial_number: str,
|
||||
config_flow_data: dict[str, Any],
|
||||
) -> None:
|
||||
@@ -46,13 +45,13 @@ async def test_form_user(
|
||||
config_flow_data,
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == f"Bosch {panel_model.name}"
|
||||
assert result["title"] == f"Bosch {model_name}"
|
||||
assert (
|
||||
result["data"]
|
||||
== {
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PORT: 7700,
|
||||
CONF_MODEL: panel_model.name,
|
||||
CONF_MODEL: model_name,
|
||||
}
|
||||
| config_flow_data
|
||||
)
|
||||
@@ -212,7 +211,7 @@ async def test_dhcp_can_finish(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_panel: AsyncMock,
|
||||
panel_model: PanelModel,
|
||||
model_name: str,
|
||||
serial_number: str,
|
||||
config_flow_data: dict[str, Any],
|
||||
) -> None:
|
||||
@@ -238,12 +237,12 @@ async def test_dhcp_can_finish(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == f"Bosch {panel_model.name}"
|
||||
assert result["title"] == f"Bosch {model_name}"
|
||||
assert result["data"] == {
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_MAC: "34:ea:34:b4:3b:5a",
|
||||
CONF_PORT: 7700,
|
||||
CONF_MODEL: panel_model.name,
|
||||
CONF_MODEL: model_name,
|
||||
**config_flow_data,
|
||||
}
|
||||
|
||||
@@ -259,7 +258,7 @@ async def test_dhcp_exceptions(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_panel: AsyncMock,
|
||||
panel_model: PanelModel,
|
||||
model_name: str,
|
||||
serial_number: str,
|
||||
config_flow_data: dict[str, Any],
|
||||
exception: Exception,
|
||||
@@ -317,7 +316,7 @@ async def test_dhcp_discovery_if_panel_setup_config_flow(
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_panel: AsyncMock,
|
||||
serial_number: str,
|
||||
panel_model: PanelModel,
|
||||
model_name: str,
|
||||
config_flow_data: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test DHCP discovery doesn't fail if a different panel was set up via config flow."""
|
||||
@@ -347,12 +346,12 @@ async def test_dhcp_discovery_if_panel_setup_config_flow(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == f"Bosch {panel_model.name}"
|
||||
assert result["title"] == f"Bosch {model_name}"
|
||||
assert result["data"] == {
|
||||
CONF_HOST: "4.5.6.7",
|
||||
CONF_MAC: "34:ea:34:b4:3b:5a",
|
||||
CONF_PORT: 7700,
|
||||
CONF_MODEL: panel_model.name,
|
||||
CONF_MODEL: model_name,
|
||||
**config_flow_data,
|
||||
}
|
||||
assert mock_config_entry.unique_id == serial_number
|
||||
@@ -396,7 +395,7 @@ async def test_dhcp_updates_mac(
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_panel: AsyncMock,
|
||||
panel_model: PanelModel,
|
||||
model_name: str,
|
||||
serial_number: str,
|
||||
config_flow_data: dict[str, Any],
|
||||
) -> None:
|
||||
@@ -425,7 +424,7 @@ async def test_reauth_flow_success(
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_panel: AsyncMock,
|
||||
panel_model: PanelModel,
|
||||
model_name: str,
|
||||
serial_number: str,
|
||||
config_flow_data: dict[str, Any],
|
||||
) -> None:
|
||||
@@ -460,7 +459,7 @@ async def test_reauth_flow_error(
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_panel: AsyncMock,
|
||||
panel_model: PanelModel,
|
||||
model_name: str,
|
||||
serial_number: str,
|
||||
config_flow_data: dict[str, Any],
|
||||
exception: Exception,
|
||||
@@ -495,7 +494,7 @@ async def test_reconfig_flow(
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_panel: AsyncMock,
|
||||
panel_model: PanelModel,
|
||||
model_name: str,
|
||||
serial_number: str,
|
||||
config_flow_data: dict[str, Any],
|
||||
) -> None:
|
||||
@@ -530,7 +529,7 @@ async def test_reconfig_flow(
|
||||
assert mock_config_entry.data == {
|
||||
CONF_HOST: "1.1.1.1",
|
||||
CONF_PORT: 7700,
|
||||
CONF_MODEL: panel_model.name,
|
||||
CONF_MODEL: model_name,
|
||||
**config_flow_data,
|
||||
}
|
||||
|
||||
@@ -541,7 +540,7 @@ async def test_reconfig_flow_incorrect_model(
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_panel: AsyncMock,
|
||||
panel_model: PanelModel,
|
||||
model_name: str,
|
||||
serial_number: str,
|
||||
config_flow_data: dict[str, Any],
|
||||
) -> None:
|
||||
@@ -557,7 +556,7 @@ async def test_reconfig_flow_incorrect_model(
|
||||
},
|
||||
)
|
||||
|
||||
mock_panel.model = PanelModel("Solution 3000", family=PANEL_FAMILY.SOLUTION)
|
||||
mock_panel.model = "Solution 3000"
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from bosch_alarm_mode2.const import PanelModel
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -20,7 +19,7 @@ async def test_diagnostics(
|
||||
hass_client: ClientSessionGenerator,
|
||||
mock_panel: AsyncMock,
|
||||
area: AsyncMock,
|
||||
panel_model: PanelModel,
|
||||
model_name: str,
|
||||
serial_number: str,
|
||||
snapshot: SnapshotAssertion,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
|
||||
297
tests/components/cover/test_trigger.py
Normal file
297
tests/components/cover/test_trigger.py
Normal file
@@ -0,0 +1,297 @@
|
||||
"""Test cover trigger."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.cover import ATTR_CURRENT_POSITION, CoverState
|
||||
from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_LABEL_ID, CONF_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.components import (
|
||||
StateDescription,
|
||||
arm_trigger,
|
||||
parametrize_target_entities,
|
||||
parametrize_trigger_states,
|
||||
set_or_remove_state,
|
||||
target_entities,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, name="stub_blueprint_populate")
|
||||
def stub_blueprint_populate_autouse(stub_blueprint_populate: None) -> None:
|
||||
"""Stub copying the blueprints to the config folder."""
|
||||
|
||||
|
||||
@pytest.fixture(name="enable_experimental_triggers_conditions")
|
||||
def enable_experimental_triggers_conditions() -> Generator[None]:
|
||||
"""Enable experimental triggers and conditions."""
|
||||
with patch(
|
||||
"homeassistant.components.labs.async_is_preview_feature_enabled",
|
||||
return_value=True,
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def target_covers(hass: HomeAssistant) -> list[str]:
|
||||
"""Create multiple cover entities associated with different targets."""
|
||||
return await target_entities(hass, "cover")
|
||||
|
||||
|
||||
def parametrize_opened_trigger_states(
|
||||
trigger: str, device_class: str
|
||||
) -> list[tuple[str, dict, str, list[StateDescription]]]:
|
||||
"""Parametrize states and expected service call counts.
|
||||
|
||||
Returns a list of tuples with (trigger, trigger_options,
|
||||
list of StateDescription).
|
||||
"""
|
||||
additional_attributes = {ATTR_DEVICE_CLASS: device_class}
|
||||
return [
|
||||
# Test fully_opened = True
|
||||
*(
|
||||
(s[0], {"fully_opened": True}, *s[1:])
|
||||
for s in parametrize_trigger_states(
|
||||
trigger=trigger,
|
||||
target_states=[
|
||||
(CoverState.OPEN, {}),
|
||||
(CoverState.OPENING, {}),
|
||||
(CoverState.OPEN, {ATTR_CURRENT_POSITION: 100}),
|
||||
(CoverState.OPENING, {ATTR_CURRENT_POSITION: 100}),
|
||||
],
|
||||
other_states=[
|
||||
(CoverState.CLOSED, {}),
|
||||
(CoverState.OPEN, {ATTR_CURRENT_POSITION: 0}),
|
||||
],
|
||||
additional_attributes=additional_attributes,
|
||||
trigger_from_none=False,
|
||||
)
|
||||
),
|
||||
# Test fully_opened = False
|
||||
*(
|
||||
(s[0], {}, *s[1:])
|
||||
for s in parametrize_trigger_states(
|
||||
trigger=trigger,
|
||||
target_states=[
|
||||
(CoverState.OPEN, {}),
|
||||
(CoverState.OPENING, {}),
|
||||
(CoverState.OPEN, {ATTR_CURRENT_POSITION: 1}),
|
||||
(CoverState.OPENING, {ATTR_CURRENT_POSITION: 1}),
|
||||
],
|
||||
other_states=[
|
||||
(CoverState.CLOSED, {}),
|
||||
(CoverState.CLOSED, {ATTR_CURRENT_POSITION: 0}),
|
||||
],
|
||||
additional_attributes=additional_attributes,
|
||||
trigger_from_none=False,
|
||||
)
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"trigger_key",
|
||||
[
|
||||
"cover.awning_opened",
|
||||
"cover.blind_opened",
|
||||
"cover.curtain_opened",
|
||||
"cover.door_opened",
|
||||
"cover.garage_opened",
|
||||
"cover.gate_opened",
|
||||
"cover.shade_opened",
|
||||
"cover.shutter_opened",
|
||||
"cover.window_opened",
|
||||
],
|
||||
)
|
||||
async def test_cover_triggers_gated_by_labs_flag(
|
||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
|
||||
) -> None:
|
||||
"""Test the cover triggers are gated by the labs flag."""
|
||||
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
|
||||
assert (
|
||||
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
|
||||
f"'{trigger_key}' requires the experimental 'New triggers and conditions' "
|
||||
"feature to be enabled in Home Assistant Labs settings (feature flag: "
|
||||
"'new_triggers_conditions')"
|
||||
) in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("enable_experimental_triggers_conditions")
|
||||
@pytest.mark.parametrize(
|
||||
("trigger_target_config", "entity_id", "entities_in_target"),
|
||||
parametrize_target_entities("cover"),
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("trigger", "trigger_options", "states"),
|
||||
[
|
||||
*parametrize_opened_trigger_states("cover.awning_opened", "awning"),
|
||||
*parametrize_opened_trigger_states("cover.blind_opened", "blind"),
|
||||
*parametrize_opened_trigger_states("cover.curtain_opened", "curtain"),
|
||||
*parametrize_opened_trigger_states("cover.door_opened", "door"),
|
||||
*parametrize_opened_trigger_states("cover.garage_opened", "garage"),
|
||||
*parametrize_opened_trigger_states("cover.gate_opened", "gate"),
|
||||
*parametrize_opened_trigger_states("cover.shade_opened", "shade"),
|
||||
*parametrize_opened_trigger_states("cover.shutter_opened", "shutter"),
|
||||
*parametrize_opened_trigger_states("cover.window_opened", "window"),
|
||||
],
|
||||
)
|
||||
async def test_cover_state_attribute_trigger_behavior_any(
|
||||
hass: HomeAssistant,
|
||||
service_calls: list[ServiceCall],
|
||||
target_covers: list[str],
|
||||
trigger_target_config: dict,
|
||||
entity_id: str,
|
||||
entities_in_target: int,
|
||||
trigger: str,
|
||||
trigger_options: dict,
|
||||
states: list[StateDescription],
|
||||
) -> None:
|
||||
"""Test that the cover state trigger fires when any cover state changes to a specific state."""
|
||||
await async_setup_component(hass, "cover", {})
|
||||
|
||||
other_entity_ids = set(target_covers) - {entity_id}
|
||||
|
||||
# Set all covers, including the tested cover, to the initial state
|
||||
for eid in target_covers:
|
||||
set_or_remove_state(hass, eid, states[0])
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await arm_trigger(hass, trigger, trigger_options, trigger_target_config)
|
||||
|
||||
for state in states[1:]:
|
||||
set_or_remove_state(hass, entity_id, state)
|
||||
await hass.async_block_till_done()
|
||||
assert len(service_calls) == state["count"]
|
||||
for service_call in service_calls:
|
||||
assert service_call.data[CONF_ENTITY_ID] == entity_id
|
||||
service_calls.clear()
|
||||
|
||||
# Check if changing other covers also triggers
|
||||
for other_entity_id in other_entity_ids:
|
||||
set_or_remove_state(hass, other_entity_id, state)
|
||||
await hass.async_block_till_done()
|
||||
assert len(service_calls) == (entities_in_target - 1) * state["count"]
|
||||
service_calls.clear()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("enable_experimental_triggers_conditions")
|
||||
@pytest.mark.parametrize(
|
||||
("trigger_target_config", "entity_id", "entities_in_target"),
|
||||
parametrize_target_entities("cover"),
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("trigger", "trigger_options", "states"),
|
||||
[
|
||||
*parametrize_opened_trigger_states("cover.awning_opened", "awning"),
|
||||
*parametrize_opened_trigger_states("cover.blind_opened", "blind"),
|
||||
*parametrize_opened_trigger_states("cover.curtain_opened", "curtain"),
|
||||
*parametrize_opened_trigger_states("cover.door_opened", "door"),
|
||||
*parametrize_opened_trigger_states("cover.garage_opened", "garage"),
|
||||
*parametrize_opened_trigger_states("cover.gate_opened", "gate"),
|
||||
*parametrize_opened_trigger_states("cover.shade_opened", "shade"),
|
||||
*parametrize_opened_trigger_states("cover.shutter_opened", "shutter"),
|
||||
*parametrize_opened_trigger_states("cover.window_opened", "window"),
|
||||
],
|
||||
)
|
||||
async def test_cover_state_attribute_trigger_behavior_first(
|
||||
hass: HomeAssistant,
|
||||
service_calls: list[ServiceCall],
|
||||
target_covers: list[str],
|
||||
trigger_target_config: dict,
|
||||
entity_id: str,
|
||||
entities_in_target: int,
|
||||
trigger: str,
|
||||
trigger_options: dict,
|
||||
states: list[StateDescription],
|
||||
) -> None:
|
||||
"""Test that the cover state trigger fires when the first cover state changes to a specific state."""
|
||||
await async_setup_component(hass, "cover", {})
|
||||
|
||||
other_entity_ids = set(target_covers) - {entity_id}
|
||||
|
||||
# Set all covers, including the tested cover, to the initial state
|
||||
for eid in target_covers:
|
||||
set_or_remove_state(hass, eid, states[0])
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await arm_trigger(
|
||||
hass,
|
||||
trigger,
|
||||
{"behavior": "first"} | trigger_options,
|
||||
trigger_target_config,
|
||||
)
|
||||
|
||||
for state in states[1:]:
|
||||
set_or_remove_state(hass, entity_id, state)
|
||||
await hass.async_block_till_done()
|
||||
assert len(service_calls) == state["count"]
|
||||
for service_call in service_calls:
|
||||
assert service_call.data[CONF_ENTITY_ID] == entity_id
|
||||
service_calls.clear()
|
||||
|
||||
# Triggering other covers should not cause the trigger to fire again
|
||||
for other_entity_id in other_entity_ids:
|
||||
set_or_remove_state(hass, other_entity_id, state)
|
||||
await hass.async_block_till_done()
|
||||
assert len(service_calls) == 0
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("enable_experimental_triggers_conditions")
|
||||
@pytest.mark.parametrize(
|
||||
("trigger_target_config", "entity_id", "entities_in_target"),
|
||||
parametrize_target_entities("cover"),
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("trigger", "trigger_options", "states"),
|
||||
[
|
||||
*parametrize_opened_trigger_states("cover.awning_opened", "awning"),
|
||||
*parametrize_opened_trigger_states("cover.blind_opened", "blind"),
|
||||
*parametrize_opened_trigger_states("cover.curtain_opened", "curtain"),
|
||||
*parametrize_opened_trigger_states("cover.door_opened", "door"),
|
||||
*parametrize_opened_trigger_states("cover.garage_opened", "garage"),
|
||||
*parametrize_opened_trigger_states("cover.gate_opened", "gate"),
|
||||
*parametrize_opened_trigger_states("cover.shade_opened", "shade"),
|
||||
*parametrize_opened_trigger_states("cover.shutter_opened", "shutter"),
|
||||
*parametrize_opened_trigger_states("cover.window_opened", "window"),
|
||||
],
|
||||
)
|
||||
async def test_cover_state_attribute_trigger_behavior_last(
|
||||
hass: HomeAssistant,
|
||||
service_calls: list[ServiceCall],
|
||||
target_covers: list[str],
|
||||
trigger_target_config: dict,
|
||||
entity_id: str,
|
||||
entities_in_target: int,
|
||||
trigger: str,
|
||||
trigger_options: dict,
|
||||
states: list[StateDescription],
|
||||
) -> None:
|
||||
"""Test that the cover state trigger fires when the last cover state changes to a specific state."""
|
||||
await async_setup_component(hass, "cover", {})
|
||||
|
||||
other_entity_ids = set(target_covers) - {entity_id}
|
||||
|
||||
# Set all covers, including the tested cover, to the initial state
|
||||
for eid in target_covers:
|
||||
set_or_remove_state(hass, eid, states[0])
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await arm_trigger(
|
||||
hass, trigger, {"behavior": "last"} | trigger_options, trigger_target_config
|
||||
)
|
||||
|
||||
for state in states[1:]:
|
||||
for other_entity_id in other_entity_ids:
|
||||
set_or_remove_state(hass, other_entity_id, state)
|
||||
await hass.async_block_till_done()
|
||||
assert len(service_calls) == 0
|
||||
|
||||
set_or_remove_state(hass, entity_id, state)
|
||||
await hass.async_block_till_done()
|
||||
assert len(service_calls) == state["count"]
|
||||
for service_call in service_calls:
|
||||
assert service_call.data[CONF_ENTITY_ID] == entity_id
|
||||
service_calls.clear()
|
||||
@@ -91,7 +91,6 @@ async def integration_fixture(
|
||||
"door_lock_with_unbolt",
|
||||
"eberle_ute3000",
|
||||
"ecovacs_deebot",
|
||||
"eufy_vacuum_omni_e28",
|
||||
"eve_contact_sensor",
|
||||
"eve_energy_20ecn4101",
|
||||
"eve_energy_plug",
|
||||
@@ -114,7 +113,6 @@ async def integration_fixture(
|
||||
"light_sensor",
|
||||
"microwave_oven",
|
||||
"mock_lock",
|
||||
"mock_thermostat",
|
||||
"mounted_dimmable_load_control_fixture",
|
||||
"multi_endpoint_light",
|
||||
"occupancy_sensor",
|
||||
|
||||
@@ -1,652 +0,0 @@
|
||||
{
|
||||
"node_id": 40,
|
||||
"date_commissioned": "2025-11-27T01:47:55.787124",
|
||||
"last_interview": "2025-11-27T01:49:41.087618",
|
||||
"interview_version": 6,
|
||||
"available": true,
|
||||
"is_bridge": false,
|
||||
"attributes": {
|
||||
"0/3/0": 0,
|
||||
"0/3/1": 2,
|
||||
"0/3/65532": 0,
|
||||
"0/3/65533": 5,
|
||||
"0/3/65528": [],
|
||||
"0/3/65529": [0],
|
||||
"0/3/65531": [0, 1, 65528, 65529, 65531, 65532, 65533],
|
||||
"0/29/0": [
|
||||
{
|
||||
"0": 22,
|
||||
"1": 1
|
||||
}
|
||||
],
|
||||
"0/29/1": [3, 29, 31, 40, 46, 48, 51, 60, 62, 63],
|
||||
"0/29/2": [],
|
||||
"0/29/3": [1],
|
||||
"0/29/65532": 0,
|
||||
"0/29/65533": 2,
|
||||
"0/29/65528": [],
|
||||
"0/29/65529": [],
|
||||
"0/29/65531": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533],
|
||||
"0/31/0": [
|
||||
{
|
||||
"254": 5
|
||||
},
|
||||
{
|
||||
"254": 5
|
||||
},
|
||||
{
|
||||
"254": 6
|
||||
},
|
||||
{
|
||||
"1": 5,
|
||||
"2": 2,
|
||||
"3": [112233],
|
||||
"4": null,
|
||||
"254": 7
|
||||
}
|
||||
],
|
||||
"0/31/2": 4,
|
||||
"0/31/3": 3,
|
||||
"0/31/4": 4,
|
||||
"0/31/65532": 0,
|
||||
"0/31/65533": 2,
|
||||
"0/31/65528": [],
|
||||
"0/31/65529": [],
|
||||
"0/31/65531": [0, 2, 3, 4, 65528, 65529, 65531, 65532, 65533],
|
||||
"0/40/0": 18,
|
||||
"0/40/1": "Anker Innovations Technology",
|
||||
"0/40/2": 5427,
|
||||
"0/40/3": "eufy Robot Vacuum Omni E28",
|
||||
"0/40/4": 10,
|
||||
"0/40/5": "",
|
||||
"0/40/6": "**REDACTED**",
|
||||
"0/40/7": 7,
|
||||
"0/40/8": "V7",
|
||||
"0/40/9": 3,
|
||||
"0/40/10": "3.0",
|
||||
"0/40/11": "20250107",
|
||||
"0/40/13": "https://www.eufy.com/collections/robot-vacuum-with-docking-station?ref=navimenu_1_2_2_all_copy",
|
||||
"0/40/14": "2BAVS-AB6031X-44PE",
|
||||
"0/40/15": "*********************",
|
||||
"0/40/16": false,
|
||||
"0/40/18": "*********************",
|
||||
"0/40/19": {
|
||||
"0": 3,
|
||||
"1": 65535
|
||||
},
|
||||
"0/40/21": 17039360,
|
||||
"0/40/22": 1,
|
||||
"0/40/65532": 0,
|
||||
"0/40/65533": 4,
|
||||
"0/40/65528": [],
|
||||
"0/40/65529": [],
|
||||
"0/40/65531": [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 18, 19, 21, 22,
|
||||
65528, 65529, 65531, 65532, 65533
|
||||
],
|
||||
"0/46/0": [1],
|
||||
"0/46/65532": 0,
|
||||
"0/46/65533": 1,
|
||||
"0/46/65528": [],
|
||||
"0/46/65529": [],
|
||||
"0/46/65531": [0, 65528, 65529, 65531, 65532, 65533],
|
||||
"0/48/0": 0,
|
||||
"0/48/1": {
|
||||
"0": 60,
|
||||
"1": 900
|
||||
},
|
||||
"0/48/2": 0,
|
||||
"0/48/3": 2,
|
||||
"0/48/4": true,
|
||||
"0/48/65532": 0,
|
||||
"0/48/65533": 2,
|
||||
"0/48/65528": [1, 3, 5],
|
||||
"0/48/65529": [0, 2, 4],
|
||||
"0/48/65531": [0, 1, 2, 3, 4, 65528, 65529, 65531, 65532, 65533],
|
||||
"0/51/0": [
|
||||
{
|
||||
"0": "p2p0",
|
||||
"1": false,
|
||||
"2": null,
|
||||
"3": null,
|
||||
"4": "Mr4pWXYJ",
|
||||
"5": ["wKivAQ=="],
|
||||
"6": [],
|
||||
"7": 1
|
||||
},
|
||||
{
|
||||
"0": "wlan0",
|
||||
"1": true,
|
||||
"2": null,
|
||||
"3": null,
|
||||
"4": "ML4pWXYJ",
|
||||
"5": ["wKgUKQ=="],
|
||||
"6": ["/oAAAAAAAADnm2WwWr92vA=="],
|
||||
"7": 1
|
||||
},
|
||||
{
|
||||
"0": "sit0",
|
||||
"1": false,
|
||||
"2": null,
|
||||
"3": null,
|
||||
"4": "AAAAAAAA",
|
||||
"5": [],
|
||||
"6": [],
|
||||
"7": 0
|
||||
},
|
||||
{
|
||||
"0": "lo",
|
||||
"1": true,
|
||||
"2": null,
|
||||
"3": null,
|
||||
"4": "AAAAAAAA",
|
||||
"5": ["fwAAAQ=="],
|
||||
"6": ["AAAAAAAAAAAAAAAAAAAAAQ=="],
|
||||
"7": 0
|
||||
}
|
||||
],
|
||||
"0/51/1": 41,
|
||||
"0/51/2": 159947,
|
||||
"0/51/3": 3133,
|
||||
"0/51/4": 1,
|
||||
"0/51/8": false,
|
||||
"0/51/65532": 0,
|
||||
"0/51/65533": 2,
|
||||
"0/51/65528": [2],
|
||||
"0/51/65529": [0, 1],
|
||||
"0/51/65531": [0, 1, 2, 3, 4, 8, 65528, 65529, 65531, 65532, 65533],
|
||||
"0/60/0": 0,
|
||||
"0/60/1": null,
|
||||
"0/60/2": null,
|
||||
"0/60/65532": 0,
|
||||
"0/60/65533": 1,
|
||||
"0/60/65528": [],
|
||||
"0/60/65529": [0, 2],
|
||||
"0/60/65531": [0, 1, 2, 65528, 65529, 65531, 65532, 65533],
|
||||
"0/62/0": [
|
||||
{
|
||||
"254": 5
|
||||
},
|
||||
{
|
||||
"254": 6
|
||||
},
|
||||
{
|
||||
"1": "FTABAQEkAgE3AyQTAhgmBIAigScmBYAlTTo3BiQVAiQRKBgkBwEkCAEwCUEEzo04tfIUWFsjmoJzC0tgzeNlmT2cF6jmbDUuiF5q9X4b2kR9IFHTZnvd/gxTq538148FD9yBam9FTVotQe8wVDcKNQEoARgkAgE2AwQCBAEYMAQUKoF/1myXOXBAGIBagkz+UR460bQwBRRwoL/FqInZqSh+z7QAK0r1ew8axhgwC0DlvLjZ5VajnaTLnKa2JsKRrjratV6Kr0Ip3vkNck4zW9vpKVoHj3LKptNBQ4ROHaZX6eZNOZXXn3ikvKKo/pzGGA==",
|
||||
"2": "FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQTAhgkBwEkCAEwCUEE2El8wzEcEHk4un8ZJSR8RWH86lcOvI5Q5bfSmDEaoEOyvYLA4Td/T0i7ZFpv9X0+CFDOgK9vV4sZ7sN8MSxMsTcKNQEpARgkAmAwBBRwoL/FqInZqSh+z7QAK0r1ew8axjAFFHGq+OWnV3HomxvqSHDxukU8THLXGDALQBMwck3ryDmue0Fx8kGCNAA8SjFWZF1kh+JWaesBrNwcO3oV7dEIvAblOuMmnolx5Wf/5Hbp8YRjEzKxnFyddR0Y",
|
||||
"254": 7
|
||||
}
|
||||
],
|
||||
"0/62/1": [
|
||||
{
|
||||
"1": "BFhpm8fVgw4hzcuwFGwSe59XhvdUHtMntaUUbgCX0jqoaA1fjjcRYrZCA0PDImdLtZSkrUdug3S/euAVf4gvaKo=",
|
||||
"2": 4937,
|
||||
"3": 3672061662,
|
||||
"4": 4176574991,
|
||||
"5": "JVChewy",
|
||||
"254": 5
|
||||
},
|
||||
{
|
||||
"1": "BP+6ik8SJgoRwv2p99/fGYj5G4xjjiyCNSGM83kGa6FxiLEceE4RbQar+xbksBwnzBqmqYA7PwwIZ8jdL5zKSLs=",
|
||||
"2": 4996,
|
||||
"3": 2560414293,
|
||||
"4": 3062529239,
|
||||
"5": "",
|
||||
"254": 6
|
||||
},
|
||||
{
|
||||
"1": "BC59F5uD+hRhy6qCXVp+pMpuBx68wgc6eTF57OLWHJ3+Wq1/bs3OLFr2vh4vCBzvG7C0Ijo6yKK4JERyWEtFS1E=",
|
||||
"2": 4939,
|
||||
"3": 2,
|
||||
"4": 40,
|
||||
"5": "JV Home",
|
||||
"254": 7
|
||||
}
|
||||
],
|
||||
"0/62/2": 16,
|
||||
"0/62/3": 3,
|
||||
"0/62/4": [
|
||||
"FTABAQAkAgE3AyYUWi+aACYV3jbf2hgmBORpujAkBQA3BiYUWi+aACYV3jbf2hgkBwEkCAEwCUEEWGmbx9WDDiHNy7AUbBJ7n1eG91Qe0ye1pRRuAJfSOqhoDV+ONxFitkIDQ8MiZ0u1lKStR26DdL964BV/iC9oqjcKNQEpARgkAmAwBBSE8GakJIDzzseC1x/uiMKWPWSNSjAFFITwZqQkgPPOx4LXH+6IwpY9ZI1KGDALQB2kDCqAOX3YSsiOMWzA3Zq43VKqux6XQb3UtoELJok73SJIjo9YJ5UQ9bMIQIDZkW2ckuD2t4oGKSwY8tnsTwAY",
|
||||
"FTABAQAkAgE3AycU7bEVXp1kzNEmFVXSnJgYJgR5MYMuJAUANwYnFO2xFV6dZMzRJhVV0pyYGCQHASQIATAJQQT/uopPEiYKEcL9qfff3xmI+RuMY44sgjUhjPN5BmuhcYixHHhOEW0Gq/sW5LAcJ8wapqmAOz8MCGfI3S+cyki7Nwo1ASkBGCQCYDAEFE18X69qbhhbH5f5+ExXr4ahWpvBMAUUTXxfr2puGFsfl/n4TFevhqFam8EYMAtAOhpZE0/FxSRhumv4BbBDb2ogpiwzr7lrC8FBk5BZxn2L0yNLqxTDNiOPuqv3eYgsISs3KyMDhvI05izsgLaHqhg=",
|
||||
"FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQUARgkBwEkCAEwCUEELn0Xm4P6FGHLqoJdWn6kym4HHrzCBzp5MXns4tYcnf5arX9uzc4sWva+Hi8IHO8bsLQiOjrIorgkRHJYS0VLUTcKNQEpARgkAmAwBBRxqvjlp1dx6Jsb6khw8bpFPExy1zAFFHGq+OWnV3HomxvqSHDxukU8THLXGDALQEk9bbDTdpQroquObcShGBnZjzzXHVDRWxMp7o3i977ujuEdGiVOZVKQxNDO2wh6S7mEglvWP4qw4oE1+AEEPCgY"
|
||||
],
|
||||
"0/62/5": 7,
|
||||
"0/62/65532": 0,
|
||||
"0/62/65533": 1,
|
||||
"0/62/65528": [1, 3, 5, 8],
|
||||
"0/62/65529": [0, 2, 4, 6, 7, 9, 10, 11],
|
||||
"0/62/65531": [0, 1, 2, 3, 4, 5, 65528, 65529, 65531, 65532, 65533],
|
||||
"0/63/0": [],
|
||||
"0/63/1": [],
|
||||
"0/63/2": 4,
|
||||
"0/63/3": 3,
|
||||
"0/63/65532": 0,
|
||||
"0/63/65533": 2,
|
||||
"0/63/65528": [2, 5],
|
||||
"0/63/65529": [0, 1, 3, 4],
|
||||
"0/63/65531": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533],
|
||||
"1/3/0": 0,
|
||||
"1/3/1": 2,
|
||||
"1/3/65532": 0,
|
||||
"1/3/65533": 5,
|
||||
"1/3/65528": [],
|
||||
"1/3/65529": [0],
|
||||
"1/3/65531": [0, 1, 65528, 65529, 65531, 65532, 65533],
|
||||
"1/29/0": [
|
||||
{
|
||||
"0": 17,
|
||||
"1": 1
|
||||
},
|
||||
{
|
||||
"0": 116,
|
||||
"1": 1
|
||||
}
|
||||
],
|
||||
"1/29/1": [3, 29, 47, 84, 85, 97, 336],
|
||||
"1/29/2": [],
|
||||
"1/29/3": [],
|
||||
"1/29/65532": 0,
|
||||
"1/29/65533": 2,
|
||||
"1/29/65528": [],
|
||||
"1/29/65529": [],
|
||||
"1/29/65531": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533],
|
||||
"1/47/0": 1,
|
||||
"1/47/1": 0,
|
||||
"1/47/2": "Battery",
|
||||
"1/47/12": 200,
|
||||
"1/47/14": 0,
|
||||
"1/47/15": false,
|
||||
"1/47/16": 0,
|
||||
"1/47/26": 2,
|
||||
"1/47/28": false,
|
||||
"1/47/31": [],
|
||||
"1/47/65532": 6,
|
||||
"1/47/65533": 3,
|
||||
"1/47/65528": [],
|
||||
"1/47/65529": [],
|
||||
"1/47/65531": [
|
||||
0, 1, 2, 12, 14, 15, 16, 26, 28, 31, 65528, 65529, 65531, 65532, 65533
|
||||
],
|
||||
"1/84/0": [
|
||||
{
|
||||
"0": "Idle",
|
||||
"1": 0,
|
||||
"2": [
|
||||
{
|
||||
"1": 16384
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"0": "Cleaning",
|
||||
"1": 1,
|
||||
"2": [
|
||||
{
|
||||
"1": 16385
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"0": "Mapping",
|
||||
"1": 2,
|
||||
"2": [
|
||||
{
|
||||
"1": 16386
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"1/84/1": 0,
|
||||
"1/84/65532": 0,
|
||||
"1/84/65533": 3,
|
||||
"1/84/65528": [1],
|
||||
"1/84/65529": [0],
|
||||
"1/84/65531": [0, 1, 65528, 65529, 65531, 65532, 65533],
|
||||
"1/85/0": [
|
||||
{
|
||||
"0": "Auto Mop & Vacuum",
|
||||
"1": 0,
|
||||
"2": [
|
||||
{
|
||||
"1": 0
|
||||
},
|
||||
{
|
||||
"1": 16386
|
||||
},
|
||||
{
|
||||
"1": 16385
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"0": "Auto Vacuum",
|
||||
"1": 1,
|
||||
"2": [
|
||||
{
|
||||
"1": 0
|
||||
},
|
||||
{
|
||||
"1": 16385
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"0": "Auto Deep Mop & Vacuum",
|
||||
"1": 2,
|
||||
"2": [
|
||||
{
|
||||
"1": 0
|
||||
},
|
||||
{
|
||||
"1": 16384
|
||||
},
|
||||
{
|
||||
"1": 16386
|
||||
},
|
||||
{
|
||||
"1": 16385
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"0": "Quick Mop & Vacuum",
|
||||
"1": 3,
|
||||
"2": [
|
||||
{
|
||||
"1": 1
|
||||
},
|
||||
{
|
||||
"1": 16386
|
||||
},
|
||||
{
|
||||
"1": 16385
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"0": "Quick Vacuum",
|
||||
"1": 4,
|
||||
"2": [
|
||||
{
|
||||
"1": 1
|
||||
},
|
||||
{
|
||||
"1": 16385
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"0": "Low Noise Mop & Vacuum",
|
||||
"1": 5,
|
||||
"2": [
|
||||
{
|
||||
"1": 3
|
||||
},
|
||||
{
|
||||
"1": 16386
|
||||
},
|
||||
{
|
||||
"1": 16385
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"0": "Low Noise Vacuum",
|
||||
"1": 6,
|
||||
"2": [
|
||||
{
|
||||
"1": 3
|
||||
},
|
||||
{
|
||||
"1": 16385
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"0": "Eufy Customize Mode",
|
||||
"1": 7,
|
||||
"2": [
|
||||
{
|
||||
"1": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"1/85/1": 0,
|
||||
"1/85/65532": 65536,
|
||||
"1/85/65533": 3,
|
||||
"1/85/65528": [1],
|
||||
"1/85/65529": [0],
|
||||
"1/85/65531": [0, 1, 65528, 65529, 65531, 65532, 65533],
|
||||
"1/97/0": null,
|
||||
"1/97/1": null,
|
||||
"1/97/3": [
|
||||
{
|
||||
"0": 0
|
||||
},
|
||||
{
|
||||
"0": 1
|
||||
},
|
||||
{
|
||||
"0": 2
|
||||
},
|
||||
{
|
||||
"0": 3
|
||||
},
|
||||
{
|
||||
"0": 64
|
||||
},
|
||||
{
|
||||
"0": 65
|
||||
},
|
||||
{
|
||||
"0": 66
|
||||
}
|
||||
],
|
||||
"1/97/4": 0,
|
||||
"1/97/5": {
|
||||
"0": 0
|
||||
},
|
||||
"1/97/65532": 0,
|
||||
"1/97/65533": 2,
|
||||
"1/97/65528": [4],
|
||||
"1/97/65529": [0, 3, 128],
|
||||
"1/97/65531": [0, 1, 3, 4, 5, 65528, 65529, 65531, 65532, 65533],
|
||||
"1/336/0": [
|
||||
{
|
||||
"0": 40001,
|
||||
"1": 4,
|
||||
"2": {
|
||||
"0": {
|
||||
"0": "Dining Room",
|
||||
"1": null,
|
||||
"2": null
|
||||
},
|
||||
"1": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"0": 40002,
|
||||
"1": 4,
|
||||
"2": {
|
||||
"0": {
|
||||
"0": "Spare Bedroom",
|
||||
"1": null,
|
||||
"2": null
|
||||
},
|
||||
"1": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"0": 40003,
|
||||
"1": 4,
|
||||
"2": {
|
||||
"0": {
|
||||
"0": "Living Room",
|
||||
"1": null,
|
||||
"2": null
|
||||
},
|
||||
"1": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"0": 40004,
|
||||
"1": 4,
|
||||
"2": {
|
||||
"0": {
|
||||
"0": "Kitchen",
|
||||
"1": null,
|
||||
"2": null
|
||||
},
|
||||
"1": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"0": 40005,
|
||||
"1": 4,
|
||||
"2": {
|
||||
"0": {
|
||||
"0": "Master bedroom",
|
||||
"1": null,
|
||||
"2": null
|
||||
},
|
||||
"1": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"0": 40006,
|
||||
"1": 4,
|
||||
"2": {
|
||||
"0": {
|
||||
"0": "Laundry Room",
|
||||
"1": null,
|
||||
"2": null
|
||||
},
|
||||
"1": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"0": 40007,
|
||||
"1": 4,
|
||||
"2": {
|
||||
"0": {
|
||||
"0": "Master Closet",
|
||||
"1": null,
|
||||
"2": null
|
||||
},
|
||||
"1": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"0": 40008,
|
||||
"1": 4,
|
||||
"2": {
|
||||
"0": {
|
||||
"0": "Back Hallway",
|
||||
"1": null,
|
||||
"2": null
|
||||
},
|
||||
"1": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"0": 40009,
|
||||
"1": 4,
|
||||
"2": {
|
||||
"0": {
|
||||
"0": "Home Office",
|
||||
"1": null,
|
||||
"2": null
|
||||
},
|
||||
"1": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"0": 40010,
|
||||
"1": 4,
|
||||
"2": {
|
||||
"0": {
|
||||
"0": "room_10",
|
||||
"1": null,
|
||||
"2": null
|
||||
},
|
||||
"1": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"0": 40011,
|
||||
"1": 4,
|
||||
"2": {
|
||||
"0": {
|
||||
"0": "Gym",
|
||||
"1": null,
|
||||
"2": null
|
||||
},
|
||||
"1": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"0": 40012,
|
||||
"1": 4,
|
||||
"2": {
|
||||
"0": {
|
||||
"0": "room_12",
|
||||
"1": null,
|
||||
"2": null
|
||||
},
|
||||
"1": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"0": 40013,
|
||||
"1": 4,
|
||||
"2": {
|
||||
"0": {
|
||||
"0": "Master Bathroom",
|
||||
"1": null,
|
||||
"2": null
|
||||
},
|
||||
"1": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"0": 40014,
|
||||
"1": 4,
|
||||
"2": {
|
||||
"0": {
|
||||
"0": "Front Hallway",
|
||||
"1": null,
|
||||
"2": null
|
||||
},
|
||||
"1": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"0": 40015,
|
||||
"1": 4,
|
||||
"2": {
|
||||
"0": {
|
||||
"0": "Guest Bathroom",
|
||||
"1": null,
|
||||
"2": null
|
||||
},
|
||||
"1": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"1/336/1": [
|
||||
{
|
||||
"0": 4,
|
||||
"1": "map_4"
|
||||
}
|
||||
],
|
||||
"1/336/2": [],
|
||||
"1/336/3": null,
|
||||
"1/336/5": [],
|
||||
"1/336/65532": 6,
|
||||
"1/336/65533": 1,
|
||||
"1/336/65528": [1],
|
||||
"1/336/65529": [0],
|
||||
"1/336/65531": [0, 1, 2, 3, 5, 65528, 65529, 65531, 65532, 65533]
|
||||
},
|
||||
"attribute_subscriptions": []
|
||||
}
|
||||
@@ -1,526 +0,0 @@
|
||||
{
|
||||
"node_id": 150,
|
||||
"date_commissioned": "2025-11-18T06:53:08.679289",
|
||||
"last_interview": "2025-11-18T06:53:08.679325",
|
||||
"interview_version": 6,
|
||||
"available": true,
|
||||
"is_bridge": false,
|
||||
"attributes": {
|
||||
"0/49/0": 1,
|
||||
"0/49/1": [
|
||||
{
|
||||
"0": "ZW5zMzM=",
|
||||
"1": true
|
||||
}
|
||||
],
|
||||
"0/49/4": true,
|
||||
"0/49/5": 0,
|
||||
"0/49/6": "ZW5zMzM=",
|
||||
"0/49/7": null,
|
||||
"0/49/65532": 4,
|
||||
"0/49/65533": 2,
|
||||
"0/49/65528": [],
|
||||
"0/49/65529": [],
|
||||
"0/49/65531": [0, 1, 4, 5, 6, 7, 65532, 65533, 65528, 65529, 65531],
|
||||
"0/65/0": [],
|
||||
"0/65/65532": 0,
|
||||
"0/65/65533": 1,
|
||||
"0/65/65528": [],
|
||||
"0/65/65529": [],
|
||||
"0/65/65531": [0, 65532, 65533, 65528, 65529, 65531],
|
||||
"0/63/0": [],
|
||||
"0/63/1": [],
|
||||
"0/63/2": 4,
|
||||
"0/63/3": 3,
|
||||
"0/63/65532": 0,
|
||||
"0/63/65533": 2,
|
||||
"0/63/65528": [5, 2],
|
||||
"0/63/65529": [0, 1, 3, 4],
|
||||
"0/63/65531": [0, 1, 2, 3, 65532, 65533, 65528, 65529, 65531],
|
||||
"0/62/0": [
|
||||
{
|
||||
"1": "FTABAQEkAgE3AyQTAhgmBIAigScmBYAlTTo3BiQVAiQRlhgkBwEkCAEwCUEE2p7AKvoklmZUFHB0JFUiCsv5FCm0dmeH35yXz4UUH4HAWUwpbeU+R7hMGbAITM3T1R/mVWYthssdVcPNsfIVcjcKNQEoARgkAgE2AwQCBAEYMAQUQbZ3toX8hpE/FmJz7M6xHTbh6RMwBRS5+zzv8ZPGnI9mC3wH9vq10JnwlhgwC0DughBITJJHW/pS7o0J6o6FYTe1ufe0vCpaCj3qYeWb/QxLUydUaJQbce5Z3lUcFeHybUa/M9HID+0PRp2Ker3/GA==",
|
||||
"2": "FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQTAhgkBwEkCAEwCUEE/DujEcdTsX19xbxX+KuKKWiMaA5D9u99P/pVxIOmscd2BA2PadEMNnjvtPOpf+WE2Zxar4rby1IfAClGUUuQrTcKNQEpARgkAmAwBBS5+zzv8ZPGnI9mC3wH9vq10JnwljAFFPT6p93JKGcb7g+rTWnA6evF2EdGGDALQGkPpvsbkAFEbfPN6H3Kf23R0zzmW/gpAA3kgaL6wKB2Ofm+Tmylw22qM536Kj8mOMwaV0EL1dCCGcuxF98aL6gY",
|
||||
"254": 1
|
||||
}
|
||||
],
|
||||
"0/62/1": [
|
||||
{
|
||||
"1": "BBmX+KwLR5HGlVNbvlC+dO8Jv9fPthHiTfGpUzi2JJADX5az6GxBAFn02QKHwLcZHyh+lh9faf6rf38/nPYF7/M=",
|
||||
"2": 4939,
|
||||
"3": 2,
|
||||
"4": 150,
|
||||
"5": "ha",
|
||||
"254": 1
|
||||
}
|
||||
],
|
||||
"0/62/2": 16,
|
||||
"0/62/3": 1,
|
||||
"0/62/4": [
|
||||
"FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQUARgkBwEkCAEwCUEEGZf4rAtHkcaVU1u+UL507wm/18+2EeJN8alTOLYkkANflrPobEEAWfTZAofAtxkfKH6WH19p/qt/fz+c9gXv8zcKNQEpARgkAmAwBBT0+qfdyShnG+4Pq01pwOnrxdhHRjAFFPT6p93JKGcb7g+rTWnA6evF2EdGGDALQPVrsFnfFplsQGV5m5EUua+rmo9hAr+OP1bvaifdLqiEIn3uXLTLoKmVUkPImRL2Fb+xcMEAqR2p7RM6ZlFCR20Y"
|
||||
],
|
||||
"0/62/5": 1,
|
||||
"0/62/65532": 0,
|
||||
"0/62/65533": 2,
|
||||
"0/62/65528": [1, 3, 5, 8, 14],
|
||||
"0/62/65529": [0, 2, 4, 6, 7, 9, 10, 11, 12, 13],
|
||||
"0/62/65531": [0, 1, 2, 3, 4, 5, 65532, 65533, 65528, 65529, 65531],
|
||||
"0/60/0": 0,
|
||||
"0/60/1": null,
|
||||
"0/60/2": null,
|
||||
"0/60/65532": 0,
|
||||
"0/60/65533": 1,
|
||||
"0/60/65528": [],
|
||||
"0/60/65529": [0, 2],
|
||||
"0/60/65531": [0, 1, 2, 65532, 65533, 65528, 65529, 65531],
|
||||
"0/55/2": 425,
|
||||
"0/55/3": 61,
|
||||
"0/55/4": 0,
|
||||
"0/55/5": 0,
|
||||
"0/55/6": 0,
|
||||
"0/55/7": null,
|
||||
"0/55/1": true,
|
||||
"0/55/0": 2,
|
||||
"0/55/8": 16,
|
||||
"0/55/65532": 3,
|
||||
"0/55/65533": 1,
|
||||
"0/55/65528": [],
|
||||
"0/55/65529": [0],
|
||||
"0/55/65531": [
|
||||
2, 3, 4, 5, 6, 7, 1, 0, 8, 65532, 65533, 65528, 65529, 65531
|
||||
],
|
||||
"0/54/0": null,
|
||||
"0/54/1": null,
|
||||
"0/54/2": 3,
|
||||
"0/54/3": null,
|
||||
"0/54/4": null,
|
||||
"0/54/5": null,
|
||||
"0/54/12": null,
|
||||
"0/54/6": null,
|
||||
"0/54/7": null,
|
||||
"0/54/8": null,
|
||||
"0/54/9": null,
|
||||
"0/54/10": null,
|
||||
"0/54/11": null,
|
||||
"0/54/65532": 3,
|
||||
"0/54/65533": 1,
|
||||
"0/54/65528": [],
|
||||
"0/54/65529": [0],
|
||||
"0/54/65531": [
|
||||
0, 1, 2, 3, 4, 5, 12, 6, 7, 8, 9, 10, 11, 65532, 65533, 65528, 65529,
|
||||
65531
|
||||
],
|
||||
"0/52/0": [
|
||||
{
|
||||
"0": 6163,
|
||||
"1": "6163"
|
||||
},
|
||||
{
|
||||
"0": 6162,
|
||||
"1": "6162"
|
||||
},
|
||||
{
|
||||
"0": 6161,
|
||||
"1": "6161"
|
||||
},
|
||||
{
|
||||
"0": 6160,
|
||||
"1": "6160"
|
||||
},
|
||||
{
|
||||
"0": 6159,
|
||||
"1": "6159"
|
||||
}
|
||||
],
|
||||
"0/52/1": 545392,
|
||||
"0/52/2": 650640,
|
||||
"0/52/3": 650640,
|
||||
"0/52/65532": 1,
|
||||
"0/52/65533": 1,
|
||||
"0/52/65528": [],
|
||||
"0/52/65529": [0],
|
||||
"0/52/65531": [0, 1, 2, 3, 65532, 65533, 65528, 65529, 65531],
|
||||
"0/51/0": [
|
||||
{
|
||||
"0": "docker0",
|
||||
"1": false,
|
||||
"2": null,
|
||||
"3": null,
|
||||
"4": "8mJ0KirG",
|
||||
"5": ["rBEAAQ=="],
|
||||
"6": [],
|
||||
"7": 0
|
||||
},
|
||||
{
|
||||
"0": "ens33",
|
||||
"1": true,
|
||||
"2": null,
|
||||
"3": null,
|
||||
"4": "AAwpaqXN",
|
||||
"5": ["wKgBxA=="],
|
||||
"6": [
|
||||
"KgEOCgKzOZAcmuLd4EsaUA==",
|
||||
"KgEOCgKzOZA2wMm9YG06Ag==",
|
||||
"/oAAAAAAAACluAo+qvkuxw=="
|
||||
],
|
||||
"7": 2
|
||||
},
|
||||
{
|
||||
"0": "lo",
|
||||
"1": true,
|
||||
"2": null,
|
||||
"3": null,
|
||||
"4": "AAAAAAAA",
|
||||
"5": ["fwAAAQ=="],
|
||||
"6": ["AAAAAAAAAAAAAAAAAAAAAQ=="],
|
||||
"7": 0
|
||||
}
|
||||
],
|
||||
"0/51/1": 1,
|
||||
"0/51/8": false,
|
||||
"0/51/3": 0,
|
||||
"0/51/4": 0,
|
||||
"0/51/2": 16,
|
||||
"0/51/65532": 0,
|
||||
"0/51/65533": 2,
|
||||
"0/51/65528": [2],
|
||||
"0/51/65529": [0, 1],
|
||||
"0/51/65531": [0, 1, 8, 3, 4, 2, 65532, 65533, 65528, 65529, 65531],
|
||||
"0/50/65532": 0,
|
||||
"0/50/65533": 1,
|
||||
"0/50/65528": [1],
|
||||
"0/50/65529": [0],
|
||||
"0/50/65531": [65532, 65533, 65528, 65529, 65531],
|
||||
"0/48/0": 0,
|
||||
"0/48/1": {
|
||||
"0": 60,
|
||||
"1": 900
|
||||
},
|
||||
"0/48/2": 0,
|
||||
"0/48/3": 2,
|
||||
"0/48/4": true,
|
||||
"0/48/65532": 0,
|
||||
"0/48/65533": 2,
|
||||
"0/48/65528": [1, 3, 5],
|
||||
"0/48/65529": [0, 2, 4],
|
||||
"0/48/65531": [0, 1, 2, 3, 4, 65532, 65533, 65528, 65529, 65531],
|
||||
"0/43/0": "en-US",
|
||||
"0/43/1": ["en-US"],
|
||||
"0/43/65532": 0,
|
||||
"0/43/65533": 1,
|
||||
"0/43/65528": [],
|
||||
"0/43/65529": [],
|
||||
"0/43/65531": [0, 1, 65532, 65533, 65528, 65529, 65531],
|
||||
"0/40/0": 19,
|
||||
"0/40/1": "TEST_VENDOR",
|
||||
"0/40/2": 65521,
|
||||
"0/40/3": "Mock Thermostat",
|
||||
"0/40/4": 32769,
|
||||
"0/40/5": "",
|
||||
"0/40/6": "**REDACTED**",
|
||||
"0/40/7": 0,
|
||||
"0/40/8": "TEST_VERSION",
|
||||
"0/40/9": 1,
|
||||
"0/40/10": "1.0",
|
||||
"0/40/19": {
|
||||
"0": 3,
|
||||
"1": 65535
|
||||
},
|
||||
"0/40/21": 17104896,
|
||||
"0/40/22": 1,
|
||||
"0/40/24": 1,
|
||||
"0/40/11": "20200101",
|
||||
"0/40/12": "",
|
||||
"0/40/13": "",
|
||||
"0/40/14": "",
|
||||
"0/40/15": "TEST_SN",
|
||||
"0/40/16": false,
|
||||
"0/40/18": "29DB8B9DB518F05F",
|
||||
"0/40/65532": 0,
|
||||
"0/40/65533": 5,
|
||||
"0/40/65528": [],
|
||||
"0/40/65529": [],
|
||||
"0/40/65531": [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 19, 21, 22, 24, 11, 12, 13, 14, 15, 16,
|
||||
18, 65532, 65533, 65528, 65529, 65531
|
||||
],
|
||||
"0/31/0": [
|
||||
{
|
||||
"1": 5,
|
||||
"2": 2,
|
||||
"3": [112233],
|
||||
"4": null,
|
||||
"254": 1
|
||||
}
|
||||
],
|
||||
"0/31/2": 4,
|
||||
"0/31/3": 3,
|
||||
"0/31/4": 4,
|
||||
"0/31/65532": 0,
|
||||
"0/31/65533": 3,
|
||||
"0/31/65528": [],
|
||||
"0/31/65529": [],
|
||||
"0/31/65531": [0, 2, 3, 4, 65532, 65533, 65528, 65529, 65531],
|
||||
"0/30/0": [],
|
||||
"0/30/65532": 0,
|
||||
"0/30/65533": 1,
|
||||
"0/30/65528": [],
|
||||
"0/30/65529": [],
|
||||
"0/30/65531": [0, 65532, 65533, 65528, 65529, 65531],
|
||||
"0/29/0": [
|
||||
{
|
||||
"0": 18,
|
||||
"1": 1
|
||||
},
|
||||
{
|
||||
"0": 22,
|
||||
"1": 3
|
||||
}
|
||||
],
|
||||
"0/29/1": [
|
||||
49, 65, 63, 62, 60, 55, 54, 52, 51, 50, 48, 43, 40, 31, 30, 29, 3, 42, 45,
|
||||
53
|
||||
],
|
||||
"0/29/2": [41],
|
||||
"0/29/3": [1],
|
||||
"0/29/65532": 0,
|
||||
"0/29/65533": 3,
|
||||
"0/29/65528": [],
|
||||
"0/29/65529": [],
|
||||
"0/29/65531": [0, 1, 2, 3, 65532, 65533, 65528, 65529, 65531],
|
||||
"0/3/0": 0,
|
||||
"0/3/1": 2,
|
||||
"0/3/65532": 0,
|
||||
"0/3/65533": 6,
|
||||
"0/3/65528": [],
|
||||
"0/3/65529": [0, 64],
|
||||
"0/3/65531": [0, 1, 65532, 65533, 65528, 65529, 65531],
|
||||
"0/42/0": [],
|
||||
"0/42/1": true,
|
||||
"0/42/2": 0,
|
||||
"0/42/3": 0,
|
||||
"0/42/65532": 0,
|
||||
"0/42/65533": 1,
|
||||
"0/42/65528": [],
|
||||
"0/42/65529": [0],
|
||||
"0/42/65531": [0, 1, 2, 3, 65532, 65533, 65528, 65529, 65531],
|
||||
"0/45/0": 1,
|
||||
"0/45/65532": 1,
|
||||
"0/45/65533": 2,
|
||||
"0/45/65528": [],
|
||||
"0/45/65529": [],
|
||||
"0/45/65531": [0, 65532, 65533, 65528, 65529, 65531],
|
||||
"0/53/0": null,
|
||||
"0/53/1": null,
|
||||
"0/53/2": null,
|
||||
"0/53/3": null,
|
||||
"0/53/4": null,
|
||||
"0/53/5": null,
|
||||
"0/53/6": 0,
|
||||
"0/53/7": [],
|
||||
"0/53/8": [],
|
||||
"0/53/9": null,
|
||||
"0/53/10": null,
|
||||
"0/53/11": null,
|
||||
"0/53/12": null,
|
||||
"0/53/13": null,
|
||||
"0/53/14": 0,
|
||||
"0/53/15": 0,
|
||||
"0/53/16": 0,
|
||||
"0/53/17": 0,
|
||||
"0/53/18": 0,
|
||||
"0/53/19": 0,
|
||||
"0/53/20": 0,
|
||||
"0/53/21": 0,
|
||||
"0/53/22": 0,
|
||||
"0/53/23": 0,
|
||||
"0/53/24": 0,
|
||||
"0/53/25": 0,
|
||||
"0/53/26": 0,
|
||||
"0/53/27": 0,
|
||||
"0/53/28": 0,
|
||||
"0/53/29": 0,
|
||||
"0/53/30": 0,
|
||||
"0/53/31": 0,
|
||||
"0/53/32": 0,
|
||||
"0/53/33": 0,
|
||||
"0/53/34": 0,
|
||||
"0/53/35": 0,
|
||||
"0/53/36": 0,
|
||||
"0/53/37": 0,
|
||||
"0/53/38": 0,
|
||||
"0/53/39": 0,
|
||||
"0/53/40": 0,
|
||||
"0/53/41": 0,
|
||||
"0/53/42": 0,
|
||||
"0/53/43": 0,
|
||||
"0/53/44": 0,
|
||||
"0/53/45": 0,
|
||||
"0/53/46": 0,
|
||||
"0/53/47": 0,
|
||||
"0/53/48": 0,
|
||||
"0/53/49": 0,
|
||||
"0/53/50": 0,
|
||||
"0/53/51": 0,
|
||||
"0/53/52": 0,
|
||||
"0/53/53": 0,
|
||||
"0/53/54": 0,
|
||||
"0/53/55": 0,
|
||||
"0/53/56": null,
|
||||
"0/53/57": null,
|
||||
"0/53/58": null,
|
||||
"0/53/59": null,
|
||||
"0/53/60": null,
|
||||
"0/53/61": null,
|
||||
"0/53/62": [],
|
||||
"0/53/65532": 15,
|
||||
"0/53/65533": 3,
|
||||
"0/53/65528": [],
|
||||
"0/53/65529": [0],
|
||||
"0/53/65531": [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
||||
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
|
||||
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
|
||||
57, 58, 59, 60, 61, 62, 65532, 65533, 65528, 65529, 65531
|
||||
],
|
||||
"1/29/0": [
|
||||
{
|
||||
"0": 769,
|
||||
"1": 4
|
||||
}
|
||||
],
|
||||
"1/29/1": [29, 3, 4, 513, 516],
|
||||
"1/29/2": [3],
|
||||
"1/29/3": [],
|
||||
"1/29/65532": 0,
|
||||
"1/29/65533": 3,
|
||||
"1/29/65528": [],
|
||||
"1/29/65529": [],
|
||||
"1/29/65531": [0, 1, 2, 3, 65532, 65533, 65528, 65529, 65531],
|
||||
"1/3/0": 0,
|
||||
"1/3/1": 2,
|
||||
"1/3/65532": 0,
|
||||
"1/3/65533": 6,
|
||||
"1/3/65528": [],
|
||||
"1/3/65529": [0, 64],
|
||||
"1/3/65531": [0, 1, 65532, 65533, 65528, 65529, 65531],
|
||||
"1/4/0": 128,
|
||||
"1/4/65532": 1,
|
||||
"1/4/65533": 4,
|
||||
"1/4/65528": [0, 1, 2, 3],
|
||||
"1/4/65529": [0, 1, 2, 3, 4, 5],
|
||||
"1/4/65531": [0, 65532, 65533, 65528, 65529, 65531],
|
||||
|
||||
"1/513/0": 1800,
|
||||
"1/513/1": 500,
|
||||
"1/513/3": 700,
|
||||
"1/513/4": 3000,
|
||||
"1/513/5": 1600,
|
||||
"1/513/6": 3200,
|
||||
"1/513/7": 0,
|
||||
"1/513/8": 25,
|
||||
"1/513/16": 0,
|
||||
"1/513/17": 2600,
|
||||
"1/513/18": 2000,
|
||||
"1/513/21": 700,
|
||||
"1/513/22": 3000,
|
||||
"1/513/23": 1600,
|
||||
"1/513/24": 3200,
|
||||
"1/513/25": 25,
|
||||
"1/513/26": 0,
|
||||
"1/513/27": 4,
|
||||
"1/513/28": 1,
|
||||
"1/513/30": 4,
|
||||
"1/513/35": 0,
|
||||
"1/513/36": 0,
|
||||
"1/513/37": 0,
|
||||
"1/513/41": 1,
|
||||
"1/513/48": 0,
|
||||
"1/513/49": 150,
|
||||
"1/513/50": 789004800,
|
||||
"1/513/72": [
|
||||
{
|
||||
"0": 1,
|
||||
"1": 1,
|
||||
"2": 1
|
||||
},
|
||||
{
|
||||
"0": 2,
|
||||
"1": 1,
|
||||
"2": 1
|
||||
},
|
||||
{
|
||||
"0": 3,
|
||||
"1": 1,
|
||||
"2": 2
|
||||
},
|
||||
{
|
||||
"0": 4,
|
||||
"1": 1,
|
||||
"2": 2
|
||||
},
|
||||
{
|
||||
"0": 5,
|
||||
"1": 1,
|
||||
"2": 2
|
||||
},
|
||||
{
|
||||
"0": 254,
|
||||
"1": 1,
|
||||
"2": 2
|
||||
}
|
||||
],
|
||||
"1/513/73": [
|
||||
{
|
||||
"0": 4,
|
||||
"1": 1,
|
||||
"2": 2
|
||||
},
|
||||
{
|
||||
"0": 3,
|
||||
"1": 1,
|
||||
"2": 2
|
||||
}
|
||||
],
|
||||
"1/513/74": 5,
|
||||
"1/513/78": null,
|
||||
"1/513/80": [
|
||||
{
|
||||
"0": "AQ==",
|
||||
"1": 1,
|
||||
"3": 2500,
|
||||
"4": 2100,
|
||||
"5": true
|
||||
},
|
||||
{
|
||||
"0": "Ag==",
|
||||
"1": 2,
|
||||
"3": 2600,
|
||||
"4": 2000,
|
||||
"5": true
|
||||
}
|
||||
],
|
||||
"1/513/82": 0,
|
||||
"1/513/83": 5,
|
||||
"1/513/84": [],
|
||||
"1/513/85": null,
|
||||
"1/513/86": null,
|
||||
"1/513/65532": 423,
|
||||
"1/513/65533": 9,
|
||||
"1/513/65528": [2, 253],
|
||||
"1/513/65529": [0, 6, 7, 8, 254],
|
||||
"1/513/65531": [
|
||||
0, 1, 3, 4, 5, 6, 7, 8, 16, 17, 18, 21, 22, 23, 24, 25, 26, 27, 28, 30,
|
||||
35, 36, 37, 41, 48, 49, 50, 72, 73, 74, 78, 80, 82, 83, 84, 85, 86, 65532,
|
||||
65533, 65528, 65529, 65531
|
||||
],
|
||||
"1/516/0": 0,
|
||||
"1/516/1": 0,
|
||||
"1/516/65532": 0,
|
||||
"1/516/65533": 2,
|
||||
"1/516/65528": [],
|
||||
"1/516/65529": [],
|
||||
"1/516/65531": [0, 1, 65532, 65533, 65528, 65529, 65531]
|
||||
},
|
||||
"attribute_subscriptions": []
|
||||
}
|
||||
@@ -391,102 +391,6 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[eve_thermo][binary_sensor.eve_thermo_local_temperature_remote_sensing-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': 'binary_sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'binary_sensor.eve_thermo_local_temperature_remote_sensing',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Local temperature remote sensing',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'thermostat_remote_sensing_local_temperature',
|
||||
'unique_id': '00000000000004D2-0000000000000021-MatterNodeDevice-1-ThermostatRemoteSensing_LocalTemperature-513-26',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[eve_thermo][binary_sensor.eve_thermo_local_temperature_remote_sensing-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Eve Thermo Local temperature remote sensing',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.eve_thermo_local_temperature_remote_sensing',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[eve_thermo][binary_sensor.eve_thermo_outdoor_temperature_remote_sensing-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': 'binary_sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'binary_sensor.eve_thermo_outdoor_temperature_remote_sensing',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Outdoor temperature remote sensing',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'thermostat_remote_sensing_outdoor_temperature',
|
||||
'unique_id': '00000000000004D2-0000000000000021-MatterNodeDevice-1-ThermostatRemoteSensing_OutdoorTemperature-513-26',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[eve_thermo][binary_sensor.eve_thermo_outdoor_temperature_remote_sensing-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Eve Thermo Outdoor temperature remote sensing',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.eve_thermo_outdoor_temperature_remote_sensing',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[heiman_motion_sensor_m1][binary_sensor.smart_motion_sensor_occupancy-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -732,150 +636,6 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[mock_thermostat][binary_sensor.mock_thermostat_local_temperature_remote_sensing-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': 'binary_sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'binary_sensor.mock_thermostat_local_temperature_remote_sensing',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Local temperature remote sensing',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'thermostat_remote_sensing_local_temperature',
|
||||
'unique_id': '00000000000004D2-0000000000000096-MatterNodeDevice-1-ThermostatRemoteSensing_LocalTemperature-513-26',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[mock_thermostat][binary_sensor.mock_thermostat_local_temperature_remote_sensing-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Mock Thermostat Local temperature remote sensing',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.mock_thermostat_local_temperature_remote_sensing',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[mock_thermostat][binary_sensor.mock_thermostat_occupancy_remote_sensing-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': 'binary_sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'binary_sensor.mock_thermostat_occupancy_remote_sensing',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Occupancy remote sensing',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'thermostat_remote_sensing_occupancy',
|
||||
'unique_id': '00000000000004D2-0000000000000096-MatterNodeDevice-1-ThermostatRemoteSensing_Occupancy-513-26',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[mock_thermostat][binary_sensor.mock_thermostat_occupancy_remote_sensing-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Mock Thermostat Occupancy remote sensing',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.mock_thermostat_occupancy_remote_sensing',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[mock_thermostat][binary_sensor.mock_thermostat_outdoor_temperature_remote_sensing-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': 'binary_sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'binary_sensor.mock_thermostat_outdoor_temperature_remote_sensing',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Outdoor temperature remote sensing',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'thermostat_remote_sensing_outdoor_temperature',
|
||||
'unique_id': '00000000000004D2-0000000000000096-MatterNodeDevice-1-ThermostatRemoteSensing_OutdoorTemperature-513-26',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[mock_thermostat][binary_sensor.mock_thermostat_outdoor_temperature_remote_sensing-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Mock Thermostat Outdoor temperature remote sensing',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.mock_thermostat_outdoor_temperature_remote_sensing',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[occupancy_sensor][binary_sensor.mock_occupancy_sensor_occupancy-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
@@ -1026,104 +1026,6 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[eufy_vacuum_omni_e28][button.2bavs_ab6031x_44pe_identify_0-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.2bavs_ab6031x_44pe_identify_0',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Identify (0)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-0000000000000028-MatterNodeDevice-0-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[eufy_vacuum_omni_e28][button.2bavs_ab6031x_44pe_identify_0-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': '2BAVS-AB6031X-44PE Identify (0)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.2bavs_ab6031x_44pe_identify_0',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[eufy_vacuum_omni_e28][button.2bavs_ab6031x_44pe_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.2bavs_ab6031x_44pe_identify_1',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'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-0000000000000028-MatterNodeDevice-1-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[eufy_vacuum_omni_e28][button.2bavs_ab6031x_44pe_identify_1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': '2BAVS-AB6031X-44PE Identify (1)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.2bavs_ab6031x_44pe_identify_1',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[eve_contact_sensor][button.eve_door_identify-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -2388,104 +2290,6 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[mock_thermostat][button.mock_thermostat_identify_0-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.mock_thermostat_identify_0',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Identify (0)',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-0000000000000096-MatterNodeDevice-0-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[mock_thermostat][button.mock_thermostat_identify_0-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': 'Mock Thermostat Identify (0)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.mock_thermostat_identify_0',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[mock_thermostat][button.mock_thermostat_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.mock_thermostat_identify_1',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'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-0000000000000096-MatterNodeDevice-1-IdentifyButton-3-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[mock_thermostat][button.mock_thermostat_identify_1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'identify',
|
||||
'friendly_name': 'Mock Thermostat Identify (1)',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.mock_thermostat_identify_1',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_buttons[multi_endpoint_light][button.inovelli_identify_1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
@@ -325,77 +325,6 @@
|
||||
'state': 'cool',
|
||||
})
|
||||
# ---
|
||||
# name: test_climates[mock_thermostat][climate.mock_thermostat-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'hvac_modes': list([
|
||||
<HVACMode.OFF: 'off'>,
|
||||
<HVACMode.HEAT: 'heat'>,
|
||||
<HVACMode.COOL: 'cool'>,
|
||||
<HVACMode.HEAT_COOL: 'heat_cool'>,
|
||||
]),
|
||||
'max_temp': 32.0,
|
||||
'min_temp': 7.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_thermostat',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <ClimateEntityFeature: 387>,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-0000000000000096-MatterNodeDevice-1-MatterThermostat-513-0',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_climates[mock_thermostat][climate.mock_thermostat-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'current_temperature': 18.0,
|
||||
'friendly_name': 'Mock Thermostat',
|
||||
'hvac_action': <HVACAction.HEATING: 'heating'>,
|
||||
'hvac_modes': list([
|
||||
<HVACMode.OFF: 'off'>,
|
||||
<HVACMode.HEAT: 'heat'>,
|
||||
<HVACMode.COOL: 'cool'>,
|
||||
<HVACMode.HEAT_COOL: 'heat_cool'>,
|
||||
]),
|
||||
'max_temp': 32.0,
|
||||
'min_temp': 7.0,
|
||||
'supported_features': <ClimateEntityFeature: 387>,
|
||||
'target_temp_high': 26.0,
|
||||
'target_temp_low': 20.0,
|
||||
'temperature': None,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'climate.mock_thermostat',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'heat_cool',
|
||||
})
|
||||
# ---
|
||||
# name: test_climates[room_airconditioner][climate.room_airconditioner-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
@@ -1105,75 +1105,6 @@
|
||||
'state': 'Vacuum & Mop',
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[eufy_vacuum_omni_e28][select.2bavs_ab6031x_44pe_clean_mode-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'Auto Mop & Vacuum',
|
||||
'Auto Vacuum',
|
||||
'Auto Deep Mop & Vacuum',
|
||||
'Quick Mop & Vacuum',
|
||||
'Quick Vacuum',
|
||||
'Low Noise Mop & Vacuum',
|
||||
'Low Noise Vacuum',
|
||||
'Eufy Customize Mode',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': None,
|
||||
'entity_id': 'select.2bavs_ab6031x_44pe_clean_mode',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Clean mode',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'clean_mode',
|
||||
'unique_id': '00000000000004D2-0000000000000028-MatterNodeDevice-1-MatterRvcCleanMode-85-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[eufy_vacuum_omni_e28][select.2bavs_ab6031x_44pe_clean_mode-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': '2BAVS-AB6031X-44PE Clean mode',
|
||||
'options': list([
|
||||
'Auto Mop & Vacuum',
|
||||
'Auto Vacuum',
|
||||
'Auto Deep Mop & Vacuum',
|
||||
'Quick Mop & Vacuum',
|
||||
'Quick Vacuum',
|
||||
'Low Noise Mop & Vacuum',
|
||||
'Low Noise Vacuum',
|
||||
'Eufy Customize Mode',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.2bavs_ab6031x_44pe_clean_mode',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'Auto Mop & Vacuum',
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[eve_energy_20ecn4101][select.eve_energy_20ecn4101_power_on_behavior_on_startup_bottom-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -2639,63 +2570,6 @@
|
||||
'state': 'silent',
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[mock_thermostat][select.mock_thermostat_temperature_display_mode-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'Celsius',
|
||||
'Fahrenheit',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'select.mock_thermostat_temperature_display_mode',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Temperature display mode',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'temperature_display_mode',
|
||||
'unique_id': '00000000000004D2-0000000000000096-MatterNodeDevice-1-TrvTemperatureDisplayMode-516-0',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[mock_thermostat][select.mock_thermostat_temperature_display_mode-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Mock Thermostat Temperature display mode',
|
||||
'options': list([
|
||||
'Celsius',
|
||||
'Fahrenheit',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.mock_thermostat_temperature_display_mode',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'Celsius',
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[mounted_dimmable_load_control_fixture][select.mock_mounted_dimmable_load_control_power_on_behavior_on_startup-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
@@ -3786,279 +3786,6 @@
|
||||
'state': '0.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[eufy_vacuum_omni_e28][sensor.2bavs_ab6031x_44pe_battery-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.2bavs_ab6031x_44pe_battery',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.BATTERY: 'battery'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Battery',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-0000000000000028-MatterNodeDevice-1-PowerSource-47-12',
|
||||
'unit_of_measurement': '%',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[eufy_vacuum_omni_e28][sensor.2bavs_ab6031x_44pe_battery-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'battery',
|
||||
'friendly_name': '2BAVS-AB6031X-44PE Battery',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': '%',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.2bavs_ab6031x_44pe_battery',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '100',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[eufy_vacuum_omni_e28][sensor.2bavs_ab6031x_44pe_battery_charge_state-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'not_charging',
|
||||
'charging',
|
||||
'full_charge',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.2bavs_ab6031x_44pe_battery_charge_state',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Battery charge state',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'battery_charge_state',
|
||||
'unique_id': '00000000000004D2-0000000000000028-MatterNodeDevice-1-PowerSourceBatChargeState-47-26',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[eufy_vacuum_omni_e28][sensor.2bavs_ab6031x_44pe_battery_charge_state-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'enum',
|
||||
'friendly_name': '2BAVS-AB6031X-44PE Battery charge state',
|
||||
'options': list([
|
||||
'not_charging',
|
||||
'charging',
|
||||
'full_charge',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.2bavs_ab6031x_44pe_battery_charge_state',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'full_charge',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[eufy_vacuum_omni_e28][sensor.2bavs_ab6031x_44pe_operational_error-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'no_error',
|
||||
'unable_to_start_or_resume',
|
||||
'unable_to_complete_operation',
|
||||
'command_invalid_in_state',
|
||||
'failed_to_find_charging_dock',
|
||||
'stuck',
|
||||
'dust_bin_missing',
|
||||
'dust_bin_full',
|
||||
'water_tank_empty',
|
||||
'water_tank_missing',
|
||||
'water_tank_lid_open',
|
||||
'mop_cleaning_pad_missing',
|
||||
'low_battery',
|
||||
'cannot_reach_target_area',
|
||||
'dirty_water_tank_full',
|
||||
'dirty_water_tank_missing',
|
||||
'wheels_jammed',
|
||||
'brush_jammed',
|
||||
'navigation_sensor_obscured',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.2bavs_ab6031x_44pe_operational_error',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Operational error',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'operational_error',
|
||||
'unique_id': '00000000000004D2-0000000000000028-MatterNodeDevice-1-RvcOperationalStateOperationalError-97-5',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[eufy_vacuum_omni_e28][sensor.2bavs_ab6031x_44pe_operational_error-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'enum',
|
||||
'friendly_name': '2BAVS-AB6031X-44PE Operational error',
|
||||
'options': list([
|
||||
'no_error',
|
||||
'unable_to_start_or_resume',
|
||||
'unable_to_complete_operation',
|
||||
'command_invalid_in_state',
|
||||
'failed_to_find_charging_dock',
|
||||
'stuck',
|
||||
'dust_bin_missing',
|
||||
'dust_bin_full',
|
||||
'water_tank_empty',
|
||||
'water_tank_missing',
|
||||
'water_tank_lid_open',
|
||||
'mop_cleaning_pad_missing',
|
||||
'low_battery',
|
||||
'cannot_reach_target_area',
|
||||
'dirty_water_tank_full',
|
||||
'dirty_water_tank_missing',
|
||||
'wheels_jammed',
|
||||
'brush_jammed',
|
||||
'navigation_sensor_obscured',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.2bavs_ab6031x_44pe_operational_error',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'no_error',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[eufy_vacuum_omni_e28][sensor.2bavs_ab6031x_44pe_operational_state-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'stopped',
|
||||
'running',
|
||||
'paused',
|
||||
'error',
|
||||
'seeking_charger',
|
||||
'charging',
|
||||
'docked',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.2bavs_ab6031x_44pe_operational_state',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Operational state',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'operational_state',
|
||||
'unique_id': '00000000000004D2-0000000000000028-MatterNodeDevice-1-RvcOperationalState-97-4',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[eufy_vacuum_omni_e28][sensor.2bavs_ab6031x_44pe_operational_state-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'enum',
|
||||
'friendly_name': '2BAVS-AB6031X-44PE Operational state',
|
||||
'options': list([
|
||||
'stopped',
|
||||
'running',
|
||||
'paused',
|
||||
'error',
|
||||
'seeking_charger',
|
||||
'charging',
|
||||
'docked',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.2bavs_ab6031x_44pe_operational_state',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'stopped',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[eve_contact_sensor][sensor.eve_door_battery-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -7532,167 +7259,6 @@
|
||||
'state': 'stopped',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[mock_thermostat][sensor.mock_thermostat_heating_demand-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.mock_thermostat_heating_demand',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Heating demand',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'pi_heating_demand',
|
||||
'unique_id': '00000000000004D2-0000000000000096-MatterNodeDevice-1-ThermostatPIHeatingDemand-513-8',
|
||||
'unit_of_measurement': '%',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[mock_thermostat][sensor.mock_thermostat_heating_demand-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Mock Thermostat Heating demand',
|
||||
'unit_of_measurement': '%',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_thermostat_heating_demand',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '25',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[mock_thermostat][sensor.mock_thermostat_outdoor_temperature-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_thermostat_outdoor_temperature',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 1,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Outdoor temperature',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'outdoor_temperature',
|
||||
'unique_id': '00000000000004D2-0000000000000096-MatterNodeDevice-1-ThermostatOutdoorTemperature-513-1',
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[mock_thermostat][sensor.mock_thermostat_outdoor_temperature-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'temperature',
|
||||
'friendly_name': 'Mock Thermostat Outdoor temperature',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_thermostat_outdoor_temperature',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '5.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[mock_thermostat][sensor.mock_thermostat_temperature-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_thermostat_temperature',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 1,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Temperature',
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-0000000000000096-MatterNodeDevice-1-ThermostatLocalTemperature-513-0',
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[mock_thermostat][sensor.mock_thermostat_temperature-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'temperature',
|
||||
'friendly_name': 'Mock Thermostat Temperature',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_thermostat_temperature',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '18.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[multi_endpoint_light][sensor.inovelli_current_switch_position_config-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
@@ -48,55 +48,6 @@
|
||||
'state': 'docked',
|
||||
})
|
||||
# ---
|
||||
# name: test_vacuum[eufy_vacuum_omni_e28][vacuum.2bavs_ab6031x_44pe-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': 'vacuum',
|
||||
'entity_category': None,
|
||||
'entity_id': 'vacuum.2bavs_ab6031x_44pe',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': None,
|
||||
'platform': 'matter',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <VacuumEntityFeature: 12828>,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-0000000000000028-MatterNodeDevice-1-MatterVacuumCleaner-84-1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_vacuum[eufy_vacuum_omni_e28][vacuum.2bavs_ab6031x_44pe-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': '2BAVS-AB6031X-44PE',
|
||||
'supported_features': <VacuumEntityFeature: 12828>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'vacuum.2bavs_ab6031x_44pe',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'idle',
|
||||
})
|
||||
# ---
|
||||
# name: test_vacuum[switchbot_k11_plus][vacuum.k11-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
@@ -435,160 +435,3 @@ async def test_shutter_problem(
|
||||
state = hass.states.get("binary_sensor.eve_shutter_switch_20eci1701_problem")
|
||||
assert state
|
||||
assert state.state == "on"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("node_fixture", ["mock_thermostat"])
|
||||
async def test_thermostat_remote_sensing(
|
||||
hass: HomeAssistant,
|
||||
matter_client: MagicMock,
|
||||
matter_node: MatterNode,
|
||||
) -> None:
|
||||
"""Test thermostat remote sensing binary sensors."""
|
||||
remote_sensing_attribute = clusters.Thermostat.Attributes.RemoteSensing
|
||||
|
||||
# Test initial state (RemoteSensing = 0, all bits off)
|
||||
state = hass.states.get(
|
||||
"binary_sensor.mock_thermostat_local_temperature_remote_sensing"
|
||||
)
|
||||
assert state
|
||||
assert state.state == "off"
|
||||
|
||||
state = hass.states.get(
|
||||
"binary_sensor.mock_thermostat_outdoor_temperature_remote_sensing"
|
||||
)
|
||||
assert state
|
||||
assert state.state == "off"
|
||||
|
||||
state = hass.states.get("binary_sensor.mock_thermostat_occupancy_remote_sensing")
|
||||
assert state
|
||||
assert state.state == "off"
|
||||
|
||||
# Set LocalTemperature bit (bit 0)
|
||||
set_node_attribute(
|
||||
matter_node,
|
||||
1,
|
||||
remote_sensing_attribute.cluster_id,
|
||||
remote_sensing_attribute.attribute_id,
|
||||
1,
|
||||
)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get(
|
||||
"binary_sensor.mock_thermostat_local_temperature_remote_sensing"
|
||||
)
|
||||
assert state
|
||||
assert state.state == "on"
|
||||
|
||||
state = hass.states.get(
|
||||
"binary_sensor.mock_thermostat_outdoor_temperature_remote_sensing"
|
||||
)
|
||||
assert state
|
||||
assert state.state == "off"
|
||||
|
||||
state = hass.states.get("binary_sensor.mock_thermostat_occupancy_remote_sensing")
|
||||
assert state
|
||||
assert state.state == "off"
|
||||
|
||||
# Set OutdoorTemperature bit (bit 1)
|
||||
set_node_attribute(
|
||||
matter_node,
|
||||
1,
|
||||
remote_sensing_attribute.cluster_id,
|
||||
remote_sensing_attribute.attribute_id,
|
||||
2,
|
||||
)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get(
|
||||
"binary_sensor.mock_thermostat_local_temperature_remote_sensing"
|
||||
)
|
||||
assert state
|
||||
assert state.state == "off"
|
||||
|
||||
state = hass.states.get(
|
||||
"binary_sensor.mock_thermostat_outdoor_temperature_remote_sensing"
|
||||
)
|
||||
assert state
|
||||
assert state.state == "on"
|
||||
|
||||
state = hass.states.get("binary_sensor.mock_thermostat_occupancy_remote_sensing")
|
||||
assert state
|
||||
assert state.state == "off"
|
||||
|
||||
# Set Occupancy bit (bit 2)
|
||||
set_node_attribute(
|
||||
matter_node,
|
||||
1,
|
||||
remote_sensing_attribute.cluster_id,
|
||||
remote_sensing_attribute.attribute_id,
|
||||
4,
|
||||
)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get(
|
||||
"binary_sensor.mock_thermostat_local_temperature_remote_sensing"
|
||||
)
|
||||
assert state
|
||||
assert state.state == "off"
|
||||
|
||||
state = hass.states.get(
|
||||
"binary_sensor.mock_thermostat_outdoor_temperature_remote_sensing"
|
||||
)
|
||||
assert state
|
||||
assert state.state == "off"
|
||||
|
||||
state = hass.states.get("binary_sensor.mock_thermostat_occupancy_remote_sensing")
|
||||
assert state
|
||||
assert state.state == "on"
|
||||
|
||||
# Set multiple bits (bits 0 and 2 = value 5)
|
||||
set_node_attribute(
|
||||
matter_node,
|
||||
1,
|
||||
remote_sensing_attribute.cluster_id,
|
||||
remote_sensing_attribute.attribute_id,
|
||||
5,
|
||||
)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get(
|
||||
"binary_sensor.mock_thermostat_local_temperature_remote_sensing"
|
||||
)
|
||||
assert state
|
||||
assert state.state == "on"
|
||||
|
||||
state = hass.states.get(
|
||||
"binary_sensor.mock_thermostat_outdoor_temperature_remote_sensing"
|
||||
)
|
||||
assert state
|
||||
assert state.state == "off"
|
||||
|
||||
state = hass.states.get("binary_sensor.mock_thermostat_occupancy_remote_sensing")
|
||||
assert state
|
||||
assert state.state == "on"
|
||||
|
||||
# Set all bits (value 7)
|
||||
set_node_attribute(
|
||||
matter_node,
|
||||
1,
|
||||
remote_sensing_attribute.cluster_id,
|
||||
remote_sensing_attribute.attribute_id,
|
||||
7,
|
||||
)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get(
|
||||
"binary_sensor.mock_thermostat_local_temperature_remote_sensing"
|
||||
)
|
||||
assert state
|
||||
assert state.state == "on"
|
||||
|
||||
state = hass.states.get(
|
||||
"binary_sensor.mock_thermostat_outdoor_temperature_remote_sensing"
|
||||
)
|
||||
assert state
|
||||
assert state.state == "on"
|
||||
|
||||
state = hass.states.get("binary_sensor.mock_thermostat_occupancy_remote_sensing")
|
||||
assert state
|
||||
assert state.state == "on"
|
||||
|
||||
@@ -1584,7 +1584,6 @@ async def test_discovery_with_object_id(
|
||||
async def test_discovery_with_default_entity_id_for_previous_deleted_entity(
|
||||
hass: HomeAssistant,
|
||||
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test discovering an MQTT entity with default_entity_id and unique_id."""
|
||||
|
||||
@@ -1599,7 +1598,6 @@ async def test_discovery_with_default_entity_id_for_previous_deleted_entity(
|
||||
)
|
||||
initial_entity_id = "sensor.hello_id"
|
||||
new_entity_id = "sensor.updated_hello_id"
|
||||
later_entity_id = "sensor.later_hello_id"
|
||||
name = "Hello World 11"
|
||||
domain = "sensor"
|
||||
|
||||
@@ -1628,14 +1626,6 @@ async def test_discovery_with_default_entity_id_for_previous_deleted_entity(
|
||||
assert state.name == name
|
||||
assert (domain, "object bla") in hass.data["mqtt"].discovery_already_discovered
|
||||
|
||||
# Assert the entity ID can be changed later
|
||||
entity_registry.async_update_entity(new_entity_id, new_entity_id=later_entity_id)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get(later_entity_id)
|
||||
|
||||
assert state is not None
|
||||
assert state.name == name
|
||||
|
||||
|
||||
async def test_discovery_incl_nodeid(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"flow_rate": {
|
||||
"value": 150,
|
||||
"unit": "l/s"
|
||||
"unit": "L/s"
|
||||
},
|
||||
"ph_type_dosing": {
|
||||
"value": "alcalyne",
|
||||
@@ -75,14 +75,6 @@
|
||||
"orp_calibration_slope": {
|
||||
"value": 0.96,
|
||||
"unit": "mV"
|
||||
},
|
||||
"water_meter_total_permanent": {
|
||||
"value": 12345.67,
|
||||
"unit": "m3"
|
||||
},
|
||||
"water_meter_total_resettable": {
|
||||
"value": 123.45,
|
||||
"unit": "m3"
|
||||
}
|
||||
},
|
||||
"binary_sensor": {
|
||||
|
||||
@@ -1054,59 +1054,3 @@
|
||||
'state': '25',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_sensors[sensor.pool_device_totalizer-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.pool_device_totalizer',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 2,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.VOLUME: 'volume'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Totalizer',
|
||||
'platform': 'pooldose',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'water_meter_total_permanent',
|
||||
'unique_id': 'TEST123456789_water_meter_total_permanent',
|
||||
'unit_of_measurement': <UnitOfVolume.CUBIC_METERS: 'm³'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_sensors[sensor.pool_device_totalizer-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'volume',
|
||||
'friendly_name': 'Pool Device Totalizer',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
'unit_of_measurement': <UnitOfVolume.CUBIC_METERS: 'm³'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.pool_device_totalizer',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '12345.67',
|
||||
})
|
||||
# ---
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user