mirror of
https://github.com/home-assistant/core.git
synced 2026-01-12 10:08:19 +00:00
Compare commits
22 Commits
2026.1.0b4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49086b2a76 | ||
|
|
1f28fe9933 | ||
|
|
4465aa264c | ||
|
|
2c1bc96161 | ||
|
|
7127159a5b | ||
|
|
9f0eb6f077 | ||
|
|
da19cc06e3 | ||
|
|
fd92377cf2 | ||
|
|
c201938b8b | ||
|
|
b3765204b1 | ||
|
|
786257e051 | ||
|
|
9559634151 | ||
|
|
cf12ed8f08 | ||
|
|
e213f49c75 | ||
|
|
09c7cc113a | ||
|
|
e1e7e039a9 | ||
|
|
05a0f0d23f | ||
|
|
d3853019eb | ||
|
|
ccbaac55b3 | ||
|
|
771292ced9 | ||
|
|
5d4262e8b3 | ||
|
|
d96da9a639 |
@@ -7,7 +7,7 @@ import asyncio
|
||||
from collections.abc import Callable, Mapping
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from typing import Any, Protocol, cast
|
||||
from typing import Any, Literal, Protocol, cast
|
||||
|
||||
from propcache.api import cached_property
|
||||
import voluptuous as vol
|
||||
@@ -16,7 +16,10 @@ from homeassistant.components import labs, websocket_api
|
||||
from homeassistant.components.blueprint import CONF_USE_BLUEPRINT
|
||||
from homeassistant.components.labs import async_listen as async_labs_listen
|
||||
from homeassistant.const import (
|
||||
ATTR_AREA_ID,
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_FLOOR_ID,
|
||||
ATTR_LABEL_ID,
|
||||
ATTR_MODE,
|
||||
ATTR_NAME,
|
||||
CONF_ACTIONS,
|
||||
@@ -30,6 +33,7 @@ from homeassistant.const import (
|
||||
CONF_OPTIONS,
|
||||
CONF_PATH,
|
||||
CONF_PLATFORM,
|
||||
CONF_TARGET,
|
||||
CONF_TRIGGERS,
|
||||
CONF_VARIABLES,
|
||||
CONF_ZONE,
|
||||
@@ -588,20 +592,32 @@ class AutomationEntity(BaseAutomationEntity, RestoreEntity):
|
||||
"""Return True if entity is on."""
|
||||
return self._async_detach_triggers is not None or self._is_enabled
|
||||
|
||||
@property
|
||||
@cached_property
|
||||
def referenced_labels(self) -> set[str]:
|
||||
"""Return a set of referenced labels."""
|
||||
return self.action_script.referenced_labels
|
||||
referenced = self.action_script.referenced_labels
|
||||
|
||||
@property
|
||||
for conf in self._trigger_config:
|
||||
referenced |= set(_get_targets_from_trigger_config(conf, ATTR_LABEL_ID))
|
||||
return referenced
|
||||
|
||||
@cached_property
|
||||
def referenced_floors(self) -> set[str]:
|
||||
"""Return a set of referenced floors."""
|
||||
return self.action_script.referenced_floors
|
||||
referenced = self.action_script.referenced_floors
|
||||
|
||||
for conf in self._trigger_config:
|
||||
referenced |= set(_get_targets_from_trigger_config(conf, ATTR_FLOOR_ID))
|
||||
return referenced
|
||||
|
||||
@cached_property
|
||||
def referenced_areas(self) -> set[str]:
|
||||
"""Return a set of referenced areas."""
|
||||
return self.action_script.referenced_areas
|
||||
referenced = self.action_script.referenced_areas
|
||||
|
||||
for conf in self._trigger_config:
|
||||
referenced |= set(_get_targets_from_trigger_config(conf, ATTR_AREA_ID))
|
||||
return referenced
|
||||
|
||||
@property
|
||||
def referenced_blueprint(self) -> str | None:
|
||||
@@ -1209,6 +1225,9 @@ def _trigger_extract_devices(trigger_conf: dict) -> list[str]:
|
||||
if trigger_conf[CONF_PLATFORM] == "tag" and CONF_DEVICE_ID in trigger_conf:
|
||||
return trigger_conf[CONF_DEVICE_ID] # type: ignore[no-any-return]
|
||||
|
||||
if target_devices := _get_targets_from_trigger_config(trigger_conf, CONF_DEVICE_ID):
|
||||
return target_devices
|
||||
|
||||
return []
|
||||
|
||||
|
||||
@@ -1239,9 +1258,28 @@ def _trigger_extract_entities(trigger_conf: dict) -> list[str]:
|
||||
):
|
||||
return [trigger_conf[CONF_EVENT_DATA][CONF_ENTITY_ID]]
|
||||
|
||||
if target_entities := _get_targets_from_trigger_config(
|
||||
trigger_conf, CONF_ENTITY_ID
|
||||
):
|
||||
return target_entities
|
||||
|
||||
return []
|
||||
|
||||
|
||||
@callback
|
||||
def _get_targets_from_trigger_config(
|
||||
config: dict,
|
||||
target: Literal["entity_id", "device_id", "area_id", "floor_id", "label_id"],
|
||||
) -> list[str]:
|
||||
"""Extract targets from a target config."""
|
||||
if not (target_conf := config.get(CONF_TARGET)):
|
||||
return []
|
||||
if not (targets := target_conf.get(target)):
|
||||
return []
|
||||
|
||||
return [targets] if isinstance(targets, str) else targets
|
||||
|
||||
|
||||
@websocket_api.websocket_command({"type": "automation/config", "entity_id": str})
|
||||
def websocket_config(
|
||||
hass: HomeAssistant,
|
||||
|
||||
@@ -11,6 +11,7 @@ from homeassistant.const import CONF_HOST, CONF_MAC, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.aiohttp_client import async_create_clientsession
|
||||
|
||||
from .const import CONF_USE_SSL
|
||||
from .coordinator import BraviaTVConfigEntry, BraviaTVCoordinator
|
||||
|
||||
PLATFORMS: Final[list[Platform]] = [
|
||||
@@ -26,11 +27,12 @@ async def async_setup_entry(
|
||||
"""Set up a config entry."""
|
||||
host = config_entry.data[CONF_HOST]
|
||||
mac = config_entry.data[CONF_MAC]
|
||||
ssl = config_entry.data.get(CONF_USE_SSL, False)
|
||||
|
||||
session = async_create_clientsession(
|
||||
hass, cookie_jar=CookieJar(unsafe=True, quote_cookie=False)
|
||||
)
|
||||
client = BraviaClient(host, mac, session=session)
|
||||
client = BraviaClient(host, mac, session=session, ssl=ssl)
|
||||
coordinator = BraviaTVCoordinator(
|
||||
hass=hass,
|
||||
config_entry=config_entry,
|
||||
|
||||
@@ -28,6 +28,7 @@ from .const import (
|
||||
ATTR_MODEL,
|
||||
CONF_NICKNAME,
|
||||
CONF_USE_PSK,
|
||||
CONF_USE_SSL,
|
||||
DOMAIN,
|
||||
NICKNAME_PREFIX,
|
||||
)
|
||||
@@ -46,11 +47,12 @@ class BraviaTVConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
def create_client(self) -> None:
|
||||
"""Create Bravia TV client from config."""
|
||||
host = self.device_config[CONF_HOST]
|
||||
ssl = self.device_config[CONF_USE_SSL]
|
||||
session = async_create_clientsession(
|
||||
self.hass,
|
||||
cookie_jar=CookieJar(unsafe=True, quote_cookie=False),
|
||||
)
|
||||
self.client = BraviaClient(host=host, session=session)
|
||||
self.client = BraviaClient(host=host, session=session, ssl=ssl)
|
||||
|
||||
async def gen_instance_ids(self) -> tuple[str, str]:
|
||||
"""Generate client_id and nickname."""
|
||||
@@ -123,10 +125,10 @@ class BraviaTVConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle authorize step."""
|
||||
self.create_client()
|
||||
|
||||
if user_input is not None:
|
||||
self.device_config[CONF_USE_PSK] = user_input[CONF_USE_PSK]
|
||||
self.device_config[CONF_USE_SSL] = user_input[CONF_USE_SSL]
|
||||
self.create_client()
|
||||
if user_input[CONF_USE_PSK]:
|
||||
return await self.async_step_psk()
|
||||
return await self.async_step_pin()
|
||||
@@ -136,6 +138,7 @@ class BraviaTVConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_USE_PSK, default=False): bool,
|
||||
vol.Required(CONF_USE_SSL, default=False): bool,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
@@ -12,6 +12,7 @@ ATTR_MODEL: Final = "model"
|
||||
|
||||
CONF_NICKNAME: Final = "nickname"
|
||||
CONF_USE_PSK: Final = "use_psk"
|
||||
CONF_USE_SSL: Final = "use_ssl"
|
||||
|
||||
DOMAIN: Final = "braviatv"
|
||||
LEGACY_CLIENT_ID: Final = "HomeAssistant"
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
"step": {
|
||||
"authorize": {
|
||||
"data": {
|
||||
"use_psk": "Use PSK authentication"
|
||||
"use_psk": "Use PSK authentication",
|
||||
"use_ssl": "Use SSL connection"
|
||||
},
|
||||
"description": "Make sure that «Control remotely» is enabled on your TV, go to: \nSettings -> Network -> Remote device settings -> Control remotely. \n\nThere are two authorization methods: PIN code or PSK (Pre-Shared Key). \nAuthorization via PSK is recommended as more stable.",
|
||||
"description": "Make sure that «Control remotely» is enabled on your TV. Go to: \nSettings -> Network -> Remote device settings -> Control remotely. \n\nThere are two authorization methods: PIN code or PSK (Pre-Shared Key). \nAuthorization via PSK is recommended, as it is more stable. \n\nUse an SSL connection only if your TV supports this connection type.",
|
||||
"title": "Authorize Sony Bravia TV"
|
||||
},
|
||||
"confirm": {
|
||||
|
||||
@@ -33,7 +33,7 @@ HVAC_MODE_CHANGED_TRIGGER_SCHEMA = ENTITY_STATE_TRIGGER_SCHEMA_FIRST_LAST.extend
|
||||
{
|
||||
vol.Required(CONF_OPTIONS): {
|
||||
vol.Required(CONF_HVAC_MODE): vol.All(
|
||||
cv.ensure_list, vol.Length(min=1), [HVACMode]
|
||||
cv.ensure_list, vol.Length(min=1), [vol.Coerce(HVACMode)]
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/conversation",
|
||||
"integration_type": "entity",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["hassil==3.5.0", "home-assistant-intents==2026.1.1"]
|
||||
"requirements": ["hassil==3.5.0", "home-assistant-intents==2026.1.6"]
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["sleekxmppfs", "sucks", "deebot_client"],
|
||||
"requirements": ["py-sucks==0.9.11", "deebot-client==17.0.0"]
|
||||
"requirements": ["py-sucks==0.9.11", "deebot-client==17.0.1"]
|
||||
}
|
||||
|
||||
@@ -23,5 +23,5 @@
|
||||
"winter_mode": {}
|
||||
},
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["home-assistant-frontend==20251229.1"]
|
||||
"requirements": ["home-assistant-frontend==20260107.0"]
|
||||
}
|
||||
|
||||
@@ -116,6 +116,8 @@ class IsraelRailEntitySensor(
|
||||
@property
|
||||
def native_value(self) -> StateType | datetime:
|
||||
"""Return the state of the sensor."""
|
||||
if self.entity_description.index >= len(self.coordinator.data):
|
||||
return None
|
||||
return self.entity_description.value_fn(
|
||||
self.coordinator.data[self.entity_description.index]
|
||||
)
|
||||
|
||||
@@ -128,8 +128,9 @@ class RingCam(RingEntity[RingDoorBell], Camera):
|
||||
self._device = self._get_coordinator_data().get_video_device(
|
||||
self._device.device_api_id
|
||||
)
|
||||
|
||||
history_data = self._device.last_history
|
||||
if history_data:
|
||||
if history_data and self._device.has_subscription:
|
||||
self._last_event = history_data[0]
|
||||
# will call async_update to update the attributes and get the
|
||||
# video url from the api
|
||||
@@ -154,8 +155,16 @@ class RingCam(RingEntity[RingDoorBell], Camera):
|
||||
self, width: int | None = None, height: int | None = None
|
||||
) -> bytes | None:
|
||||
"""Return a still image response from the camera."""
|
||||
if self._video_url is None:
|
||||
if not self._device.has_subscription:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="no_subscription",
|
||||
)
|
||||
return None
|
||||
|
||||
key = (width, height)
|
||||
if not (image := self._images.get(key)) and self._video_url is not None:
|
||||
if not (image := self._images.get(key)):
|
||||
image = await ffmpeg.async_get_image(
|
||||
self.hass,
|
||||
self._video_url,
|
||||
|
||||
@@ -151,6 +151,9 @@
|
||||
"api_timeout": {
|
||||
"message": "Timeout communicating with Ring API"
|
||||
},
|
||||
"no_subscription": {
|
||||
"message": "Ring Protect subscription required for snapshots"
|
||||
},
|
||||
"sdp_m_line_index_required": {
|
||||
"message": "Error negotiating stream for {device}"
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"loggers": ["roborock"],
|
||||
"quality_scale": "silver",
|
||||
"requirements": [
|
||||
"python-roborock==4.2.0",
|
||||
"python-roborock==4.2.1",
|
||||
"vacuum-map-parser-roborock==0.1.4"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -391,15 +391,6 @@ Q7_B01_SENSOR_DESCRIPTIONS = [
|
||||
translation_key="mop_life_time_left",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
RoborockSensorDescriptionB01(
|
||||
key="total_cleaning_time",
|
||||
value_fn=lambda data: data.real_clean_time,
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||
suggested_unit_of_measurement=UnitOfTime.HOURS,
|
||||
translation_key="total_cleaning_time",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["solarlog_cli"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["solarlog_cli==0.6.1"]
|
||||
"requirements": ["solarlog_cli==0.7.0"]
|
||||
}
|
||||
|
||||
@@ -80,10 +80,6 @@ class TelegramNotificationService(BaseNotificationService):
|
||||
def send_message(self, message="", **kwargs):
|
||||
"""Send a message to a user."""
|
||||
service_data = {ATTR_TARGET: kwargs.get(ATTR_TARGET, self._chat_id)}
|
||||
if ATTR_TITLE in kwargs:
|
||||
service_data.update({ATTR_TITLE: kwargs.get(ATTR_TITLE)})
|
||||
if message:
|
||||
service_data.update({ATTR_MESSAGE: message})
|
||||
data = kwargs.get(ATTR_DATA)
|
||||
|
||||
# Set message tag
|
||||
@@ -161,6 +157,12 @@ class TelegramNotificationService(BaseNotificationService):
|
||||
)
|
||||
|
||||
# Send message
|
||||
|
||||
if ATTR_TITLE in kwargs:
|
||||
service_data.update({ATTR_TITLE: kwargs.get(ATTR_TITLE)})
|
||||
if message:
|
||||
service_data.update({ATTR_MESSAGE: message})
|
||||
|
||||
_LOGGER.debug(
|
||||
"TELEGRAM NOTIFIER calling %s.send_message with %s",
|
||||
TELEGRAM_BOT_DOMAIN,
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/tibber",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["tibber"],
|
||||
"requirements": ["pyTibber==0.34.0"]
|
||||
"requirements": ["pyTibber==0.34.1"]
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["uiprotect", "unifi_discovery"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["uiprotect==7.33.3", "unifi-discovery==1.2.0"],
|
||||
"requirements": ["uiprotect==8.0.0", "unifi-discovery==1.2.0"],
|
||||
"ssdp": [
|
||||
{
|
||||
"manufacturer": "Ubiquiti Networks",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Watts Vision +",
|
||||
"codeowners": ["@theobld-ww", "@devender-verma-ww", "@ssi-spyro"],
|
||||
"config_flow": true,
|
||||
"dependencies": ["application_credentials"],
|
||||
"dependencies": ["application_credentials", "cloud"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/watts",
|
||||
"iot_class": "cloud_polling",
|
||||
"quality_scale": "bronze",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"universal_silabs_flasher",
|
||||
"serialx"
|
||||
],
|
||||
"requirements": ["zha==0.0.82", "serialx==0.5.0"],
|
||||
"requirements": ["zha==0.0.83", "serialx==0.5.0"],
|
||||
"usb": [
|
||||
{
|
||||
"description": "*2652*",
|
||||
|
||||
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
||||
APPLICATION_NAME: Final = "HomeAssistant"
|
||||
MAJOR_VERSION: Final = 2026
|
||||
MINOR_VERSION: Final = 1
|
||||
PATCH_VERSION: Final = "0b4"
|
||||
PATCH_VERSION: Final = "0"
|
||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 13, 2)
|
||||
|
||||
@@ -39,8 +39,8 @@ habluetooth==5.8.0
|
||||
hass-nabucasa==1.7.0
|
||||
hassil==3.5.0
|
||||
home-assistant-bluetooth==1.13.1
|
||||
home-assistant-frontend==20251229.1
|
||||
home-assistant-intents==2026.1.1
|
||||
home-assistant-frontend==20260107.0
|
||||
home-assistant-intents==2026.1.6
|
||||
httpx==0.28.1
|
||||
ifaddr==0.2.0
|
||||
Jinja2==3.1.6
|
||||
@@ -226,3 +226,6 @@ gql<4.0.0
|
||||
|
||||
# Pin pytest-rerunfailures to prevent accidental breaks
|
||||
pytest-rerunfailures==16.0.1
|
||||
|
||||
# Fixes detected blocking call to load_default_certs https://github.com/home-assistant/core/issues/157475
|
||||
aiomqtt>=2.5.0
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "homeassistant"
|
||||
version = "2026.1.0b4"
|
||||
version = "2026.1.0"
|
||||
license = "Apache-2.0"
|
||||
license-files = ["LICENSE*", "homeassistant/backports/LICENSE*"]
|
||||
description = "Open-source home automation platform running on Python 3."
|
||||
|
||||
2
requirements.txt
generated
2
requirements.txt
generated
@@ -27,7 +27,7 @@ ha-ffmpeg==3.2.2
|
||||
hass-nabucasa==1.7.0
|
||||
hassil==3.5.0
|
||||
home-assistant-bluetooth==1.13.1
|
||||
home-assistant-intents==2026.1.1
|
||||
home-assistant-intents==2026.1.6
|
||||
httpx==0.28.1
|
||||
ifaddr==0.2.0
|
||||
Jinja2==3.1.6
|
||||
|
||||
16
requirements_all.txt
generated
16
requirements_all.txt
generated
@@ -782,7 +782,7 @@ debugpy==1.8.17
|
||||
decora-wifi==1.4
|
||||
|
||||
# homeassistant.components.ecovacs
|
||||
deebot-client==17.0.0
|
||||
deebot-client==17.0.1
|
||||
|
||||
# homeassistant.components.ihc
|
||||
# homeassistant.components.namecheapdns
|
||||
@@ -1213,10 +1213,10 @@ hole==0.9.0
|
||||
holidays==0.84
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20251229.1
|
||||
home-assistant-frontend==20260107.0
|
||||
|
||||
# homeassistant.components.conversation
|
||||
home-assistant-intents==2026.1.1
|
||||
home-assistant-intents==2026.1.6
|
||||
|
||||
# homeassistant.components.gentex_homelink
|
||||
homelink-integration-api==0.0.1
|
||||
@@ -1867,7 +1867,7 @@ pyRFXtrx==0.31.1
|
||||
pySDCP==1
|
||||
|
||||
# homeassistant.components.tibber
|
||||
pyTibber==0.34.0
|
||||
pyTibber==0.34.1
|
||||
|
||||
# homeassistant.components.dlink
|
||||
pyW215==0.8.0
|
||||
@@ -2581,7 +2581,7 @@ python-rabbitair==0.0.8
|
||||
python-ripple-api==0.0.3
|
||||
|
||||
# homeassistant.components.roborock
|
||||
python-roborock==4.2.0
|
||||
python-roborock==4.2.1
|
||||
|
||||
# homeassistant.components.smarttub
|
||||
python-smarttub==0.0.46
|
||||
@@ -2896,7 +2896,7 @@ solaredge-local==0.2.3
|
||||
solaredge-web==0.0.1
|
||||
|
||||
# homeassistant.components.solarlog
|
||||
solarlog_cli==0.6.1
|
||||
solarlog_cli==0.7.0
|
||||
|
||||
# homeassistant.components.solax
|
||||
solax==3.2.3
|
||||
@@ -3078,7 +3078,7 @@ typedmonarchmoney==0.4.4
|
||||
uasiren==0.0.1
|
||||
|
||||
# homeassistant.components.unifiprotect
|
||||
uiprotect==7.33.3
|
||||
uiprotect==8.0.0
|
||||
|
||||
# homeassistant.components.landisgyr_heat_meter
|
||||
ultraheat-api==0.5.7
|
||||
@@ -3277,7 +3277,7 @@ zeroconf==0.148.0
|
||||
zeversolar==0.3.2
|
||||
|
||||
# homeassistant.components.zha
|
||||
zha==0.0.82
|
||||
zha==0.0.83
|
||||
|
||||
# homeassistant.components.zhong_hong
|
||||
zhong-hong-hvac==1.0.13
|
||||
|
||||
16
requirements_test_all.txt
generated
16
requirements_test_all.txt
generated
@@ -691,7 +691,7 @@ dbus-fast==3.1.2
|
||||
debugpy==1.8.17
|
||||
|
||||
# homeassistant.components.ecovacs
|
||||
deebot-client==17.0.0
|
||||
deebot-client==17.0.1
|
||||
|
||||
# homeassistant.components.ihc
|
||||
# homeassistant.components.namecheapdns
|
||||
@@ -1071,10 +1071,10 @@ hole==0.9.0
|
||||
holidays==0.84
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20251229.1
|
||||
home-assistant-frontend==20260107.0
|
||||
|
||||
# homeassistant.components.conversation
|
||||
home-assistant-intents==2026.1.1
|
||||
home-assistant-intents==2026.1.6
|
||||
|
||||
# homeassistant.components.gentex_homelink
|
||||
homelink-integration-api==0.0.1
|
||||
@@ -1595,7 +1595,7 @@ pyHomee==1.3.8
|
||||
pyRFXtrx==0.31.1
|
||||
|
||||
# homeassistant.components.tibber
|
||||
pyTibber==0.34.0
|
||||
pyTibber==0.34.1
|
||||
|
||||
# homeassistant.components.dlink
|
||||
pyW215==0.8.0
|
||||
@@ -2165,7 +2165,7 @@ python-pooldose==0.8.1
|
||||
python-rabbitair==0.0.8
|
||||
|
||||
# homeassistant.components.roborock
|
||||
python-roborock==4.2.0
|
||||
python-roborock==4.2.1
|
||||
|
||||
# homeassistant.components.smarttub
|
||||
python-smarttub==0.0.46
|
||||
@@ -2420,7 +2420,7 @@ soco==0.30.13
|
||||
solaredge-web==0.0.1
|
||||
|
||||
# homeassistant.components.solarlog
|
||||
solarlog_cli==0.6.1
|
||||
solarlog_cli==0.7.0
|
||||
|
||||
# homeassistant.components.solax
|
||||
solax==3.2.3
|
||||
@@ -2569,7 +2569,7 @@ typedmonarchmoney==0.4.4
|
||||
uasiren==0.0.1
|
||||
|
||||
# homeassistant.components.unifiprotect
|
||||
uiprotect==7.33.3
|
||||
uiprotect==8.0.0
|
||||
|
||||
# homeassistant.components.landisgyr_heat_meter
|
||||
ultraheat-api==0.5.7
|
||||
@@ -2738,7 +2738,7 @@ zeroconf==0.148.0
|
||||
zeversolar==0.3.2
|
||||
|
||||
# homeassistant.components.zha
|
||||
zha==0.0.82
|
||||
zha==0.0.83
|
||||
|
||||
# homeassistant.components.zwave_js
|
||||
zwave-js-server-python==0.67.1
|
||||
|
||||
@@ -217,6 +217,9 @@ gql<4.0.0
|
||||
|
||||
# Pin pytest-rerunfailures to prevent accidental breaks
|
||||
pytest-rerunfailures==16.0.1
|
||||
|
||||
# Fixes detected blocking call to load_default_certs https://github.com/home-assistant/core/issues/157475
|
||||
aiomqtt>=2.5.0
|
||||
"""
|
||||
|
||||
GENERATED_MESSAGE = (
|
||||
|
||||
@@ -2232,6 +2232,202 @@ async def test_extraction_functions(
|
||||
assert automation.blueprint_in_automation(hass, "automation.test3") is None
|
||||
|
||||
|
||||
async def test_extraction_functions_with_targets(
|
||||
hass: HomeAssistant,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
) -> None:
|
||||
"""Test extraction functions with targets in triggers.
|
||||
|
||||
This test verifies that targets specified in trigger configurations
|
||||
(using new-style triggers that support target) are properly extracted for
|
||||
entity, device, area, floor, and label references.
|
||||
"""
|
||||
config_entry = MockConfigEntry(domain="fake_integration", data={})
|
||||
config_entry.mock_state(hass, ConfigEntryState.LOADED)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
trigger_device = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry.entry_id,
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "00:00:00:00:00:01")},
|
||||
)
|
||||
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
await async_setup_component(
|
||||
hass, "scene", {"scene": {"name": "test", "entities": {}}}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Enable the new_triggers_conditions feature flag to allow new-style triggers
|
||||
assert await async_setup_component(hass, "labs", {})
|
||||
ws_client = await hass_ws_client(hass)
|
||||
await ws_client.send_json_auto_id(
|
||||
{
|
||||
"type": "labs/update",
|
||||
"domain": "automation",
|
||||
"preview_feature": "new_triggers_conditions",
|
||||
"enabled": True,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
DOMAIN,
|
||||
{
|
||||
DOMAIN: [
|
||||
{
|
||||
"alias": "test1",
|
||||
"triggers": [
|
||||
# Single entity_id in target
|
||||
{
|
||||
"trigger": "scene.activated",
|
||||
"target": {"entity_id": "scene.target_entity"},
|
||||
},
|
||||
# Multiple entity_ids in target
|
||||
{
|
||||
"trigger": "scene.activated",
|
||||
"target": {
|
||||
"entity_id": [
|
||||
"scene.target_entity_list1",
|
||||
"scene.target_entity_list2",
|
||||
]
|
||||
},
|
||||
},
|
||||
# Single device_id in target
|
||||
{
|
||||
"trigger": "scene.activated",
|
||||
"target": {"device_id": trigger_device.id},
|
||||
},
|
||||
# Multiple device_ids in target
|
||||
{
|
||||
"trigger": "scene.activated",
|
||||
"target": {
|
||||
"device_id": [
|
||||
"target-device-1",
|
||||
"target-device-2",
|
||||
]
|
||||
},
|
||||
},
|
||||
# Single area_id in target
|
||||
{
|
||||
"trigger": "scene.activated",
|
||||
"target": {"area_id": "area-target-single"},
|
||||
},
|
||||
# Multiple area_ids in target
|
||||
{
|
||||
"trigger": "scene.activated",
|
||||
"target": {"area_id": ["area-target-1", "area-target-2"]},
|
||||
},
|
||||
# Single floor_id in target
|
||||
{
|
||||
"trigger": "scene.activated",
|
||||
"target": {"floor_id": "floor-target-single"},
|
||||
},
|
||||
# Multiple floor_ids in target
|
||||
{
|
||||
"trigger": "scene.activated",
|
||||
"target": {
|
||||
"floor_id": ["floor-target-1", "floor-target-2"]
|
||||
},
|
||||
},
|
||||
# Single label_id in target
|
||||
{
|
||||
"trigger": "scene.activated",
|
||||
"target": {"label_id": "label-target-single"},
|
||||
},
|
||||
# Multiple label_ids in target
|
||||
{
|
||||
"trigger": "scene.activated",
|
||||
"target": {
|
||||
"label_id": ["label-target-1", "label-target-2"]
|
||||
},
|
||||
},
|
||||
# Combined targets
|
||||
{
|
||||
"trigger": "scene.activated",
|
||||
"target": {
|
||||
"entity_id": "scene.combined_entity",
|
||||
"device_id": "combined-device",
|
||||
"area_id": "combined-area",
|
||||
"floor_id": "combined-floor",
|
||||
"label_id": "combined-label",
|
||||
},
|
||||
},
|
||||
],
|
||||
"conditions": [],
|
||||
"actions": [
|
||||
{
|
||||
"action": "test.script",
|
||||
"data": {"entity_id": "light.action_entity"},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
# Test entity extraction from trigger targets
|
||||
assert set(automation.entities_in_automation(hass, "automation.test1")) == {
|
||||
"scene.target_entity",
|
||||
"scene.target_entity_list1",
|
||||
"scene.target_entity_list2",
|
||||
"scene.combined_entity",
|
||||
"light.action_entity",
|
||||
}
|
||||
|
||||
# Test device extraction from trigger targets
|
||||
assert set(automation.devices_in_automation(hass, "automation.test1")) == {
|
||||
trigger_device.id,
|
||||
"target-device-1",
|
||||
"target-device-2",
|
||||
"combined-device",
|
||||
}
|
||||
|
||||
# Test area extraction from trigger targets
|
||||
assert set(automation.areas_in_automation(hass, "automation.test1")) == {
|
||||
"area-target-single",
|
||||
"area-target-1",
|
||||
"area-target-2",
|
||||
"combined-area",
|
||||
}
|
||||
|
||||
# Test floor extraction from trigger targets
|
||||
assert set(automation.floors_in_automation(hass, "automation.test1")) == {
|
||||
"floor-target-single",
|
||||
"floor-target-1",
|
||||
"floor-target-2",
|
||||
"combined-floor",
|
||||
}
|
||||
|
||||
# Test label extraction from trigger targets
|
||||
assert set(automation.labels_in_automation(hass, "automation.test1")) == {
|
||||
"label-target-single",
|
||||
"label-target-1",
|
||||
"label-target-2",
|
||||
"combined-label",
|
||||
}
|
||||
|
||||
# Test automations_with_* functions
|
||||
assert set(automation.automations_with_entity(hass, "scene.target_entity")) == {
|
||||
"automation.test1"
|
||||
}
|
||||
assert set(automation.automations_with_device(hass, trigger_device.id)) == {
|
||||
"automation.test1"
|
||||
}
|
||||
assert set(automation.automations_with_area(hass, "area-target-single")) == {
|
||||
"automation.test1"
|
||||
}
|
||||
assert set(automation.automations_with_floor(hass, "floor-target-single")) == {
|
||||
"automation.test1"
|
||||
}
|
||||
assert set(automation.automations_with_label(hass, "label-target-single")) == {
|
||||
"automation.test1"
|
||||
}
|
||||
|
||||
|
||||
async def test_logbook_humanify_automation_triggered_event(hass: HomeAssistant) -> None:
|
||||
"""Test humanifying Automation Trigger event."""
|
||||
hass.config.components.add("recorder")
|
||||
|
||||
@@ -13,6 +13,7 @@ import pytest
|
||||
from homeassistant.components.braviatv.const import (
|
||||
CONF_NICKNAME,
|
||||
CONF_USE_PSK,
|
||||
CONF_USE_SSL,
|
||||
DOMAIN,
|
||||
NICKNAME_PREFIX,
|
||||
)
|
||||
@@ -131,7 +132,7 @@ async def test_ssdp_discovery(hass: HomeAssistant) -> None:
|
||||
assert result["step_id"] == "authorize"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_USE_PSK: False}
|
||||
result["flow_id"], user_input={CONF_USE_PSK: False, CONF_USE_SSL: False}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
@@ -148,6 +149,7 @@ async def test_ssdp_discovery(hass: HomeAssistant) -> None:
|
||||
CONF_HOST: "bravia-host",
|
||||
CONF_PIN: "1234",
|
||||
CONF_USE_PSK: False,
|
||||
CONF_USE_SSL: False,
|
||||
CONF_MAC: "AA:BB:CC:DD:EE:FF",
|
||||
CONF_CLIENT_ID: uuid,
|
||||
CONF_NICKNAME: f"{NICKNAME_PREFIX} {uuid[:6]}",
|
||||
@@ -307,8 +309,17 @@ async def test_duplicate_error(hass: HomeAssistant) -> None:
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_create_entry(hass: HomeAssistant) -> None:
|
||||
"""Test that entry is added correctly with PIN auth."""
|
||||
@pytest.mark.parametrize(
|
||||
("use_psk", "use_ssl"),
|
||||
[
|
||||
(True, False),
|
||||
(False, False),
|
||||
(True, True),
|
||||
(False, True),
|
||||
],
|
||||
)
|
||||
async def test_create_entry(hass: HomeAssistant, use_psk, use_ssl) -> None:
|
||||
"""Test that entry is added correctly."""
|
||||
uuid = await instance_id.async_get(hass)
|
||||
|
||||
with (
|
||||
@@ -328,14 +339,14 @@ async def test_create_entry(hass: HomeAssistant) -> None:
|
||||
assert result["step_id"] == "authorize"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_USE_PSK: False}
|
||||
result["flow_id"], user_input={CONF_USE_PSK: use_psk, CONF_USE_SSL: use_ssl}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "pin"
|
||||
assert result["step_id"] == "psk" if use_psk else "pin"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_PIN: "1234"}
|
||||
result["flow_id"], user_input={CONF_PIN: "secret"}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
@@ -343,50 +354,18 @@ async def test_create_entry(hass: HomeAssistant) -> None:
|
||||
assert result["title"] == "BRAVIA TV-Model"
|
||||
assert result["data"] == {
|
||||
CONF_HOST: "bravia-host",
|
||||
CONF_PIN: "1234",
|
||||
CONF_USE_PSK: False,
|
||||
CONF_MAC: "AA:BB:CC:DD:EE:FF",
|
||||
CONF_CLIENT_ID: uuid,
|
||||
CONF_NICKNAME: f"{NICKNAME_PREFIX} {uuid[:6]}",
|
||||
}
|
||||
|
||||
|
||||
async def test_create_entry_psk(hass: HomeAssistant) -> None:
|
||||
"""Test that entry is added correctly with PSK auth."""
|
||||
with (
|
||||
patch("pybravia.BraviaClient.connect"),
|
||||
patch("pybravia.BraviaClient.set_wol_mode"),
|
||||
patch(
|
||||
"pybravia.BraviaClient.get_system_info",
|
||||
return_value=BRAVIA_SYSTEM_INFO,
|
||||
),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}, data={CONF_HOST: "bravia-host"}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "authorize"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_USE_PSK: True}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "psk"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_PIN: "mypsk"}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["result"].unique_id == "very_unique_string"
|
||||
assert result["title"] == "BRAVIA TV-Model"
|
||||
assert result["data"] == {
|
||||
CONF_HOST: "bravia-host",
|
||||
CONF_PIN: "mypsk",
|
||||
CONF_USE_PSK: True,
|
||||
CONF_PIN: "secret",
|
||||
CONF_USE_PSK: use_psk,
|
||||
CONF_USE_SSL: use_ssl,
|
||||
CONF_MAC: "AA:BB:CC:DD:EE:FF",
|
||||
**(
|
||||
{
|
||||
CONF_CLIENT_ID: uuid,
|
||||
CONF_NICKNAME: f"{NICKNAME_PREFIX} {uuid[:6]}",
|
||||
}
|
||||
if not use_psk
|
||||
else {}
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -105,12 +105,12 @@ async def test_climate_triggers_gated_by_labs_flag(
|
||||
# Valid configurations
|
||||
(
|
||||
"climate.hvac_mode_changed",
|
||||
{CONF_HVAC_MODE: [HVACMode.HEAT, HVACMode.COOL]},
|
||||
{CONF_HVAC_MODE: ["heat", "cool"]},
|
||||
does_not_raise(),
|
||||
),
|
||||
(
|
||||
"climate.hvac_mode_changed",
|
||||
{CONF_HVAC_MODE: HVACMode.HEAT},
|
||||
{CONF_HVAC_MODE: "heat"},
|
||||
does_not_raise(),
|
||||
),
|
||||
# Invalid configurations
|
||||
@@ -305,7 +305,7 @@ def parametrize_xxx_crossed_threshold_trigger_states(
|
||||
[
|
||||
*parametrize_climate_trigger_states(
|
||||
trigger="climate.hvac_mode_changed",
|
||||
trigger_options={CONF_HVAC_MODE: [HVACMode.HEAT, HVACMode.COOL]},
|
||||
trigger_options={CONF_HVAC_MODE: ["heat", "cool"]},
|
||||
target_states=[HVACMode.HEAT, HVACMode.COOL],
|
||||
other_states=other_states([HVACMode.HEAT, HVACMode.COOL]),
|
||||
),
|
||||
@@ -465,7 +465,7 @@ async def test_climate_state_attribute_trigger_behavior_any(
|
||||
[
|
||||
*parametrize_climate_trigger_states(
|
||||
trigger="climate.hvac_mode_changed",
|
||||
trigger_options={CONF_HVAC_MODE: [HVACMode.HEAT, HVACMode.COOL]},
|
||||
trigger_options={CONF_HVAC_MODE: ["heat", "cool"]},
|
||||
target_states=[HVACMode.HEAT, HVACMode.COOL],
|
||||
other_states=other_states([HVACMode.HEAT, HVACMode.COOL]),
|
||||
),
|
||||
@@ -615,7 +615,7 @@ async def test_climate_state_attribute_trigger_behavior_first(
|
||||
[
|
||||
*parametrize_climate_trigger_states(
|
||||
trigger="climate.hvac_mode_changed",
|
||||
trigger_options={CONF_HVAC_MODE: [HVACMode.HEAT, HVACMode.COOL]},
|
||||
trigger_options={CONF_HVAC_MODE: ["heat", "cool"]},
|
||||
target_states=[HVACMode.HEAT, HVACMode.COOL],
|
||||
other_states=other_states([HVACMode.HEAT, HVACMode.COOL]),
|
||||
),
|
||||
|
||||
@@ -7,7 +7,7 @@ from unittest.mock import AsyncMock
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.const import STATE_UNAVAILABLE
|
||||
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
@@ -66,3 +66,43 @@ async def test_fail_query(
|
||||
assert len(hass.states.async_entity_ids()) == 6
|
||||
departure_sensor = hass.states.get("sensor.mock_title_departure")
|
||||
assert departure_sensor.state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_no_departures(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test handling when there are no departures available."""
|
||||
await init_integration(hass, mock_config_entry)
|
||||
assert len(hass.states.async_entity_ids()) == 6
|
||||
|
||||
# Simulate no departures (e.g., after-hours)
|
||||
mock_israelrail.query.return_value = []
|
||||
|
||||
await goto_future(hass, freezer)
|
||||
|
||||
# All sensors should still exist
|
||||
assert len(hass.states.async_entity_ids()) == 6
|
||||
|
||||
# Departure sensors should have unknown state (None)
|
||||
departure_sensor = hass.states.get("sensor.mock_title_departure")
|
||||
assert departure_sensor.state == STATE_UNKNOWN
|
||||
|
||||
departure_sensor_1 = hass.states.get("sensor.mock_title_departure_1")
|
||||
assert departure_sensor_1.state == STATE_UNKNOWN
|
||||
|
||||
departure_sensor_2 = hass.states.get("sensor.mock_title_departure_2")
|
||||
assert departure_sensor_2.state == STATE_UNKNOWN
|
||||
|
||||
# Non-departure sensors (platform, trains, train_number) also access index 0
|
||||
# and should have unknown state when no departures available
|
||||
platform_sensor = hass.states.get("sensor.mock_title_platform")
|
||||
assert platform_sensor.state == STATE_UNKNOWN
|
||||
|
||||
trains_sensor = hass.states.get("sensor.mock_title_trains")
|
||||
assert trains_sensor.state == STATE_UNKNOWN
|
||||
|
||||
train_number_sensor = hass.states.get("sensor.mock_title_train_number")
|
||||
assert train_number_sensor.state == STATE_UNKNOWN
|
||||
|
||||
@@ -325,6 +325,38 @@ async def test_camera_image(
|
||||
assert image.content == SMALLEST_VALID_JPEG_BYTES
|
||||
|
||||
|
||||
async def test_camera_live_view_no_subscription(
|
||||
hass: HomeAssistant,
|
||||
mock_ring_client,
|
||||
mock_ring_devices,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test live view camera skips recording URL when no subscription."""
|
||||
await setup_platform(hass, Platform.CAMERA)
|
||||
|
||||
front_camera_mock = mock_ring_devices.get_device(765432)
|
||||
# Set device to not have subscription
|
||||
front_camera_mock.has_subscription = False
|
||||
|
||||
state = hass.states.get("camera.front_live_view")
|
||||
assert state is not None
|
||||
|
||||
# Reset mock call counts
|
||||
front_camera_mock.async_recording_url.reset_mock()
|
||||
|
||||
# Trigger coordinator update
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
# For cameras without subscription, recording URL should NOT be fetched
|
||||
front_camera_mock.async_recording_url.assert_not_called()
|
||||
|
||||
# Requesting an image without subscription should raise an error
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await async_get_image(hass, "camera.front_live_view")
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_camera_stream_attributes(
|
||||
hass: HomeAssistant,
|
||||
|
||||
@@ -148,6 +148,20 @@ class FakeDevice(RoborockDevice):
|
||||
"""Close the device."""
|
||||
|
||||
|
||||
def set_trait_attributes(
|
||||
trait: AsyncMock,
|
||||
dataclass_template: RoborockBase,
|
||||
init_none: bool = False,
|
||||
) -> None:
|
||||
"""Set attributes on a mock roborock trait."""
|
||||
template_copy = deepcopy(dataclass_template)
|
||||
for attr_name in dir(template_copy):
|
||||
if attr_name.startswith("_"):
|
||||
continue
|
||||
attr_value = getattr(template_copy, attr_name) if not init_none else None
|
||||
setattr(trait, attr_name, attr_value)
|
||||
|
||||
|
||||
def make_mock_trait(
|
||||
trait_spec: type[V1TraitMixin] | None = None,
|
||||
dataclass_template: RoborockBase | None = None,
|
||||
@@ -156,12 +170,14 @@ def make_mock_trait(
|
||||
trait = AsyncMock(spec=trait_spec or V1TraitMixin)
|
||||
if dataclass_template is not None:
|
||||
# Copy all attributes and property methods (e.g. computed properties)
|
||||
template_copy = deepcopy(dataclass_template)
|
||||
for attr_name in dir(template_copy):
|
||||
if attr_name.startswith("_"):
|
||||
continue
|
||||
setattr(trait, attr_name, getattr(template_copy, attr_name))
|
||||
trait.refresh = AsyncMock()
|
||||
# on the first call to refresh(). The object starts uninitialized.
|
||||
set_trait_attributes(trait, dataclass_template, init_none=True)
|
||||
|
||||
async def refresh() -> None:
|
||||
if dataclass_template is not None:
|
||||
set_trait_attributes(trait, dataclass_template)
|
||||
|
||||
trait.refresh = AsyncMock(side_effect=refresh)
|
||||
return trait
|
||||
|
||||
|
||||
|
||||
491
tests/components/roborock/snapshots/test_binary_sensor.ambr
Normal file
491
tests/components/roborock/snapshots/test_binary_sensor.ambr
Normal file
@@ -0,0 +1,491 @@
|
||||
# serializer version: 1
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_2_charging-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.roborock_s7_2_charging',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.BATTERY_CHARGING: 'battery_charging'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Charging',
|
||||
'platform': 'roborock',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'battery_charging_device_2',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_2_charging-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'battery_charging',
|
||||
'friendly_name': 'Roborock S7 2 Charging',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.roborock_s7_2_charging',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_2_cleaning-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.roborock_s7_2_cleaning',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.RUNNING: 'running'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Cleaning',
|
||||
'platform': 'roborock',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'in_cleaning',
|
||||
'unique_id': 'in_cleaning_device_2',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_2_cleaning-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'running',
|
||||
'friendly_name': 'Roborock S7 2 Cleaning',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.roborock_s7_2_cleaning',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_2_mop_attached-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.roborock_s7_2_mop_attached',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.CONNECTIVITY: 'connectivity'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Mop attached',
|
||||
'platform': 'roborock',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'mop_attached',
|
||||
'unique_id': 'water_box_carriage_status_device_2',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_2_mop_attached-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'connectivity',
|
||||
'friendly_name': 'Roborock S7 2 Mop attached',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.roborock_s7_2_mop_attached',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_2_water_box_attached-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.roborock_s7_2_water_box_attached',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.CONNECTIVITY: 'connectivity'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Water box attached',
|
||||
'platform': 'roborock',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'water_box_attached',
|
||||
'unique_id': 'water_box_status_device_2',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_2_water_box_attached-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'connectivity',
|
||||
'friendly_name': 'Roborock S7 2 Water box attached',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.roborock_s7_2_water_box_attached',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_2_water_shortage-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.roborock_s7_2_water_shortage',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.PROBLEM: 'problem'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Water shortage',
|
||||
'platform': 'roborock',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'water_shortage',
|
||||
'unique_id': 'water_shortage_device_2',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_2_water_shortage-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'problem',
|
||||
'friendly_name': 'Roborock S7 2 Water shortage',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.roborock_s7_2_water_shortage',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_maxv_charging-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.roborock_s7_maxv_charging',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.BATTERY_CHARGING: 'battery_charging'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Charging',
|
||||
'platform': 'roborock',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'battery_charging_abc123',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_maxv_charging-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'battery_charging',
|
||||
'friendly_name': 'Roborock S7 MaxV Charging',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.roborock_s7_maxv_charging',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_maxv_cleaning-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.roborock_s7_maxv_cleaning',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.RUNNING: 'running'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Cleaning',
|
||||
'platform': 'roborock',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'in_cleaning',
|
||||
'unique_id': 'in_cleaning_abc123',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_maxv_cleaning-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'running',
|
||||
'friendly_name': 'Roborock S7 MaxV Cleaning',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.roborock_s7_maxv_cleaning',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_maxv_mop_attached-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.roborock_s7_maxv_mop_attached',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.CONNECTIVITY: 'connectivity'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Mop attached',
|
||||
'platform': 'roborock',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'mop_attached',
|
||||
'unique_id': 'water_box_carriage_status_abc123',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_maxv_mop_attached-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'connectivity',
|
||||
'friendly_name': 'Roborock S7 MaxV Mop attached',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.roborock_s7_maxv_mop_attached',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_maxv_water_box_attached-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.roborock_s7_maxv_water_box_attached',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.CONNECTIVITY: 'connectivity'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Water box attached',
|
||||
'platform': 'roborock',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'water_box_attached',
|
||||
'unique_id': 'water_box_status_abc123',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_maxv_water_box_attached-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'connectivity',
|
||||
'friendly_name': 'Roborock S7 MaxV Water box attached',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.roborock_s7_maxv_water_box_attached',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_maxv_water_shortage-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.roborock_s7_maxv_water_shortage',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.PROBLEM: 'problem'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Water shortage',
|
||||
'platform': 'roborock',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'water_shortage',
|
||||
'unique_id': 'water_shortage_abc123',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_binary_sensors[binary_sensor.roborock_s7_maxv_water_shortage-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'problem',
|
||||
'friendly_name': 'Roborock S7 MaxV Water shortage',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.roborock_s7_maxv_water_shortage',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,13 @@
|
||||
"""Test Roborock Binary Sensor."""
|
||||
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -15,17 +17,10 @@ def platforms() -> list[Platform]:
|
||||
|
||||
|
||||
async def test_binary_sensors(
|
||||
hass: HomeAssistant, setup_entry: MockConfigEntry
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
setup_entry: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test binary sensors and check test values are correctly set."""
|
||||
assert len(hass.states.async_all("binary_sensor")) == 10
|
||||
assert hass.states.get("binary_sensor.roborock_s7_maxv_mop_attached").state == "on"
|
||||
assert (
|
||||
hass.states.get("binary_sensor.roborock_s7_maxv_water_box_attached").state
|
||||
== "on"
|
||||
)
|
||||
assert (
|
||||
hass.states.get("binary_sensor.roborock_s7_maxv_water_shortage").state == "off"
|
||||
)
|
||||
assert hass.states.get("binary_sensor.roborock_s7_maxv_cleaning").state == "off"
|
||||
assert hass.states.get("binary_sensor.roborock_s7_maxv_charging").state == "on"
|
||||
await snapshot_platform(hass, entity_registry, snapshot, setup_entry.entry_id)
|
||||
|
||||
@@ -5,8 +5,9 @@ from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -17,8 +18,9 @@ def platforms() -> list[Platform]:
|
||||
|
||||
async def test_sensors(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
setup_entry: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test sensors and check test values are correctly set."""
|
||||
assert snapshot == hass.states.async_all("sensor")
|
||||
await snapshot_platform(hass, entity_registry, snapshot, setup_entry.entry_id)
|
||||
|
||||
@@ -31,7 +31,8 @@ from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .conftest import FakeDevice
|
||||
from .conftest import FakeDevice, set_trait_attributes
|
||||
from .mock_data import STATUS
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
@@ -132,8 +133,14 @@ async def test_resume_cleaning(
|
||||
vacuum_command: Mock,
|
||||
) -> None:
|
||||
"""Test resuming clean on start button when a clean is paused."""
|
||||
fake_vacuum.v1_properties.status.in_cleaning = in_cleaning_int
|
||||
fake_vacuum.v1_properties.status.in_returning = in_returning_int
|
||||
|
||||
async def refresh_properties() -> None:
|
||||
set_trait_attributes(fake_vacuum.v1_properties.status, STATUS)
|
||||
fake_vacuum.v1_properties.status.in_cleaning = in_cleaning_int
|
||||
fake_vacuum.v1_properties.status.in_returning = in_returning_int
|
||||
|
||||
fake_vacuum.v1_properties.status.refresh.side_effect = refresh_properties
|
||||
|
||||
await async_setup_component(hass, DOMAIN, {})
|
||||
vacuum = hass.states.get(ENTITY_ID)
|
||||
assert vacuum
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
"""The tests for the telegram.notify platform."""
|
||||
|
||||
from unittest.mock import patch
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, call, patch
|
||||
|
||||
from homeassistant import config as hass_config
|
||||
from homeassistant.components import notify
|
||||
from homeassistant.components.notify import ATTR_DATA, ATTR_MESSAGE, ATTR_TITLE
|
||||
from homeassistant.components.telegram import DOMAIN
|
||||
from homeassistant.const import SERVICE_RELOAD
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import HomeAssistant, ServiceRegistry
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
@@ -54,3 +56,108 @@ async def test_reload_notify(
|
||||
issue_id="migrate_notify",
|
||||
)
|
||||
assert len(issue_registry.issues) == 1
|
||||
|
||||
|
||||
async def test_notify(hass: HomeAssistant) -> None:
|
||||
"""Test notify."""
|
||||
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
notify.DOMAIN,
|
||||
{
|
||||
notify.DOMAIN: [
|
||||
{
|
||||
"name": DOMAIN,
|
||||
"platform": DOMAIN,
|
||||
"chat_id": 1,
|
||||
},
|
||||
]
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
original_call = ServiceRegistry.async_call
|
||||
with patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call", new_callable=AsyncMock
|
||||
) as mock_service_call:
|
||||
# setup mock
|
||||
|
||||
async def call_service(*args, **kwargs) -> Any:
|
||||
if args[0] == notify.DOMAIN:
|
||||
return await original_call(
|
||||
hass.services, args[0], args[1], args[2], kwargs["blocking"]
|
||||
)
|
||||
return AsyncMock()
|
||||
|
||||
mock_service_call.side_effect = call_service
|
||||
|
||||
# test send message
|
||||
|
||||
data: dict[str, Any] = {"title": "mock title", "message": "mock message"}
|
||||
await hass.services.async_call(
|
||||
notify.DOMAIN,
|
||||
DOMAIN,
|
||||
{ATTR_TITLE: "mock title", ATTR_MESSAGE: "mock message"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_service_call.mock_calls == [
|
||||
call(
|
||||
"notify",
|
||||
"telegram",
|
||||
data,
|
||||
blocking=True,
|
||||
),
|
||||
call(
|
||||
"telegram_bot",
|
||||
"send_message",
|
||||
{"target": 1, "title": "mock title", "message": "mock message"},
|
||||
False,
|
||||
None,
|
||||
None,
|
||||
False,
|
||||
),
|
||||
]
|
||||
|
||||
mock_service_call.reset_mock()
|
||||
|
||||
# test send file
|
||||
|
||||
data = {
|
||||
ATTR_TITLE: "mock title",
|
||||
ATTR_MESSAGE: "mock message",
|
||||
ATTR_DATA: {
|
||||
"photo": {"url": "https://mock/photo.jpg", "caption": "mock caption"}
|
||||
},
|
||||
}
|
||||
|
||||
await hass.services.async_call(
|
||||
notify.DOMAIN,
|
||||
DOMAIN,
|
||||
data,
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_service_call.mock_calls == [
|
||||
call(
|
||||
"notify",
|
||||
"telegram",
|
||||
data,
|
||||
blocking=True,
|
||||
),
|
||||
call(
|
||||
"telegram_bot",
|
||||
"send_photo",
|
||||
{
|
||||
"target": 1,
|
||||
"url": "https://mock/photo.jpg",
|
||||
"caption": "mock caption",
|
||||
},
|
||||
False,
|
||||
None,
|
||||
None,
|
||||
False,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -101,3 +101,16 @@ def mock_config_entry() -> MockConfigEntry:
|
||||
entry_id="01J0BC4QM2YBRP6H5G933CETI8",
|
||||
unique_id=TEST_USER_ID,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="skip_cloud", autouse=True)
|
||||
def skip_cloud_fixture():
|
||||
"""Skip setting up cloud.
|
||||
|
||||
Cloud already has its own tests for account link.
|
||||
|
||||
We do not need to test it here as we only need to test our
|
||||
usage of the oauth2 helpers.
|
||||
"""
|
||||
with patch("homeassistant.components.cloud.async_setup", return_value=True):
|
||||
yield
|
||||
|
||||
Reference in New Issue
Block a user