mirror of
https://github.com/home-assistant/core.git
synced 2026-05-05 12:24:48 +02:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ea4bdbb3a0 | |||
| 40cfc31dcb | |||
| 031aadff00 | |||
| 27691b7d48 | |||
| fe94107af7 | |||
| d784a76d32 | |||
| ebb1912617 | |||
| 8c605c29c3 | |||
| 74a75e709f | |||
| 2103875ff7 | |||
| 5c83b774bb | |||
| 2c870f9da9 | |||
| 40adb3809f | |||
| 8aa1242221 | |||
| 8569ddc5f9 | |||
| 7032415528 | |||
| d099fb2a26 | |||
| 35fad52913 | |||
| c170132827 | |||
| 439f82a4ec | |||
| 2481d14632 | |||
| 3cf826dc93 | |||
| e25ddf9650 | |||
| 8d79ac67f5 | |||
| 5025c15165 | |||
| ffd5e04a29 | |||
| 9fcdfd1b16 | |||
| c1e5b2e6cc | |||
| 31c0d21204 | |||
| 3ba63fc78f | |||
| 0395315267 | |||
| 6b354457c2 |
@@ -786,8 +786,6 @@ build.json @home-assistant/supervisor
|
||||
/homeassistant/components/media_source/ @hunterjm
|
||||
/tests/components/media_source/ @hunterjm
|
||||
/homeassistant/components/mediaroom/ @dgomes
|
||||
/homeassistant/components/melcloud/ @vilppuvuorinen
|
||||
/tests/components/melcloud/ @vilppuvuorinen
|
||||
/homeassistant/components/melissa/ @kennedyshead
|
||||
/tests/components/melissa/ @kennedyshead
|
||||
/homeassistant/components/melnor/ @vanstinator
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"domain": "tplink",
|
||||
"name": "TP-Link",
|
||||
"integrations": ["tplink", "tplink_omada", "tplink_lte"],
|
||||
"integrations": ["tplink", "tplink_omada", "tplink_lte", "tplink_tapo"],
|
||||
"iot_standards": ["matter"]
|
||||
}
|
||||
|
||||
@@ -53,10 +53,25 @@ class HomeassistantAnalyticsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
) -> FlowResult:
|
||||
"""Handle the initial step."""
|
||||
self._async_abort_entries_match()
|
||||
if user_input:
|
||||
return self.async_create_entry(
|
||||
title="Home Assistant Analytics Insights", data={}, options=user_input
|
||||
)
|
||||
errors: dict[str, str] = {}
|
||||
if user_input is not None:
|
||||
if not user_input.get(CONF_TRACKED_INTEGRATIONS) and not user_input.get(
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS
|
||||
):
|
||||
errors["base"] = "no_integrations_selected"
|
||||
else:
|
||||
return self.async_create_entry(
|
||||
title="Home Assistant Analytics Insights",
|
||||
data={},
|
||||
options={
|
||||
CONF_TRACKED_INTEGRATIONS: user_input.get(
|
||||
CONF_TRACKED_INTEGRATIONS, []
|
||||
),
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: user_input.get(
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS, []
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
client = HomeassistantAnalyticsClient(
|
||||
session=async_get_clientsession(self.hass)
|
||||
@@ -78,16 +93,17 @@ class HomeassistantAnalyticsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
]
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
errors=errors,
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_TRACKED_INTEGRATIONS): SelectSelector(
|
||||
vol.Optional(CONF_TRACKED_INTEGRATIONS): SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=options,
|
||||
multiple=True,
|
||||
sort=True,
|
||||
)
|
||||
),
|
||||
vol.Required(CONF_TRACKED_CUSTOM_INTEGRATIONS): SelectSelector(
|
||||
vol.Optional(CONF_TRACKED_CUSTOM_INTEGRATIONS): SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=list(custom_integrations),
|
||||
multiple=True,
|
||||
@@ -106,8 +122,24 @@ class HomeassistantAnalyticsOptionsFlowHandler(OptionsFlowWithConfigEntry):
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Manage the options."""
|
||||
if user_input:
|
||||
return self.async_create_entry(title="", data=user_input)
|
||||
errors: dict[str, str] = {}
|
||||
if user_input is not None:
|
||||
if not user_input.get(CONF_TRACKED_INTEGRATIONS) and not user_input.get(
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS
|
||||
):
|
||||
errors["base"] = "no_integrations_selected"
|
||||
else:
|
||||
return self.async_create_entry(
|
||||
title="",
|
||||
data={
|
||||
CONF_TRACKED_INTEGRATIONS: user_input.get(
|
||||
CONF_TRACKED_INTEGRATIONS, []
|
||||
),
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: user_input.get(
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS, []
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
client = HomeassistantAnalyticsClient(
|
||||
session=async_get_clientsession(self.hass)
|
||||
@@ -129,17 +161,18 @@ class HomeassistantAnalyticsOptionsFlowHandler(OptionsFlowWithConfigEntry):
|
||||
]
|
||||
return self.async_show_form(
|
||||
step_id="init",
|
||||
errors=errors,
|
||||
data_schema=self.add_suggested_values_to_schema(
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_TRACKED_INTEGRATIONS): SelectSelector(
|
||||
vol.Optional(CONF_TRACKED_INTEGRATIONS): SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=options,
|
||||
multiple=True,
|
||||
sort=True,
|
||||
)
|
||||
),
|
||||
vol.Required(CONF_TRACKED_CUSTOM_INTEGRATIONS): SelectSelector(
|
||||
vol.Optional(CONF_TRACKED_CUSTOM_INTEGRATIONS): SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=list(custom_integrations),
|
||||
multiple=True,
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
"abort": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
||||
},
|
||||
"error": {
|
||||
"no_integration_selected": "You must select at least one integration to track"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
@@ -32,6 +35,9 @@
|
||||
},
|
||||
"abort": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"error": {
|
||||
"no_integration_selected": "[%key:component::analytics_insights::config::error::no_integration_selected%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aioelectricitymaps"],
|
||||
"requirements": ["aioelectricitymaps==0.3.0"]
|
||||
"requirements": ["aioelectricitymaps==0.3.1"]
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ class EcowittEntity(Entity):
|
||||
"""Update the state on callback."""
|
||||
self.async_write_ha_state()
|
||||
|
||||
self.ecowitt.update_cb.append(_update_state) # type: ignore[arg-type] # upstream bug
|
||||
self.async_on_remove(lambda: self.ecowitt.update_cb.remove(_update_state)) # type: ignore[arg-type] # upstream bug
|
||||
self.ecowitt.update_cb.append(_update_state)
|
||||
self.async_on_remove(lambda: self.ecowitt.update_cb.remove(_update_state))
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"dependencies": ["webhook"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/ecowitt",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["aioecowitt==2023.5.0"]
|
||||
"requirements": ["aioecowitt==2024.2.0"]
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aioesphomeapi", "noiseprotocol", "bleak_esphome"],
|
||||
"requirements": [
|
||||
"aioesphomeapi==21.0.1",
|
||||
"aioesphomeapi==21.0.2",
|
||||
"esphome-dashboard-api==1.2.3",
|
||||
"bleak-esphome==0.4.1"
|
||||
],
|
||||
|
||||
@@ -7,6 +7,7 @@ from typing import Any
|
||||
|
||||
from aioflo.api import API
|
||||
from aioflo.errors import RequestError
|
||||
from orjson import JSONDecodeError
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
@@ -46,7 +47,7 @@ class FloDeviceDataUpdateCoordinator(DataUpdateCoordinator): # pylint: disable=
|
||||
await self._update_device()
|
||||
await self._update_consumption_data()
|
||||
self._failure_count = 0
|
||||
except (RequestError, TimeoutError) as error:
|
||||
except (RequestError, TimeoutError, JSONDecodeError) as error:
|
||||
self._failure_count += 1
|
||||
if self._failure_count > 3:
|
||||
raise UpdateFailed(error) from error
|
||||
|
||||
@@ -1001,12 +1001,18 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator): # pylint: disable=has
|
||||
raise_on_entry_error: bool = False,
|
||||
) -> None:
|
||||
"""Refresh data."""
|
||||
if not scheduled:
|
||||
if not scheduled and not raise_on_auth_failed:
|
||||
# Force refreshing updates for non-scheduled updates
|
||||
# If `raise_on_auth_failed` is set, it means this is
|
||||
# the first refresh and we do not want to delay
|
||||
# startup or cause a timeout so we only refresh the
|
||||
# updates if this is not a scheduled refresh and
|
||||
# we are not doing the first refresh.
|
||||
try:
|
||||
await self.hassio.refresh_updates()
|
||||
except HassioAPIError as err:
|
||||
_LOGGER.warning("Error on Supervisor API: %s", err)
|
||||
|
||||
await super()._async_refresh(
|
||||
log_failures, raise_on_auth_failed, scheduled, raise_on_entry_error
|
||||
)
|
||||
|
||||
@@ -459,7 +459,7 @@ class HassIO:
|
||||
|
||||
This method returns a coroutine.
|
||||
"""
|
||||
return self.send_command("/refresh_updates", timeout=None)
|
||||
return self.send_command("/refresh_updates", timeout=300)
|
||||
|
||||
@api_data
|
||||
def retrieve_discovery_messages(self) -> Coroutine:
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/holiday",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["holidays==0.41", "babel==2.13.1"]
|
||||
"requirements": ["holidays==0.42", "babel==2.13.1"]
|
||||
}
|
||||
|
||||
@@ -138,6 +138,8 @@ class MicroBotConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
await self._client.connect(init=True)
|
||||
return self.async_show_form(step_id="link")
|
||||
|
||||
if not await self._client.is_connected():
|
||||
await self._client.connect(init=False)
|
||||
if not await self._client.is_connected():
|
||||
errors["base"] = "linking"
|
||||
else:
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
"config_flow": true,
|
||||
"dependencies": ["bluetooth_adapters"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/keymitt_ble",
|
||||
"integration_type": "hub",
|
||||
"iot_class": "assumed_state",
|
||||
"loggers": ["keymitt_ble"],
|
||||
"requirements": ["PyMicroBot==0.0.9"]
|
||||
"requirements": ["PyMicroBot==0.0.10"]
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
"loggers": ["xknx", "xknxproject"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": [
|
||||
"xknx==2.11.2",
|
||||
"xknxproject==3.4.0",
|
||||
"xknx==2.12.0",
|
||||
"xknxproject==3.5.0",
|
||||
"knx-frontend==2024.1.20.105944"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Support for Lupusec Home Security system."""
|
||||
from json import JSONDecodeError
|
||||
import logging
|
||||
|
||||
import lupupy
|
||||
@@ -111,16 +112,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
lupusec_system = await hass.async_add_executor_job(
|
||||
lupupy.Lupusec, username, password, host
|
||||
)
|
||||
|
||||
except LupusecException:
|
||||
_LOGGER.error("Failed to connect to Lupusec device at %s", host)
|
||||
return False
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
_LOGGER.error(
|
||||
"Unknown error while trying to connect to Lupusec device at %s: %s",
|
||||
host,
|
||||
ex,
|
||||
)
|
||||
except JSONDecodeError:
|
||||
_LOGGER.error("Failed to connect to Lupusec device at %s", host)
|
||||
return False
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = lupusec_system
|
||||
|
||||
@@ -29,14 +29,14 @@ SCAN_INTERVAL = timedelta(seconds=2)
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_devices: AddEntitiesCallback,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up an alarm control panel for a Lupusec device."""
|
||||
data = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
alarm_devices = [LupusecAlarm(data, data.get_alarm(), config_entry.entry_id)]
|
||||
alarm = await hass.async_add_executor_job(data.get_alarm)
|
||||
|
||||
async_add_devices(alarm_devices)
|
||||
async_add_entities([LupusecAlarm(data, alarm, config_entry.entry_id)])
|
||||
|
||||
|
||||
class LupusecAlarm(LupusecDevice, AlarmControlPanelEntity):
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from functools import partial
|
||||
import logging
|
||||
|
||||
import lupupy.constants as CONST
|
||||
@@ -25,7 +26,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_devices: AddEntitiesCallback,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up a binary sensors for a Lupusec device."""
|
||||
|
||||
@@ -34,10 +35,12 @@ async def async_setup_entry(
|
||||
device_types = CONST.TYPE_OPENING + CONST.TYPE_SENSOR
|
||||
|
||||
sensors = []
|
||||
for device in data.get_devices(generic_type=device_types):
|
||||
partial_func = partial(data.get_devices, generic_type=device_types)
|
||||
devices = await hass.async_add_executor_job(partial_func)
|
||||
for device in devices:
|
||||
sensors.append(LupusecBinarySensor(device, config_entry.entry_id))
|
||||
|
||||
async_add_devices(sensors)
|
||||
async_add_entities(sensors)
|
||||
|
||||
|
||||
class LupusecBinarySensor(LupusecBaseSensor, BinarySensorEntity):
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
""""Config flow for Lupusec integration."""
|
||||
|
||||
from json import JSONDecodeError
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
@@ -50,6 +51,8 @@ class LupusecConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
await test_host_connection(self.hass, host, username, password)
|
||||
except CannotConnect:
|
||||
errors["base"] = "cannot_connect"
|
||||
except JSONDecodeError:
|
||||
errors["base"] = "cannot_connect"
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
errors["base"] = "unknown"
|
||||
@@ -80,6 +83,8 @@ class LupusecConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
await test_host_connection(self.hass, host, username, password)
|
||||
except CannotConnect:
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
except JSONDecodeError:
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
return self.async_abort(reason="unknown")
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"issues": {
|
||||
"deprecated_yaml_import_issue_cannot_connect": {
|
||||
"title": "The Lupus Electronics LUPUSEC YAML configuration import failed",
|
||||
"description": "Configuring Lupus Electronics LUPUSEC using YAML is being removed but there was an connection error importing your YAML configuration.\n\nEnsure connection to Lupus Electronics LUPUSEC works and restart Home Assistant to try again or remove the Lupus Electronics LUPUSEC YAML configuration from your configuration.yaml file and continue to [set up the integration]({url}) manually."
|
||||
"description": "Configuring Lupus Electronics LUPUSEC using YAML is being removed but there was a connection error importing your YAML configuration.\n\nEnsure connection to Lupus Electronics LUPUSEC works and restart Home Assistant to try again or remove the Lupus Electronics LUPUSEC YAML configuration from your configuration.yaml file and continue to [set up the integration]({url}) manually."
|
||||
},
|
||||
"deprecated_yaml_import_issue_unknown": {
|
||||
"title": "The Lupus Electronics LUPUSEC YAML configuration import failed",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from functools import partial
|
||||
from typing import Any
|
||||
|
||||
import lupupy.constants as CONST
|
||||
@@ -20,7 +21,7 @@ SCAN_INTERVAL = timedelta(seconds=2)
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_devices: AddEntitiesCallback,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Lupusec switch devices."""
|
||||
|
||||
@@ -29,10 +30,12 @@ async def async_setup_entry(
|
||||
device_types = CONST.TYPE_SWITCH
|
||||
|
||||
switches = []
|
||||
for device in data.get_devices(generic_type=device_types):
|
||||
partial_func = partial(data.get_devices, generic_type=device_types)
|
||||
devices = await hass.async_add_executor_job(partial_func)
|
||||
for device in devices:
|
||||
switches.append(LupusecSwitch(device, config_entry.entry_id))
|
||||
|
||||
async_add_devices(switches)
|
||||
async_add_entities(switches)
|
||||
|
||||
|
||||
class LupusecSwitch(LupusecBaseSensor, SwitchEntity):
|
||||
|
||||
@@ -22,7 +22,7 @@ from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5)
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15)
|
||||
|
||||
PLATFORMS = [Platform.CLIMATE, Platform.SENSOR, Platform.WATER_HEATER]
|
||||
|
||||
@@ -123,11 +123,6 @@ class MelCloudDevice:
|
||||
via_device=(DOMAIN, f"{dev.mac}-{dev.serial}"),
|
||||
)
|
||||
|
||||
@property
|
||||
def daily_energy_consumed(self) -> float | None:
|
||||
"""Return energy consumed during the current day in kWh."""
|
||||
return self.device.daily_energy_consumed
|
||||
|
||||
|
||||
async def mel_devices_setup(
|
||||
hass: HomeAssistant, token: str
|
||||
@@ -138,8 +133,7 @@ async def mel_devices_setup(
|
||||
all_devices = await get_devices(
|
||||
token,
|
||||
session,
|
||||
user_update_interval=timedelta(minutes=30),
|
||||
conf_update_interval=timedelta(minutes=15),
|
||||
conf_update_interval=timedelta(minutes=30),
|
||||
device_set_debounce=timedelta(seconds=2),
|
||||
)
|
||||
wrapped_devices: dict[str, list[MelCloudDevice]] = {}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"domain": "melcloud",
|
||||
"name": "MELCloud",
|
||||
"codeowners": ["@vilppuvuorinen"],
|
||||
"codeowners": [],
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/melcloud",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pymelcloud"],
|
||||
"requirements": ["pymelcloud==2.5.8"]
|
||||
"requirements": ["pymelcloud==2.5.9"]
|
||||
}
|
||||
|
||||
@@ -58,16 +58,6 @@ ATA_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = (
|
||||
value_fn=lambda x: x.device.total_energy_consumed,
|
||||
enabled=lambda x: x.device.has_energy_consumed_meter,
|
||||
),
|
||||
MelcloudSensorEntityDescription(
|
||||
key="daily_energy",
|
||||
translation_key="daily_energy",
|
||||
icon="mdi:factory",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
value_fn=lambda x: x.device.daily_energy_consumed,
|
||||
enabled=lambda x: True,
|
||||
),
|
||||
)
|
||||
ATW_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = (
|
||||
MelcloudSensorEntityDescription(
|
||||
@@ -90,16 +80,6 @@ ATW_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = (
|
||||
value_fn=lambda x: x.device.tank_temperature,
|
||||
enabled=lambda x: True,
|
||||
),
|
||||
MelcloudSensorEntityDescription(
|
||||
key="daily_energy",
|
||||
translation_key="daily_energy",
|
||||
icon="mdi:factory",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
value_fn=lambda x: x.device.daily_energy_consumed,
|
||||
enabled=lambda x: True,
|
||||
),
|
||||
)
|
||||
ATW_ZONE_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = (
|
||||
MelcloudSensorEntityDescription(
|
||||
|
||||
@@ -65,9 +65,6 @@
|
||||
"room_temperature": {
|
||||
"name": "Room temperature"
|
||||
},
|
||||
"daily_energy": {
|
||||
"name": "Daily energy consumed"
|
||||
},
|
||||
"outside_temperature": {
|
||||
"name": "Outside temperature"
|
||||
},
|
||||
|
||||
@@ -51,6 +51,7 @@ POSITION_DEVICE_MAP = {
|
||||
BlindType.CurtainLeft: CoverDeviceClass.CURTAIN,
|
||||
BlindType.CurtainRight: CoverDeviceClass.CURTAIN,
|
||||
BlindType.SkylightBlind: CoverDeviceClass.SHADE,
|
||||
BlindType.InsectScreen: CoverDeviceClass.SHADE,
|
||||
}
|
||||
|
||||
TILT_DEVICE_MAP = {
|
||||
@@ -69,6 +70,7 @@ TILT_ONLY_DEVICE_MAP = {
|
||||
|
||||
TDBU_DEVICE_MAP = {
|
||||
BlindType.TopDownBottomUp: CoverDeviceClass.SHADE,
|
||||
BlindType.TriangleBlind: CoverDeviceClass.BLIND,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,5 +21,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/motion_blinds",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["motionblinds"],
|
||||
"requirements": ["motionblinds==0.6.19"]
|
||||
"requirements": ["motionblinds==0.6.20"]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Support for Motion Blinds sensors."""
|
||||
from motionblinds import DEVICE_TYPES_WIFI, BlindType
|
||||
from motionblinds import DEVICE_TYPES_WIFI
|
||||
from motionblinds.motion_blinds import DEVICE_TYPE_TDBU
|
||||
|
||||
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@@ -29,7 +30,7 @@ async def async_setup_entry(
|
||||
|
||||
for blind in motion_gateway.device_list.values():
|
||||
entities.append(MotionSignalStrengthSensor(coordinator, blind))
|
||||
if blind.type == BlindType.TopDownBottomUp:
|
||||
if blind.device_type == DEVICE_TYPE_TDBU:
|
||||
entities.append(MotionTDBUBatterySensor(coordinator, blind, "Bottom"))
|
||||
entities.append(MotionTDBUBatterySensor(coordinator, blind, "Top"))
|
||||
elif blind.battery_voltage is not None and blind.battery_voltage > 0:
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/otbr",
|
||||
"integration_type": "service",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["python-otbr-api==2.5.0"]
|
||||
"requirements": ["python-otbr-api==2.6.0"]
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ class DiskSpaceDataUpdateCoordinator(RadarrDataUpdateCoordinator[list[RootFolder
|
||||
"""Fetch the data."""
|
||||
root_folders = await self.api_client.async_get_root_folders()
|
||||
if isinstance(root_folders, RootFolder):
|
||||
root_folders = [root_folders]
|
||||
return [root_folders]
|
||||
return root_folders
|
||||
|
||||
|
||||
@@ -105,7 +105,10 @@ class HealthDataUpdateCoordinator(RadarrDataUpdateCoordinator[list[Health]]):
|
||||
|
||||
async def _fetch_data(self) -> list[Health]:
|
||||
"""Fetch the health data."""
|
||||
return await self.api_client.async_get_failed_health_checks()
|
||||
health = await self.api_client.async_get_failed_health_checks()
|
||||
if isinstance(health, Health):
|
||||
return [health]
|
||||
return health
|
||||
|
||||
|
||||
class MoviesDataUpdateCoordinator(RadarrDataUpdateCoordinator[int]):
|
||||
|
||||
@@ -13,5 +13,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/ring",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["ring_doorbell"],
|
||||
"requirements": ["ring-doorbell[listen]==0.8.5"]
|
||||
"requirements": ["ring-doorbell[listen]==0.8.7"]
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ WALL_CONNECTOR_SENSORS = [
|
||||
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
value_fn=lambda data: data[WALLCONNECTOR_DATA_VITALS].session_energy_wh,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
WallConnectorSensorDescription(
|
||||
key="energy_kWh",
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/thread",
|
||||
"integration_type": "service",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["python-otbr-api==2.5.0", "pyroute2==0.7.5"],
|
||||
"requirements": ["python-otbr-api==2.6.0", "pyroute2==0.7.5"],
|
||||
"zeroconf": ["_meshcop._udp.local."]
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"""Virtual integration: TP-Link Tapo."""
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"domain": "tplink_tapo",
|
||||
"name": "Tapo",
|
||||
"integration_type": "virtual",
|
||||
"supported_by": "tplink"
|
||||
}
|
||||
@@ -129,6 +129,13 @@ async def _generate_trackables(
|
||||
if not trackable["device_id"]:
|
||||
return None
|
||||
|
||||
if "details" not in trackable:
|
||||
_LOGGER.info(
|
||||
"Tracker %s has no details and will be skipped. This happens for shared trackers",
|
||||
trackable["device_id"],
|
||||
)
|
||||
return None
|
||||
|
||||
tracker = client.tracker(trackable["device_id"])
|
||||
|
||||
tracker_details, hw_info, pos_report = await asyncio.gather(
|
||||
|
||||
@@ -430,10 +430,10 @@ class UnifiSensorEntity(UnifiEntity[HandlerT, ApiItemT], SensorEntity):
|
||||
def _make_disconnected(self, *_: core_Event) -> None:
|
||||
"""No heart beat by device.
|
||||
|
||||
Reset sensor value to 0 when client device is disconnected
|
||||
Set sensor as unavailable when client device is disconnected
|
||||
"""
|
||||
if self._attr_native_value != 0:
|
||||
self._attr_native_value = 0
|
||||
if self._attr_available:
|
||||
self._attr_available = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
@callback
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["holidays"],
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["holidays==0.41"]
|
||||
"requirements": ["holidays==0.42"]
|
||||
}
|
||||
|
||||
@@ -21,16 +21,16 @@
|
||||
"universal_silabs_flasher"
|
||||
],
|
||||
"requirements": [
|
||||
"bellows==0.37.6",
|
||||
"bellows==0.38.0",
|
||||
"pyserial==3.5",
|
||||
"pyserial-asyncio==0.6",
|
||||
"zha-quirks==0.0.110",
|
||||
"zigpy-deconz==0.22.4",
|
||||
"zigpy==0.61.0",
|
||||
"zha-quirks==0.0.111",
|
||||
"zigpy-deconz==0.23.0",
|
||||
"zigpy==0.62.3",
|
||||
"zigpy-xbee==0.20.1",
|
||||
"zigpy-zigate==0.12.0",
|
||||
"zigpy-znp==0.12.1",
|
||||
"universal-silabs-flasher==0.0.15",
|
||||
"universal-silabs-flasher==0.0.18",
|
||||
"pyserial-asyncio-fast==0.11"
|
||||
],
|
||||
"usb": [
|
||||
|
||||
@@ -838,6 +838,26 @@ class SmartEnergySummationReceived(PolledSmartEnergySummation):
|
||||
_unique_id_suffix = "summation_received"
|
||||
_attr_translation_key: str = "summation_received"
|
||||
|
||||
@classmethod
|
||||
def create_entity(
|
||||
cls,
|
||||
unique_id: str,
|
||||
zha_device: ZHADevice,
|
||||
cluster_handlers: list[ClusterHandler],
|
||||
**kwargs: Any,
|
||||
) -> Self | None:
|
||||
"""Entity Factory.
|
||||
|
||||
This attribute only started to be initialized in HA 2024.2.0,
|
||||
so the entity would still be created on the first HA start after the upgrade for existing devices,
|
||||
as the initialization to see if an attribute is unsupported happens later in the background.
|
||||
To avoid creating a lot of unnecessary entities for existing devices,
|
||||
wait until the attribute was properly initialized once for now.
|
||||
"""
|
||||
if cluster_handlers[0].cluster.get(cls._attribute_name) is None:
|
||||
return None
|
||||
return super().create_entity(unique_id, zha_device, cluster_handlers, **kwargs)
|
||||
|
||||
|
||||
@MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_PRESSURE)
|
||||
# pylint: disable-next=hass-invalid-inheritance # needs fixing
|
||||
|
||||
@@ -16,7 +16,7 @@ from .helpers.deprecation import (
|
||||
APPLICATION_NAME: Final = "HomeAssistant"
|
||||
MAJOR_VERSION: Final = 2024
|
||||
MINOR_VERSION: Final = 2
|
||||
PATCH_VERSION: Final = "0b8"
|
||||
PATCH_VERSION: Final = "0b10"
|
||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 11, 0)
|
||||
|
||||
@@ -6156,6 +6156,12 @@
|
||||
"config_flow": false,
|
||||
"iot_class": "local_polling",
|
||||
"name": "TP-Link LTE"
|
||||
},
|
||||
"tplink_tapo": {
|
||||
"integration_type": "virtual",
|
||||
"config_flow": false,
|
||||
"supported_by": "tplink",
|
||||
"name": "Tapo"
|
||||
}
|
||||
},
|
||||
"iot_standards": [
|
||||
|
||||
@@ -209,7 +209,10 @@ class LocalOAuth2Implementation(AbstractOAuth2Implementation):
|
||||
error_code = error_response.get("error", "unknown")
|
||||
error_description = error_response.get("error_description", "unknown error")
|
||||
_LOGGER.error(
|
||||
"Token request failed (%s): %s", error_code, error_description
|
||||
"Token request for %s failed (%s): %s",
|
||||
self.domain,
|
||||
error_code,
|
||||
error_description,
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return cast(dict, await resp.json())
|
||||
|
||||
@@ -57,6 +57,7 @@ SLOW_ADD_MIN_TIMEOUT = 500
|
||||
PLATFORM_NOT_READY_RETRIES = 10
|
||||
DATA_ENTITY_PLATFORM = "entity_platform"
|
||||
DATA_DOMAIN_ENTITIES = "domain_entities"
|
||||
DATA_DOMAIN_PLATFORM_ENTITIES = "domain_platform_entities"
|
||||
PLATFORM_NOT_READY_BASE_WAIT_TIME = 30 # seconds
|
||||
|
||||
_LOGGER = getLogger(__name__)
|
||||
@@ -124,6 +125,8 @@ class EntityPlatform:
|
||||
self.scan_interval = scan_interval
|
||||
self.entity_namespace = entity_namespace
|
||||
self.config_entry: config_entries.ConfigEntry | None = None
|
||||
# Storage for entities for this specific platform only
|
||||
# which are indexed by entity_id
|
||||
self.entities: dict[str, Entity] = {}
|
||||
self.component_translations: dict[str, Any] = {}
|
||||
self.platform_translations: dict[str, Any] = {}
|
||||
@@ -145,9 +148,24 @@ class EntityPlatform:
|
||||
# which powers entity_component.add_entities
|
||||
self.parallel_updates_created = platform is None
|
||||
|
||||
self.domain_entities: dict[str, Entity] = hass.data.setdefault(
|
||||
# Storage for entities indexed by domain
|
||||
# with the child dict indexed by entity_id
|
||||
#
|
||||
# This is usually media_player, light, switch, etc.
|
||||
domain_entities: dict[str, dict[str, Entity]] = hass.data.setdefault(
|
||||
DATA_DOMAIN_ENTITIES, {}
|
||||
).setdefault(domain, {})
|
||||
)
|
||||
self.domain_entities = domain_entities.setdefault(domain, {})
|
||||
|
||||
# Storage for entities indexed by domain and platform
|
||||
# with the child dict indexed by entity_id
|
||||
#
|
||||
# This is usually media_player.yamaha, light.hue, switch.tplink, etc.
|
||||
domain_platform_entities: dict[
|
||||
tuple[str, str], dict[str, Entity]
|
||||
] = hass.data.setdefault(DATA_DOMAIN_PLATFORM_ENTITIES, {})
|
||||
key = (domain, platform_name)
|
||||
self.domain_platform_entities = domain_platform_entities.setdefault(key, {})
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Represent an EntityPlatform."""
|
||||
@@ -743,6 +761,7 @@ class EntityPlatform:
|
||||
entity_id = entity.entity_id
|
||||
self.entities[entity_id] = entity
|
||||
self.domain_entities[entity_id] = entity
|
||||
self.domain_platform_entities[entity_id] = entity
|
||||
|
||||
if not restored:
|
||||
# Reserve the state in the state machine
|
||||
@@ -756,6 +775,7 @@ class EntityPlatform:
|
||||
"""Remove entity from entities dict."""
|
||||
self.entities.pop(entity_id)
|
||||
self.domain_entities.pop(entity_id)
|
||||
self.domain_platform_entities.pop(entity_id)
|
||||
|
||||
entity.async_on_remove(remove_entity_cb)
|
||||
|
||||
@@ -852,7 +872,7 @@ class EntityPlatform:
|
||||
partial(
|
||||
service.entity_service_call,
|
||||
self.hass,
|
||||
self.domain_entities,
|
||||
self.domain_platform_entities,
|
||||
service_func,
|
||||
required_features=required_features,
|
||||
),
|
||||
|
||||
@@ -9,7 +9,7 @@ astral==2.2
|
||||
async-upnp-client==0.38.1
|
||||
atomicwrites-homeassistant==1.4.1
|
||||
attrs==23.2.0
|
||||
awesomeversion==23.11.0
|
||||
awesomeversion==24.2.0
|
||||
bcrypt==4.0.1
|
||||
bleak-retry-connector==3.4.0
|
||||
bleak==0.21.1
|
||||
@@ -188,3 +188,6 @@ dacite>=1.7.0
|
||||
|
||||
# Musle wheels for pandas 2.2.0 cannot be build for any architecture.
|
||||
pandas==2.1.4
|
||||
|
||||
# chacha20poly1305-reuseable==0.12.0 is incompatible with cryptography==42.0.x
|
||||
chacha20poly1305-reuseable>=0.12.1
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "homeassistant"
|
||||
version = "2024.2.0b8"
|
||||
version = "2024.2.0b10"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "Open-source home automation platform running on Python 3."
|
||||
readme = "README.rst"
|
||||
@@ -30,7 +30,7 @@ dependencies = [
|
||||
"astral==2.2",
|
||||
"attrs==23.2.0",
|
||||
"atomicwrites-homeassistant==1.4.1",
|
||||
"awesomeversion==23.11.0",
|
||||
"awesomeversion==24.2.0",
|
||||
"bcrypt==4.0.1",
|
||||
"certifi>=2021.5.30",
|
||||
"ciso8601==2.3.0",
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ aiohttp-zlib-ng==0.3.1
|
||||
astral==2.2
|
||||
attrs==23.2.0
|
||||
atomicwrites-homeassistant==1.4.1
|
||||
awesomeversion==23.11.0
|
||||
awesomeversion==24.2.0
|
||||
bcrypt==4.0.1
|
||||
certifi>=2021.5.30
|
||||
ciso8601==2.3.0
|
||||
|
||||
+16
-16
@@ -76,7 +76,7 @@ PyMetEireann==2021.8.0
|
||||
PyMetno==0.11.0
|
||||
|
||||
# homeassistant.components.keymitt_ble
|
||||
PyMicroBot==0.0.9
|
||||
PyMicroBot==0.0.10
|
||||
|
||||
# homeassistant.components.nina
|
||||
PyNINA==0.3.3
|
||||
@@ -230,16 +230,16 @@ aioeafm==0.1.2
|
||||
aioeagle==1.1.0
|
||||
|
||||
# homeassistant.components.ecowitt
|
||||
aioecowitt==2023.5.0
|
||||
aioecowitt==2024.2.0
|
||||
|
||||
# homeassistant.components.co2signal
|
||||
aioelectricitymaps==0.3.0
|
||||
aioelectricitymaps==0.3.1
|
||||
|
||||
# homeassistant.components.emonitor
|
||||
aioemonitor==1.0.5
|
||||
|
||||
# homeassistant.components.esphome
|
||||
aioesphomeapi==21.0.1
|
||||
aioesphomeapi==21.0.2
|
||||
|
||||
# homeassistant.components.flo
|
||||
aioflo==2021.11.0
|
||||
@@ -535,7 +535,7 @@ beautifulsoup4==4.12.2
|
||||
# beewi-smartclim==0.0.10
|
||||
|
||||
# homeassistant.components.zha
|
||||
bellows==0.37.6
|
||||
bellows==0.38.0
|
||||
|
||||
# homeassistant.components.bmw_connected_drive
|
||||
bimmer-connected[china]==0.14.6
|
||||
@@ -1056,7 +1056,7 @@ hole==0.8.0
|
||||
|
||||
# homeassistant.components.holiday
|
||||
# homeassistant.components.workday
|
||||
holidays==0.41
|
||||
holidays==0.42
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20240205.0
|
||||
@@ -1310,7 +1310,7 @@ moehlenhoff-alpha2==1.3.0
|
||||
mopeka-iot-ble==0.5.0
|
||||
|
||||
# homeassistant.components.motion_blinds
|
||||
motionblinds==0.6.19
|
||||
motionblinds==0.6.20
|
||||
|
||||
# homeassistant.components.motioneye
|
||||
motioneye-client==0.3.14
|
||||
@@ -1940,7 +1940,7 @@ pymata-express==1.19
|
||||
pymediaroom==0.6.5.4
|
||||
|
||||
# homeassistant.components.melcloud
|
||||
pymelcloud==2.5.8
|
||||
pymelcloud==2.5.9
|
||||
|
||||
# homeassistant.components.meteoclimatic
|
||||
pymeteoclimatic==0.1.0
|
||||
@@ -2257,7 +2257,7 @@ python-opensky==1.0.0
|
||||
|
||||
# homeassistant.components.otbr
|
||||
# homeassistant.components.thread
|
||||
python-otbr-api==2.5.0
|
||||
python-otbr-api==2.6.0
|
||||
|
||||
# homeassistant.components.picnic
|
||||
python-picnic-api==1.1.0
|
||||
@@ -2429,7 +2429,7 @@ rfk101py==0.0.1
|
||||
rflink==0.0.65
|
||||
|
||||
# homeassistant.components.ring
|
||||
ring-doorbell[listen]==0.8.5
|
||||
ring-doorbell[listen]==0.8.7
|
||||
|
||||
# homeassistant.components.fleetgo
|
||||
ritassist==0.9.2
|
||||
@@ -2757,7 +2757,7 @@ unifi_ap==0.0.1
|
||||
unifiled==0.11
|
||||
|
||||
# homeassistant.components.zha
|
||||
universal-silabs-flasher==0.0.15
|
||||
universal-silabs-flasher==0.0.18
|
||||
|
||||
# homeassistant.components.upb
|
||||
upb-lib==0.5.4
|
||||
@@ -2856,10 +2856,10 @@ xbox-webapi==2.0.11
|
||||
xiaomi-ble==0.23.1
|
||||
|
||||
# homeassistant.components.knx
|
||||
xknx==2.11.2
|
||||
xknx==2.12.0
|
||||
|
||||
# homeassistant.components.knx
|
||||
xknxproject==3.4.0
|
||||
xknxproject==3.5.0
|
||||
|
||||
# homeassistant.components.bluesound
|
||||
# homeassistant.components.fritz
|
||||
@@ -2913,7 +2913,7 @@ zeroconf==0.131.0
|
||||
zeversolar==0.3.1
|
||||
|
||||
# homeassistant.components.zha
|
||||
zha-quirks==0.0.110
|
||||
zha-quirks==0.0.111
|
||||
|
||||
# homeassistant.components.zhong_hong
|
||||
zhong-hong-hvac==1.0.9
|
||||
@@ -2922,7 +2922,7 @@ zhong-hong-hvac==1.0.9
|
||||
ziggo-mediabox-xl==1.1.0
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy-deconz==0.22.4
|
||||
zigpy-deconz==0.23.0
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy-xbee==0.20.1
|
||||
@@ -2934,7 +2934,7 @@ zigpy-zigate==0.12.0
|
||||
zigpy-znp==0.12.1
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy==0.61.0
|
||||
zigpy==0.62.3
|
||||
|
||||
# homeassistant.components.zoneminder
|
||||
zm-py==0.5.4
|
||||
|
||||
+16
-16
@@ -64,7 +64,7 @@ PyMetEireann==2021.8.0
|
||||
PyMetno==0.11.0
|
||||
|
||||
# homeassistant.components.keymitt_ble
|
||||
PyMicroBot==0.0.9
|
||||
PyMicroBot==0.0.10
|
||||
|
||||
# homeassistant.components.nina
|
||||
PyNINA==0.3.3
|
||||
@@ -209,16 +209,16 @@ aioeafm==0.1.2
|
||||
aioeagle==1.1.0
|
||||
|
||||
# homeassistant.components.ecowitt
|
||||
aioecowitt==2023.5.0
|
||||
aioecowitt==2024.2.0
|
||||
|
||||
# homeassistant.components.co2signal
|
||||
aioelectricitymaps==0.3.0
|
||||
aioelectricitymaps==0.3.1
|
||||
|
||||
# homeassistant.components.emonitor
|
||||
aioemonitor==1.0.5
|
||||
|
||||
# homeassistant.components.esphome
|
||||
aioesphomeapi==21.0.1
|
||||
aioesphomeapi==21.0.2
|
||||
|
||||
# homeassistant.components.flo
|
||||
aioflo==2021.11.0
|
||||
@@ -457,7 +457,7 @@ base36==0.1.1
|
||||
beautifulsoup4==4.12.2
|
||||
|
||||
# homeassistant.components.zha
|
||||
bellows==0.37.6
|
||||
bellows==0.38.0
|
||||
|
||||
# homeassistant.components.bmw_connected_drive
|
||||
bimmer-connected[china]==0.14.6
|
||||
@@ -852,7 +852,7 @@ hole==0.8.0
|
||||
|
||||
# homeassistant.components.holiday
|
||||
# homeassistant.components.workday
|
||||
holidays==0.41
|
||||
holidays==0.42
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20240205.0
|
||||
@@ -1046,7 +1046,7 @@ moehlenhoff-alpha2==1.3.0
|
||||
mopeka-iot-ble==0.5.0
|
||||
|
||||
# homeassistant.components.motion_blinds
|
||||
motionblinds==0.6.19
|
||||
motionblinds==0.6.20
|
||||
|
||||
# homeassistant.components.motioneye
|
||||
motioneye-client==0.3.14
|
||||
@@ -1494,7 +1494,7 @@ pymailgunner==1.4
|
||||
pymata-express==1.19
|
||||
|
||||
# homeassistant.components.melcloud
|
||||
pymelcloud==2.5.8
|
||||
pymelcloud==2.5.9
|
||||
|
||||
# homeassistant.components.meteoclimatic
|
||||
pymeteoclimatic==0.1.0
|
||||
@@ -1727,7 +1727,7 @@ python-opensky==1.0.0
|
||||
|
||||
# homeassistant.components.otbr
|
||||
# homeassistant.components.thread
|
||||
python-otbr-api==2.5.0
|
||||
python-otbr-api==2.6.0
|
||||
|
||||
# homeassistant.components.picnic
|
||||
python-picnic-api==1.1.0
|
||||
@@ -1860,7 +1860,7 @@ reolink-aio==0.8.7
|
||||
rflink==0.0.65
|
||||
|
||||
# homeassistant.components.ring
|
||||
ring-doorbell[listen]==0.8.5
|
||||
ring-doorbell[listen]==0.8.7
|
||||
|
||||
# homeassistant.components.roku
|
||||
rokuecp==0.19.0
|
||||
@@ -2098,7 +2098,7 @@ ultraheat-api==0.5.7
|
||||
unifi-discovery==1.1.7
|
||||
|
||||
# homeassistant.components.zha
|
||||
universal-silabs-flasher==0.0.15
|
||||
universal-silabs-flasher==0.0.18
|
||||
|
||||
# homeassistant.components.upb
|
||||
upb-lib==0.5.4
|
||||
@@ -2185,10 +2185,10 @@ xbox-webapi==2.0.11
|
||||
xiaomi-ble==0.23.1
|
||||
|
||||
# homeassistant.components.knx
|
||||
xknx==2.11.2
|
||||
xknx==2.12.0
|
||||
|
||||
# homeassistant.components.knx
|
||||
xknxproject==3.4.0
|
||||
xknxproject==3.5.0
|
||||
|
||||
# homeassistant.components.bluesound
|
||||
# homeassistant.components.fritz
|
||||
@@ -2233,10 +2233,10 @@ zeroconf==0.131.0
|
||||
zeversolar==0.3.1
|
||||
|
||||
# homeassistant.components.zha
|
||||
zha-quirks==0.0.110
|
||||
zha-quirks==0.0.111
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy-deconz==0.22.4
|
||||
zigpy-deconz==0.23.0
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy-xbee==0.20.1
|
||||
@@ -2248,7 +2248,7 @@ zigpy-zigate==0.12.0
|
||||
zigpy-znp==0.12.1
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy==0.61.0
|
||||
zigpy==0.62.3
|
||||
|
||||
# homeassistant.components.zwave_js
|
||||
zwave-js-server-python==0.55.3
|
||||
|
||||
@@ -181,6 +181,9 @@ dacite>=1.7.0
|
||||
|
||||
# Musle wheels for pandas 2.2.0 cannot be build for any architecture.
|
||||
pandas==2.1.4
|
||||
|
||||
# chacha20poly1305-reuseable==0.12.0 is incompatible with cryptography==42.0.x
|
||||
chacha20poly1305-reuseable>=0.12.1
|
||||
"""
|
||||
|
||||
GENERATED_MESSAGE = (
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""Test the Homeassistant Analytics config flow."""
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
from python_homeassistant_analytics import HomeassistantAnalyticsConnectionError
|
||||
|
||||
from homeassistant import config_entries
|
||||
@@ -16,8 +18,45 @@ from tests.common import MockConfigEntry
|
||||
from tests.components.analytics_insights import setup_integration
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("user_input", "expected_options"),
|
||||
[
|
||||
(
|
||||
{
|
||||
CONF_TRACKED_INTEGRATIONS: ["youtube"],
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: ["hacs"],
|
||||
},
|
||||
{
|
||||
CONF_TRACKED_INTEGRATIONS: ["youtube"],
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: ["hacs"],
|
||||
},
|
||||
),
|
||||
(
|
||||
{
|
||||
CONF_TRACKED_INTEGRATIONS: ["youtube"],
|
||||
},
|
||||
{
|
||||
CONF_TRACKED_INTEGRATIONS: ["youtube"],
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: [],
|
||||
},
|
||||
),
|
||||
(
|
||||
{
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: ["hacs"],
|
||||
},
|
||||
{
|
||||
CONF_TRACKED_INTEGRATIONS: [],
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: ["hacs"],
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_form(
|
||||
hass: HomeAssistant, mock_setup_entry: AsyncMock, mock_analytics_client: AsyncMock
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_analytics_client: AsyncMock,
|
||||
user_input: dict[str, Any],
|
||||
expected_options: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test we get the form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@@ -25,6 +64,50 @@ async def test_form(
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "Home Assistant Analytics Insights"
|
||||
assert result["data"] == {}
|
||||
assert result["options"] == expected_options
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"user_input",
|
||||
[
|
||||
{
|
||||
CONF_TRACKED_INTEGRATIONS: [],
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: [],
|
||||
},
|
||||
{},
|
||||
],
|
||||
)
|
||||
async def test_submitting_empty_form(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_analytics_client: AsyncMock,
|
||||
user_input: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test we can't submit an empty form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "no_integrations_selected"}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
@@ -81,10 +164,45 @@ async def test_form_already_configured(
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("user_input", "expected_options"),
|
||||
[
|
||||
(
|
||||
{
|
||||
CONF_TRACKED_INTEGRATIONS: ["youtube"],
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: ["hacs"],
|
||||
},
|
||||
{
|
||||
CONF_TRACKED_INTEGRATIONS: ["youtube"],
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: ["hacs"],
|
||||
},
|
||||
),
|
||||
(
|
||||
{
|
||||
CONF_TRACKED_INTEGRATIONS: ["youtube"],
|
||||
},
|
||||
{
|
||||
CONF_TRACKED_INTEGRATIONS: ["youtube"],
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: [],
|
||||
},
|
||||
),
|
||||
(
|
||||
{
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: ["hacs"],
|
||||
},
|
||||
{
|
||||
CONF_TRACKED_INTEGRATIONS: [],
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: ["hacs"],
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_options_flow(
|
||||
hass: HomeAssistant,
|
||||
mock_analytics_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
user_input: dict[str, Any],
|
||||
expected_options: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test options flow."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
@@ -95,7 +213,50 @@ async def test_options_flow(
|
||||
mock_analytics_client.get_integrations.reset_mock()
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
user_input,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["data"] == expected_options
|
||||
await hass.async_block_till_done()
|
||||
mock_analytics_client.get_integrations.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"user_input",
|
||||
[
|
||||
{
|
||||
CONF_TRACKED_INTEGRATIONS: [],
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: [],
|
||||
},
|
||||
{},
|
||||
],
|
||||
)
|
||||
async def test_submitting_empty_options_flow(
|
||||
hass: HomeAssistant,
|
||||
mock_analytics_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
user_input: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test options flow."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
result = await hass.config_entries.options.async_init(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "no_integrations_selected"}
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_TRACKED_INTEGRATIONS: ["youtube", "hue"],
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: ["hacs"],
|
||||
},
|
||||
@@ -108,7 +269,6 @@ async def test_options_flow(
|
||||
CONF_TRACKED_CUSTOM_INTEGRATIONS: ["hacs"],
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
mock_analytics_client.get_integrations.assert_called_once()
|
||||
|
||||
|
||||
async def test_options_flow_cannot_connect(
|
||||
|
||||
@@ -245,7 +245,7 @@ async def test_setup_api_ping(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result
|
||||
assert aioclient_mock.call_count == 20
|
||||
assert aioclient_mock.call_count == 19
|
||||
assert hass.components.hassio.get_core_info()["version_latest"] == "1.0.0"
|
||||
assert hass.components.hassio.is_hassio()
|
||||
|
||||
@@ -290,7 +290,7 @@ async def test_setup_api_push_api_data(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result
|
||||
assert aioclient_mock.call_count == 20
|
||||
assert aioclient_mock.call_count == 19
|
||||
assert not aioclient_mock.mock_calls[1][2]["ssl"]
|
||||
assert aioclient_mock.mock_calls[1][2]["port"] == 9999
|
||||
assert aioclient_mock.mock_calls[1][2]["watchdog"]
|
||||
@@ -309,7 +309,7 @@ async def test_setup_api_push_api_data_server_host(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result
|
||||
assert aioclient_mock.call_count == 20
|
||||
assert aioclient_mock.call_count == 19
|
||||
assert not aioclient_mock.mock_calls[1][2]["ssl"]
|
||||
assert aioclient_mock.mock_calls[1][2]["port"] == 9999
|
||||
assert not aioclient_mock.mock_calls[1][2]["watchdog"]
|
||||
@@ -326,7 +326,7 @@ async def test_setup_api_push_api_data_default(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result
|
||||
assert aioclient_mock.call_count == 20
|
||||
assert aioclient_mock.call_count == 19
|
||||
assert not aioclient_mock.mock_calls[1][2]["ssl"]
|
||||
assert aioclient_mock.mock_calls[1][2]["port"] == 8123
|
||||
refresh_token = aioclient_mock.mock_calls[1][2]["refresh_token"]
|
||||
@@ -406,7 +406,7 @@ async def test_setup_api_existing_hassio_user(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result
|
||||
assert aioclient_mock.call_count == 20
|
||||
assert aioclient_mock.call_count == 19
|
||||
assert not aioclient_mock.mock_calls[1][2]["ssl"]
|
||||
assert aioclient_mock.mock_calls[1][2]["port"] == 8123
|
||||
assert aioclient_mock.mock_calls[1][2]["refresh_token"] == token.token
|
||||
@@ -423,7 +423,7 @@ async def test_setup_core_push_timezone(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result
|
||||
assert aioclient_mock.call_count == 20
|
||||
assert aioclient_mock.call_count == 19
|
||||
assert aioclient_mock.mock_calls[2][2]["timezone"] == "testzone"
|
||||
|
||||
with patch("homeassistant.util.dt.set_default_time_zone"):
|
||||
@@ -443,7 +443,7 @@ async def test_setup_hassio_no_additional_data(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result
|
||||
assert aioclient_mock.call_count == 20
|
||||
assert aioclient_mock.call_count == 19
|
||||
assert aioclient_mock.mock_calls[-1][3]["Authorization"] == "Bearer 123456"
|
||||
|
||||
|
||||
@@ -525,14 +525,14 @@ async def test_service_calls(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 24
|
||||
assert aioclient_mock.call_count == 23
|
||||
assert aioclient_mock.mock_calls[-1][2] == "test"
|
||||
|
||||
await hass.services.async_call("hassio", "host_shutdown", {})
|
||||
await hass.services.async_call("hassio", "host_reboot", {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 26
|
||||
assert aioclient_mock.call_count == 25
|
||||
|
||||
await hass.services.async_call("hassio", "backup_full", {})
|
||||
await hass.services.async_call(
|
||||
@@ -547,7 +547,7 @@ async def test_service_calls(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 28
|
||||
assert aioclient_mock.call_count == 27
|
||||
assert aioclient_mock.mock_calls[-1][2] == {
|
||||
"name": "2021-11-13 03:48:00",
|
||||
"homeassistant": True,
|
||||
@@ -572,7 +572,7 @@ async def test_service_calls(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 30
|
||||
assert aioclient_mock.call_count == 29
|
||||
assert aioclient_mock.mock_calls[-1][2] == {
|
||||
"addons": ["test"],
|
||||
"folders": ["ssl"],
|
||||
@@ -591,7 +591,7 @@ async def test_service_calls(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 31
|
||||
assert aioclient_mock.call_count == 30
|
||||
assert aioclient_mock.mock_calls[-1][2] == {
|
||||
"name": "backup_name",
|
||||
"location": "backup_share",
|
||||
@@ -607,7 +607,7 @@ async def test_service_calls(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 32
|
||||
assert aioclient_mock.call_count == 31
|
||||
assert aioclient_mock.mock_calls[-1][2] == {
|
||||
"name": "2021-11-13 03:48:00",
|
||||
"location": None,
|
||||
@@ -625,7 +625,7 @@ async def test_service_calls(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 34
|
||||
assert aioclient_mock.call_count == 33
|
||||
assert aioclient_mock.mock_calls[-1][2] == {
|
||||
"name": "2021-11-13 11:48:00",
|
||||
"location": None,
|
||||
@@ -702,12 +702,12 @@ async def test_service_calls_core(
|
||||
await hass.services.async_call("homeassistant", "stop")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 6
|
||||
assert aioclient_mock.call_count == 5
|
||||
|
||||
await hass.services.async_call("homeassistant", "check_config")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 6
|
||||
assert aioclient_mock.call_count == 5
|
||||
|
||||
with patch(
|
||||
"homeassistant.config.async_check_ha_config_file", return_value=None
|
||||
@@ -716,7 +716,7 @@ async def test_service_calls_core(
|
||||
await hass.async_block_till_done()
|
||||
assert mock_check_config.called
|
||||
|
||||
assert aioclient_mock.call_count == 7
|
||||
assert aioclient_mock.call_count == 6
|
||||
|
||||
|
||||
async def test_entry_load_and_unload(hass: HomeAssistant) -> None:
|
||||
@@ -897,14 +897,17 @@ async def test_coordinator_updates(
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
# Initial refresh without stats
|
||||
assert refresh_updates_mock.call_count == 1
|
||||
|
||||
# Initial refresh, no update refresh call
|
||||
assert refresh_updates_mock.call_count == 0
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.hassio.HassIO.refresh_updates",
|
||||
) as refresh_updates_mock:
|
||||
async_fire_time_changed(hass, dt_util.now() + timedelta(minutes=20))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Scheduled refresh, no update refresh call
|
||||
assert refresh_updates_mock.call_count == 0
|
||||
|
||||
with patch(
|
||||
@@ -921,13 +924,14 @@ async def test_coordinator_updates(
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert refresh_updates_mock.call_count == 0
|
||||
|
||||
# There is a REQUEST_REFRESH_DELAYs cooldown on the debouncer
|
||||
async_fire_time_changed(
|
||||
hass, dt_util.now() + timedelta(seconds=REQUEST_REFRESH_DELAY)
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
# There is a REQUEST_REFRESH_DELAYs cooldown on the debouncer
|
||||
assert refresh_updates_mock.call_count == 0
|
||||
async_fire_time_changed(
|
||||
hass, dt_util.now() + timedelta(seconds=REQUEST_REFRESH_DELAY)
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert refresh_updates_mock.call_count == 1
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.hassio.HassIO.refresh_updates",
|
||||
@@ -968,14 +972,14 @@ async def test_coordinator_updates_stats_entities_enabled(
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
# Initial refresh without stats
|
||||
assert refresh_updates_mock.call_count == 1
|
||||
assert refresh_updates_mock.call_count == 0
|
||||
|
||||
# Refresh with stats once we know which ones are needed
|
||||
async_fire_time_changed(
|
||||
hass, dt_util.now() + timedelta(seconds=REQUEST_REFRESH_DELAY)
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert refresh_updates_mock.call_count == 2
|
||||
assert refresh_updates_mock.call_count == 1
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.hassio.HassIO.refresh_updates",
|
||||
@@ -1059,7 +1063,7 @@ async def test_setup_hardware_integration(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result
|
||||
assert aioclient_mock.call_count == 20
|
||||
assert aioclient_mock.call_count == 19
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
""""Unit tests for the Lupusec config flow."""
|
||||
|
||||
from json import JSONDecodeError
|
||||
from unittest.mock import patch
|
||||
|
||||
from lupupy import LupusecException
|
||||
@@ -51,8 +52,7 @@ async def test_form_valid_input(hass: HomeAssistant) -> None:
|
||||
"homeassistant.components.lupusec.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry, patch(
|
||||
"homeassistant.components.lupusec.config_flow.lupupy.Lupusec.__init__",
|
||||
return_value=None,
|
||||
"homeassistant.components.lupusec.config_flow.lupupy.Lupusec",
|
||||
) as mock_initialize_lupusec:
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
@@ -71,6 +71,7 @@ async def test_form_valid_input(hass: HomeAssistant) -> None:
|
||||
("raise_error", "text_error"),
|
||||
[
|
||||
(LupusecException("Test lupusec exception"), "cannot_connect"),
|
||||
(JSONDecodeError("Test JSONDecodeError", "test", 1), "cannot_connect"),
|
||||
(Exception("Test unknown exception"), "unknown"),
|
||||
],
|
||||
)
|
||||
@@ -85,7 +86,7 @@ async def test_flow_user_init_data_error_and_recover(
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.lupusec.config_flow.lupupy.Lupusec.__init__",
|
||||
"homeassistant.components.lupusec.config_flow.lupupy.Lupusec",
|
||||
side_effect=raise_error,
|
||||
) as mock_initialize_lupusec:
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
@@ -104,8 +105,7 @@ async def test_flow_user_init_data_error_and_recover(
|
||||
"homeassistant.components.lupusec.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry, patch(
|
||||
"homeassistant.components.lupusec.config_flow.lupupy.Lupusec.__init__",
|
||||
return_value=None,
|
||||
"homeassistant.components.lupusec.config_flow.lupupy.Lupusec",
|
||||
) as mock_initialize_lupusec:
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
@@ -164,8 +164,7 @@ async def test_flow_source_import(
|
||||
"homeassistant.components.lupusec.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry, patch(
|
||||
"homeassistant.components.lupusec.config_flow.lupupy.Lupusec.__init__",
|
||||
return_value=None,
|
||||
"homeassistant.components.lupusec.config_flow.lupupy.Lupusec",
|
||||
) as mock_initialize_lupusec:
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
@@ -186,6 +185,7 @@ async def test_flow_source_import(
|
||||
("raise_error", "text_error"),
|
||||
[
|
||||
(LupusecException("Test lupusec exception"), "cannot_connect"),
|
||||
(JSONDecodeError("Test JSONDecodeError", "test", 1), "cannot_connect"),
|
||||
(Exception("Test unknown exception"), "unknown"),
|
||||
],
|
||||
)
|
||||
@@ -195,7 +195,7 @@ async def test_flow_source_import_error_and_recover(
|
||||
"""Test exceptions and recovery."""
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.lupusec.config_flow.lupupy.Lupusec.__init__",
|
||||
"homeassistant.components.lupusec.config_flow.lupupy.Lupusec",
|
||||
side_effect=raise_error,
|
||||
) as mock_initialize_lupusec:
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
|
||||
@@ -416,8 +416,8 @@ async def test_bandwidth_sensors(
|
||||
async_fire_time_changed(hass, new_time)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("sensor.wireless_client_rx").state == "0"
|
||||
assert hass.states.get("sensor.wireless_client_tx").state == "0"
|
||||
assert hass.states.get("sensor.wireless_client_rx").state == STATE_UNAVAILABLE
|
||||
assert hass.states.get("sensor.wireless_client_tx").state == STATE_UNAVAILABLE
|
||||
|
||||
# Disable option
|
||||
|
||||
|
||||
@@ -368,6 +368,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
"report_count",
|
||||
"read_plug",
|
||||
"unsupported_attrs",
|
||||
"initial_sensor_state",
|
||||
),
|
||||
(
|
||||
(
|
||||
@@ -377,6 +378,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
1,
|
||||
None,
|
||||
None,
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
(
|
||||
measurement.TemperatureMeasurement.cluster_id,
|
||||
@@ -385,6 +387,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
1,
|
||||
None,
|
||||
None,
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
(
|
||||
measurement.PressureMeasurement.cluster_id,
|
||||
@@ -393,6 +396,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
1,
|
||||
None,
|
||||
None,
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
(
|
||||
measurement.IlluminanceMeasurement.cluster_id,
|
||||
@@ -401,6 +405,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
1,
|
||||
None,
|
||||
None,
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
(
|
||||
smartenergy.Metering.cluster_id,
|
||||
@@ -415,6 +420,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
"status": 0x00,
|
||||
},
|
||||
{"current_summ_delivered", "current_summ_received"},
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
(
|
||||
smartenergy.Metering.cluster_id,
|
||||
@@ -431,6 +437,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
"unit_of_measure": 0x00,
|
||||
},
|
||||
{"instaneneous_demand", "current_summ_received"},
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
(
|
||||
smartenergy.Metering.cluster_id,
|
||||
@@ -445,8 +452,10 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
"status": 0x00,
|
||||
"summation_formatting": 0b1_0111_010,
|
||||
"unit_of_measure": 0x00,
|
||||
"current_summ_received": 0,
|
||||
},
|
||||
{"instaneneous_demand", "current_summ_delivered"},
|
||||
"0.0",
|
||||
),
|
||||
(
|
||||
homeautomation.ElectricalMeasurement.cluster_id,
|
||||
@@ -455,6 +464,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
7,
|
||||
{"ac_power_divisor": 1000, "ac_power_multiplier": 1},
|
||||
{"apparent_power", "rms_current", "rms_voltage"},
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
(
|
||||
homeautomation.ElectricalMeasurement.cluster_id,
|
||||
@@ -463,6 +473,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
7,
|
||||
{"ac_power_divisor": 1000, "ac_power_multiplier": 1},
|
||||
{"active_power", "rms_current", "rms_voltage"},
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
(
|
||||
homeautomation.ElectricalMeasurement.cluster_id,
|
||||
@@ -471,6 +482,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
7,
|
||||
{"ac_power_divisor": 1000, "ac_power_multiplier": 1},
|
||||
{"active_power", "apparent_power", "rms_current", "rms_voltage"},
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
(
|
||||
homeautomation.ElectricalMeasurement.cluster_id,
|
||||
@@ -479,6 +491,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
7,
|
||||
{"ac_current_divisor": 1000, "ac_current_multiplier": 1},
|
||||
{"active_power", "apparent_power", "rms_voltage"},
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
(
|
||||
homeautomation.ElectricalMeasurement.cluster_id,
|
||||
@@ -487,6 +500,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
7,
|
||||
{"ac_voltage_divisor": 10, "ac_voltage_multiplier": 1},
|
||||
{"active_power", "apparent_power", "rms_current"},
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
(
|
||||
general.PowerConfiguration.cluster_id,
|
||||
@@ -499,6 +513,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
"battery_quantity": 3,
|
||||
},
|
||||
None,
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
(
|
||||
general.PowerConfiguration.cluster_id,
|
||||
@@ -511,6 +526,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
"battery_quantity": 3,
|
||||
},
|
||||
None,
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
(
|
||||
general.DeviceTemperature.cluster_id,
|
||||
@@ -519,6 +535,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
1,
|
||||
None,
|
||||
None,
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
(
|
||||
hvac.Thermostat.cluster_id,
|
||||
@@ -527,6 +544,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
10,
|
||||
None,
|
||||
None,
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
(
|
||||
hvac.Thermostat.cluster_id,
|
||||
@@ -535,6 +553,7 @@ async def async_test_pi_heating_demand(hass, cluster, entity_id):
|
||||
10,
|
||||
None,
|
||||
None,
|
||||
STATE_UNKNOWN,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -548,6 +567,7 @@ async def test_sensor(
|
||||
report_count,
|
||||
read_plug,
|
||||
unsupported_attrs,
|
||||
initial_sensor_state,
|
||||
) -> None:
|
||||
"""Test ZHA sensor platform."""
|
||||
|
||||
@@ -582,8 +602,8 @@ async def test_sensor(
|
||||
# allow traffic to flow through the gateway and devices
|
||||
await async_enable_traffic(hass, [zha_device])
|
||||
|
||||
# test that the sensor now have a state of unknown
|
||||
assert hass.states.get(entity_id).state == STATE_UNKNOWN
|
||||
# test that the sensor now have their correct initial state (mostly unknown)
|
||||
assert hass.states.get(entity_id).state == initial_sensor_state
|
||||
|
||||
# test sensor associated logic
|
||||
await test_func(hass, cluster, entity_id)
|
||||
@@ -826,7 +846,6 @@ async def test_electrical_measurement_init(
|
||||
},
|
||||
{
|
||||
"summation_delivered",
|
||||
"summation_received",
|
||||
},
|
||||
{
|
||||
"instantaneous_demand",
|
||||
@@ -834,12 +853,11 @@ async def test_electrical_measurement_init(
|
||||
),
|
||||
(
|
||||
smartenergy.Metering.cluster_id,
|
||||
{"instantaneous_demand", "current_summ_delivered", "current_summ_received"},
|
||||
{"instantaneous_demand", "current_summ_delivered"},
|
||||
{},
|
||||
{
|
||||
"instantaneous_demand",
|
||||
"summation_delivered",
|
||||
"summation_received",
|
||||
},
|
||||
),
|
||||
(
|
||||
@@ -848,7 +866,6 @@ async def test_electrical_measurement_init(
|
||||
{
|
||||
"instantaneous_demand",
|
||||
"summation_delivered",
|
||||
"summation_received",
|
||||
},
|
||||
{},
|
||||
),
|
||||
|
||||
@@ -205,7 +205,7 @@ def make_packet(zigpy_device, cluster, cmd_name: str, **kwargs):
|
||||
command_id=cluster.commands_by_name[cmd_name].id,
|
||||
schema=cluster.commands_by_name[cmd_name].schema,
|
||||
disable_default_response=False,
|
||||
direction=foundation.Direction.Server_to_Client,
|
||||
direction=foundation.Direction.Client_to_Server,
|
||||
args=(),
|
||||
kwargs=kwargs,
|
||||
)
|
||||
|
||||
@@ -243,11 +243,6 @@ DEVICES = [
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.centralite_3210_l_summation_delivered",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.centralite_3210_l_summation_received",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
|
||||
@@ -616,13 +611,6 @@ DEVICES = [
|
||||
"sensor.climaxtechnology_psmp5_00_00_02_02tc_summation_delivered"
|
||||
),
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: (
|
||||
"sensor.climaxtechnology_psmp5_00_00_02_02tc_summation_received"
|
||||
),
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
|
||||
@@ -1617,11 +1605,6 @@ DEVICES = [
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.jasco_products_45852_summation_delivered",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.jasco_products_45852_summation_received",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
|
||||
@@ -1682,11 +1665,6 @@ DEVICES = [
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.jasco_products_45856_summation_delivered",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.jasco_products_45856_summation_received",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
|
||||
@@ -1747,11 +1725,6 @@ DEVICES = [
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.jasco_products_45857_summation_delivered",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.jasco_products_45857_summation_received",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
|
||||
@@ -2370,11 +2343,6 @@ DEVICES = [
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.lumi_lumi_relay_c2acn01_summation_delivered",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.lumi_lumi_relay_c2acn01_summation_received",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-1794"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergyMetering",
|
||||
@@ -4511,11 +4479,6 @@ DEVICES = [
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.sercomm_corp_sz_esw01_summation_delivered",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.sercomm_corp_sz_esw01_summation_received",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
|
||||
@@ -5362,11 +5325,6 @@ DEVICES = [
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.sengled_e11_g13_summation_delivered",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.sengled_e11_g13_summation_received",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
|
||||
@@ -5420,11 +5378,6 @@ DEVICES = [
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.sengled_e12_n14_summation_delivered",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.sengled_e12_n14_summation_received",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
|
||||
@@ -5478,11 +5431,6 @@ DEVICES = [
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.sengled_z01_a19nae26_summation_delivered",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_received"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
|
||||
DEV_SIG_ENT_MAP_ID: "sensor.sengled_z01_a19nae26_summation_received",
|
||||
},
|
||||
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
|
||||
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
|
||||
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
|
||||
|
||||
@@ -396,19 +396,19 @@ async def test_abort_discovered_multiple(
|
||||
HTTPStatus.UNAUTHORIZED,
|
||||
{},
|
||||
"oauth_unauthorized",
|
||||
"Token request failed (unknown): unknown",
|
||||
"Token request for oauth2_test failed (unknown): unknown",
|
||||
),
|
||||
(
|
||||
HTTPStatus.NOT_FOUND,
|
||||
{},
|
||||
"oauth_failed",
|
||||
"Token request failed (unknown): unknown",
|
||||
"Token request for oauth2_test failed (unknown): unknown",
|
||||
),
|
||||
(
|
||||
HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||
{},
|
||||
"oauth_failed",
|
||||
"Token request failed (unknown): unknown",
|
||||
"Token request for oauth2_test failed (unknown): unknown",
|
||||
),
|
||||
(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
@@ -418,7 +418,7 @@ async def test_abort_discovered_multiple(
|
||||
"error_uri": "See the full API docs at https://authorization-server.com/docs/access_token",
|
||||
},
|
||||
"oauth_failed",
|
||||
"Token request failed (invalid_request): Request was missing the",
|
||||
"Token request for oauth2_test failed (invalid_request): Request was missing the",
|
||||
),
|
||||
],
|
||||
)
|
||||
@@ -541,7 +541,7 @@ async def test_abort_if_oauth_token_closing_error(
|
||||
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
assert "Token request failed (unknown): unknown" in caplog.text
|
||||
assert "Token request for oauth2_test failed (unknown): unknown" in caplog.text
|
||||
|
||||
assert result["type"] == data_entry_flow.FlowResultType.ABORT
|
||||
assert result["reason"] == "oauth_unauthorized"
|
||||
|
||||
@@ -19,6 +19,7 @@ from homeassistant.core import (
|
||||
)
|
||||
from homeassistant.exceptions import HomeAssistantError, PlatformNotReady
|
||||
from homeassistant.helpers import (
|
||||
area_registry as ar,
|
||||
device_registry as dr,
|
||||
entity_platform,
|
||||
entity_registry as er,
|
||||
@@ -1628,6 +1629,87 @@ async def test_register_entity_service_response_data_multiple_matches_raises(
|
||||
)
|
||||
|
||||
|
||||
async def test_register_entity_service_limited_to_matching_platforms(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
area_registry: ar.AreaRegistry,
|
||||
) -> None:
|
||||
"""Test an entity services only targets entities for the platform and domain."""
|
||||
|
||||
mock_area = area_registry.async_get_or_create("mock_area")
|
||||
|
||||
entity1_entry = entity_registry.async_get_or_create(
|
||||
"base_platform", "mock_platform", "1234", suggested_object_id="entity1"
|
||||
)
|
||||
entity_registry.async_update_entity(entity1_entry.entity_id, area_id=mock_area.id)
|
||||
entity2_entry = entity_registry.async_get_or_create(
|
||||
"base_platform", "mock_platform", "5678", suggested_object_id="entity2"
|
||||
)
|
||||
entity_registry.async_update_entity(entity2_entry.entity_id, area_id=mock_area.id)
|
||||
entity3_entry = entity_registry.async_get_or_create(
|
||||
"base_platform", "other_mock_platform", "7891", suggested_object_id="entity3"
|
||||
)
|
||||
entity_registry.async_update_entity(entity3_entry.entity_id, area_id=mock_area.id)
|
||||
entity4_entry = entity_registry.async_get_or_create(
|
||||
"base_platform", "other_mock_platform", "1433", suggested_object_id="entity4"
|
||||
)
|
||||
entity_registry.async_update_entity(entity4_entry.entity_id, area_id=mock_area.id)
|
||||
|
||||
async def generate_response(
|
||||
target: MockEntity, call: ServiceCall
|
||||
) -> ServiceResponse:
|
||||
assert call.return_response
|
||||
return {"response-key": f"response-value-{target.entity_id}"}
|
||||
|
||||
entity_platform = MockEntityPlatform(
|
||||
hass, domain="base_platform", platform_name="mock_platform", platform=None
|
||||
)
|
||||
entity1 = MockEntity(
|
||||
entity_id=entity1_entry.entity_id, unique_id=entity1_entry.unique_id
|
||||
)
|
||||
entity2 = MockEntity(
|
||||
entity_id=entity2_entry.entity_id, unique_id=entity2_entry.unique_id
|
||||
)
|
||||
await entity_platform.async_add_entities([entity1, entity2])
|
||||
|
||||
other_entity_platform = MockEntityPlatform(
|
||||
hass, domain="base_platform", platform_name="other_mock_platform", platform=None
|
||||
)
|
||||
entity3 = MockEntity(
|
||||
entity_id=entity3_entry.entity_id, unique_id=entity3_entry.unique_id
|
||||
)
|
||||
entity4 = MockEntity(
|
||||
entity_id=entity4_entry.entity_id, unique_id=entity4_entry.unique_id
|
||||
)
|
||||
await other_entity_platform.async_add_entities([entity3, entity4])
|
||||
|
||||
entity_platform.async_register_entity_service(
|
||||
"hello",
|
||||
{"some": str},
|
||||
generate_response,
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
|
||||
response_data = await hass.services.async_call(
|
||||
"mock_platform",
|
||||
"hello",
|
||||
service_data={"some": "data"},
|
||||
target={"area_id": [mock_area.id]},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
# We should not target entity3 and entity4 even though they are in the area
|
||||
# because they are only part of the domain and not the platform
|
||||
assert response_data == {
|
||||
"base_platform.entity1": {
|
||||
"response-key": "response-value-base_platform.entity1"
|
||||
},
|
||||
"base_platform.entity2": {
|
||||
"response-key": "response-value-base_platform.entity2"
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
async def test_invalid_entity_id(hass: HomeAssistant) -> None:
|
||||
"""Test specifying an invalid entity id."""
|
||||
platform = MockEntityPlatform(hass)
|
||||
|
||||
Reference in New Issue
Block a user