mirror of
https://github.com/home-assistant/core.git
synced 2026-03-17 00:12:02 +01:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f552b8221f | ||
|
|
2f9faa53a1 |
@@ -1,5 +1,6 @@
|
||||
"""Defines a base Alexa Devices entity."""
|
||||
|
||||
from aioamazondevices.const.devices import SPEAKER_GROUP_DEVICE_TYPE
|
||||
from aioamazondevices.structures import AmazonDevice
|
||||
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
@@ -24,15 +25,20 @@ class AmazonEntity(CoordinatorEntity[AmazonDevicesCoordinator]):
|
||||
"""Initialize the entity."""
|
||||
super().__init__(coordinator)
|
||||
self._serial_num = serial_num
|
||||
model = self.device.model
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, serial_num)},
|
||||
name=self.device.account_name,
|
||||
model=self.device.model,
|
||||
model=model,
|
||||
model_id=self.device.device_type,
|
||||
manufacturer=self.device.manufacturer or "Amazon",
|
||||
hw_version=self.device.hardware_version,
|
||||
sw_version=self.device.software_version,
|
||||
serial_number=serial_num,
|
||||
sw_version=(
|
||||
self.device.software_version
|
||||
if model != SPEAKER_GROUP_DEVICE_TYPE
|
||||
else None
|
||||
),
|
||||
serial_number=serial_num if model != SPEAKER_GROUP_DEVICE_TYPE else None,
|
||||
)
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{serial_num}-{description.key}"
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aioamazondevices"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["aioamazondevices==13.0.1"]
|
||||
"requirements": ["aioamazondevices==13.0.0"]
|
||||
}
|
||||
|
||||
@@ -101,10 +101,7 @@ class AmazonSwitchEntity(AmazonEntity, SwitchEntity):
|
||||
assert method is not None
|
||||
|
||||
await method(self.device, state)
|
||||
self.coordinator.data[self.device.serial_number].sensors[
|
||||
self.entity_description.key
|
||||
].value = state
|
||||
self.async_write_ha_state()
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the switch on."""
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pyanglianwater"],
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["pyanglianwater==3.1.1"]
|
||||
"requirements": ["pyanglianwater==3.1.0"]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ from __future__ import annotations
|
||||
from pathlib import Path
|
||||
from typing import cast
|
||||
|
||||
from aiohttp import ClientError
|
||||
from aiohttp import ClientResponseError
|
||||
from yalexs.exceptions import AugustApiAIOHTTPError
|
||||
from yalexs.manager.exceptions import CannotConnect, InvalidAuth, RequireValidation
|
||||
from yalexs.manager.gateway import Config as YaleXSConfig
|
||||
@@ -13,12 +13,7 @@ from yalexs.manager.gateway import Config as YaleXSConfig
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import (
|
||||
ConfigEntryAuthFailed,
|
||||
ConfigEntryNotReady,
|
||||
OAuth2TokenRequestError,
|
||||
OAuth2TokenRequestReauthError,
|
||||
)
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import device_registry as dr, issue_registry as ir
|
||||
from homeassistant.helpers.config_entry_oauth2_flow import (
|
||||
ImplementationUnavailableError,
|
||||
@@ -50,18 +45,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: AugustConfigEntry) -> bo
|
||||
august_gateway = AugustGateway(Path(hass.config.config_dir), session, oauth_session)
|
||||
try:
|
||||
await async_setup_august(hass, entry, august_gateway)
|
||||
except OAuth2TokenRequestReauthError as err:
|
||||
raise ConfigEntryAuthFailed from err
|
||||
except (RequireValidation, InvalidAuth) as err:
|
||||
raise ConfigEntryAuthFailed from err
|
||||
except TimeoutError as err:
|
||||
raise ConfigEntryNotReady("Timed out connecting to august api") from err
|
||||
except (
|
||||
AugustApiAIOHTTPError,
|
||||
OAuth2TokenRequestError,
|
||||
ClientError,
|
||||
CannotConnect,
|
||||
) as err:
|
||||
except (AugustApiAIOHTTPError, ClientResponseError, CannotConnect) as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
@@ -30,5 +30,5 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["pubnub", "yalexs"],
|
||||
"requirements": ["yalexs==9.2.0", "yalexs-ble==3.2.8"]
|
||||
"requirements": ["yalexs==9.2.0", "yalexs-ble==3.2.7"]
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["casttube", "pychromecast"],
|
||||
"requirements": ["PyChromecast==14.0.10"],
|
||||
"requirements": ["PyChromecast==14.0.9"],
|
||||
"single_config_entry": true,
|
||||
"zeroconf": ["_googlecast._tcp.local."]
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["aiocomelit"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["aiocomelit==2.0.1"]
|
||||
"requirements": ["aiocomelit==2.0.0"]
|
||||
}
|
||||
|
||||
@@ -133,20 +133,26 @@ async def _async_wifi_entities_list(
|
||||
]
|
||||
)
|
||||
_LOGGER.debug("WiFi networks count: %s", wifi_count)
|
||||
networks: dict[int, dict[str, Any]] = {}
|
||||
networks: dict = {}
|
||||
for i in range(1, wifi_count + 1):
|
||||
network_info = await avm_wrapper.async_get_wlan_configuration(i)
|
||||
# Devices with 4 WLAN services, use the 2nd for internal communications
|
||||
if not (wifi_count == 4 and i == 2):
|
||||
networks[i] = network_info
|
||||
networks[i] = {
|
||||
"ssid": network_info["NewSSID"],
|
||||
"bssid": network_info["NewBSSID"],
|
||||
"standard": network_info["NewStandard"],
|
||||
"enabled": network_info["NewEnable"],
|
||||
"status": network_info["NewStatus"],
|
||||
}
|
||||
for i, network in networks.copy().items():
|
||||
networks[i]["switch_name"] = network["NewSSID"]
|
||||
networks[i]["switch_name"] = network["ssid"]
|
||||
if (
|
||||
len(
|
||||
[
|
||||
j
|
||||
for j, n in networks.items()
|
||||
if slugify(n["NewSSID"]) == slugify(network["NewSSID"])
|
||||
if slugify(n["ssid"]) == slugify(network["ssid"])
|
||||
]
|
||||
)
|
||||
> 1
|
||||
@@ -428,11 +434,13 @@ class FritzBoxPortSwitch(FritzBoxBaseSwitch):
|
||||
for key, attr in attributes_dict.items():
|
||||
self._attributes[attr] = self.port_mapping[key]
|
||||
|
||||
async def _async_switch_on_off_executor(self, turn_on: bool) -> None:
|
||||
async def _async_switch_on_off_executor(self, turn_on: bool) -> bool:
|
||||
self.port_mapping["NewEnabled"] = "1" if turn_on else "0"
|
||||
await self._avm_wrapper.async_add_port_mapping(
|
||||
|
||||
resp = await self._avm_wrapper.async_add_port_mapping(
|
||||
self.connection_type, self.port_mapping
|
||||
)
|
||||
return bool(resp is not None)
|
||||
|
||||
|
||||
class FritzBoxDeflectionSwitch(FritzBoxBaseCoordinatorSwitch):
|
||||
@@ -517,11 +525,12 @@ class FritzBoxProfileSwitch(FritzDeviceBase, SwitchEntity):
|
||||
"""Turn off switch."""
|
||||
await self._async_handle_turn_on_off(turn_on=False)
|
||||
|
||||
async def _async_handle_turn_on_off(self, turn_on: bool) -> None:
|
||||
async def _async_handle_turn_on_off(self, turn_on: bool) -> bool:
|
||||
"""Handle switch state change request."""
|
||||
await self._avm_wrapper.async_set_allow_wan_access(self.ip_address, turn_on)
|
||||
self._avm_wrapper.devices[self._mac].wan_access = turn_on
|
||||
self.async_write_ha_state()
|
||||
return True
|
||||
|
||||
|
||||
class FritzBoxWifiSwitch(FritzBoxBaseSwitch):
|
||||
@@ -532,11 +541,10 @@ class FritzBoxWifiSwitch(FritzBoxBaseSwitch):
|
||||
avm_wrapper: AvmWrapper,
|
||||
device_friendly_name: str,
|
||||
network_num: int,
|
||||
network_data: dict[str, Any],
|
||||
network_data: dict,
|
||||
) -> None:
|
||||
"""Init Fritz Wifi switch."""
|
||||
self._avm_wrapper = avm_wrapper
|
||||
self._wifi_info = network_data
|
||||
|
||||
self._attributes = {}
|
||||
self._attr_entity_category = EntityCategory.CONFIG
|
||||
@@ -552,7 +560,7 @@ class FritzBoxWifiSwitch(FritzBoxBaseSwitch):
|
||||
type=SWITCH_TYPE_WIFINETWORK,
|
||||
callback_update=self._async_fetch_update,
|
||||
callback_switch=self._async_switch_on_off_executor,
|
||||
init_state=network_data["NewEnable"],
|
||||
init_state=network_data["enabled"],
|
||||
)
|
||||
super().__init__(self._avm_wrapper, device_friendly_name, switch_info)
|
||||
|
||||
@@ -579,9 +587,7 @@ class FritzBoxWifiSwitch(FritzBoxBaseSwitch):
|
||||
self._attributes["mac_address_control"] = wifi_info[
|
||||
"NewMACAddressControlEnabled"
|
||||
]
|
||||
self._wifi_info = wifi_info
|
||||
|
||||
async def _async_switch_on_off_executor(self, turn_on: bool) -> None:
|
||||
"""Handle wifi switch."""
|
||||
self._wifi_info["NewEnable"] = turn_on
|
||||
await self._avm_wrapper.async_set_wlan_configuration(self._network_num, turn_on)
|
||||
|
||||
@@ -21,5 +21,5 @@
|
||||
"integration_type": "system",
|
||||
"preview_features": { "winter_mode": {} },
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["home-assistant-frontend==20260312.0"]
|
||||
"requirements": ["home-assistant-frontend==20260304.0"]
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["googleapiclient"],
|
||||
"requirements": ["gcal-sync==8.0.0", "oauth2client==4.1.3", "ical==13.2.2"]
|
||||
"requirements": ["gcal-sync==8.0.0", "oauth2client==4.1.3", "ical==13.2.0"]
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"dependencies": ["network"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/govee_light_local",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["govee-local-api==2.4.0"]
|
||||
"requirements": ["govee-local-api==2.3.0"]
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ INTELLIFIRE_SENSORS: tuple[IntellifireSensorEntityDescription, ...] = (
|
||||
IntellifireSensorEntityDescription(
|
||||
key="timer_end_timestamp",
|
||||
translation_key="timer_end_timestamp",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
value_fn=_time_remaining_to_timestamp,
|
||||
),
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["jvcprojector"],
|
||||
"requirements": ["pyjvcprojector==2.0.3"]
|
||||
"requirements": ["pyjvcprojector==2.0.1"]
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/local_calendar",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["ical"],
|
||||
"requirements": ["ical==13.2.2"]
|
||||
"requirements": ["ical==13.2.0"]
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/local_todo",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["ical==13.2.2"]
|
||||
"requirements": ["ical==13.2.0"]
|
||||
}
|
||||
|
||||
@@ -188,7 +188,6 @@ class ProgramPhaseTumbleDryer(MieleEnum, missing_to_none=True):
|
||||
finished = 522, 11012
|
||||
extra_dry = 523
|
||||
hand_iron = 524
|
||||
hygiene_drying = 525
|
||||
moisten = 526
|
||||
thermo_spin = 527
|
||||
timed_drying = 528
|
||||
|
||||
@@ -1006,7 +1006,6 @@
|
||||
"heating_up_phase": "Heating up phase",
|
||||
"hot_milk": "Hot milk",
|
||||
"hygiene": "Hygiene",
|
||||
"hygiene_drying": "Hygiene drying",
|
||||
"interim_rinse": "Interim rinse",
|
||||
"keep_warm": "Keep warm",
|
||||
"keeping_warm": "Keeping warm",
|
||||
|
||||
@@ -163,6 +163,8 @@ class MqttDeviceTracker(MqttEntity, TrackerEntity):
|
||||
latitude: float | None
|
||||
longitude: float | None
|
||||
gps_accuracy: float
|
||||
# Reset manually set location to allow automatic zone detection
|
||||
self._attr_location_name = None
|
||||
if isinstance(
|
||||
latitude := extra_state_attributes.get(ATTR_LATITUDE), (int, float)
|
||||
) and isinstance(
|
||||
|
||||
@@ -19,5 +19,6 @@ async def async_get_config_entry_diagnostics(
|
||||
return {
|
||||
"device_info": client.device_info,
|
||||
"vehicles": client.vehicles,
|
||||
"ct_connected": client.ct_connected,
|
||||
"cap_available": client.cap_available,
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "cloud_polling",
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["ohme==1.7.0"]
|
||||
"requirements": ["ohme==1.6.0"]
|
||||
}
|
||||
|
||||
@@ -10,5 +10,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["onedrive_personal_sdk"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["onedrive-personal-sdk==0.1.7"]
|
||||
"requirements": ["onedrive-personal-sdk==0.1.5"]
|
||||
}
|
||||
|
||||
@@ -10,5 +10,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["onedrive_personal_sdk"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["onedrive-personal-sdk==0.1.7"]
|
||||
"requirements": ["onedrive-personal-sdk==0.1.5"]
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/otbr",
|
||||
"integration_type": "service",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["python-otbr-api==2.9.0"]
|
||||
"requirements": ["python-otbr-api==2.8.0"]
|
||||
}
|
||||
|
||||
@@ -159,12 +159,8 @@ class PortainerConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
errors["base"] = "unknown"
|
||||
else:
|
||||
# Logic that can be reverted back once the new unique ID is in
|
||||
existing_entry = await self.async_set_unique_id(
|
||||
user_input[CONF_API_TOKEN]
|
||||
)
|
||||
if existing_entry and existing_entry.entry_id != reconf_entry.entry_id:
|
||||
return self.async_abort(reason="already_configured")
|
||||
await self.async_set_unique_id(user_input[CONF_API_TOKEN])
|
||||
self._abort_if_unique_id_configured()
|
||||
return self.async_update_reload_and_abort(
|
||||
reconf_entry,
|
||||
data_updates={
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "local_polling",
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["pyportainer==1.0.33"]
|
||||
"requirements": ["pyportainer==1.0.31"]
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["ical"],
|
||||
"quality_scale": "silver",
|
||||
"requirements": ["ical==13.2.2"]
|
||||
"requirements": ["ical==13.2.0"]
|
||||
}
|
||||
|
||||
@@ -34,5 +34,5 @@
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["pysmartthings"],
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["pysmartthings==3.7.0"]
|
||||
"requirements": ["pysmartthings==3.6.0"]
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/thread",
|
||||
"integration_type": "service",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["python-otbr-api==2.9.0", "pyroute2==0.7.5"],
|
||||
"requirements": ["python-otbr-api==2.8.0", "pyroute2==0.7.5"],
|
||||
"single_config_entry": true,
|
||||
"zeroconf": ["_meshcop._udp.local."]
|
||||
}
|
||||
|
||||
@@ -81,7 +81,6 @@ clean_area:
|
||||
selector:
|
||||
area:
|
||||
multiple: true
|
||||
reorder: true
|
||||
|
||||
send_command:
|
||||
target:
|
||||
|
||||
@@ -398,7 +398,7 @@ SENSOR_DESCRIPTIONS = {
|
||||
Keys.WARNING: VictronBLESensorEntityDescription(
|
||||
key=Keys.WARNING,
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
translation_key="warning",
|
||||
translation_key="alarm",
|
||||
options=ALARM_OPTIONS,
|
||||
),
|
||||
Keys.YIELD_TODAY: VictronBLESensorEntityDescription(
|
||||
|
||||
@@ -248,24 +248,7 @@
|
||||
"name": "[%key:component::victron_ble::common::starter_voltage%]"
|
||||
},
|
||||
"warning": {
|
||||
"name": "Warning",
|
||||
"state": {
|
||||
"bms_lockout": "[%key:component::victron_ble::entity::sensor::alarm::state::bms_lockout%]",
|
||||
"dc_ripple": "[%key:component::victron_ble::entity::sensor::alarm::state::dc_ripple%]",
|
||||
"high_starter_voltage": "[%key:component::victron_ble::entity::sensor::alarm::state::high_starter_voltage%]",
|
||||
"high_temperature": "[%key:component::victron_ble::entity::sensor::alarm::state::high_temperature%]",
|
||||
"high_v_ac_out": "[%key:component::victron_ble::entity::sensor::alarm::state::high_v_ac_out%]",
|
||||
"high_voltage": "[%key:component::victron_ble::entity::sensor::alarm::state::high_voltage%]",
|
||||
"low_soc": "[%key:component::victron_ble::entity::sensor::alarm::state::low_soc%]",
|
||||
"low_starter_voltage": "[%key:component::victron_ble::entity::sensor::alarm::state::low_starter_voltage%]",
|
||||
"low_temperature": "[%key:component::victron_ble::entity::sensor::alarm::state::low_temperature%]",
|
||||
"low_v_ac_out": "[%key:component::victron_ble::entity::sensor::alarm::state::low_v_ac_out%]",
|
||||
"low_voltage": "[%key:component::victron_ble::entity::sensor::alarm::state::low_voltage%]",
|
||||
"mid_voltage": "[%key:component::victron_ble::common::midpoint_voltage%]",
|
||||
"no_alarm": "[%key:component::victron_ble::entity::sensor::alarm::state::no_alarm%]",
|
||||
"overload": "[%key:component::victron_ble::entity::sensor::alarm::state::overload%]",
|
||||
"short_circuit": "[%key:component::victron_ble::entity::sensor::alarm::state::short_circuit%]"
|
||||
}
|
||||
"name": "Warning"
|
||||
},
|
||||
"yield_today": {
|
||||
"name": "Yield today"
|
||||
|
||||
@@ -78,7 +78,6 @@ class VodafoneStationRouter(DataUpdateCoordinator[UpdateCoordinatorDataType]):
|
||||
data,
|
||||
session,
|
||||
)
|
||||
self._session = session
|
||||
|
||||
# Last resort as no MAC or S/N can be retrieved via API
|
||||
self._id = config_entry.unique_id
|
||||
@@ -136,15 +135,11 @@ class VodafoneStationRouter(DataUpdateCoordinator[UpdateCoordinatorDataType]):
|
||||
_LOGGER.debug("Polling Vodafone Station host: %s", self.api.base_url.host)
|
||||
|
||||
try:
|
||||
if not self._session.cookie_jar.filter_cookies(self.api.base_url):
|
||||
_LOGGER.debug(
|
||||
"Session cookies missing for host %s, re-login",
|
||||
self.api.base_url.host,
|
||||
)
|
||||
await self.api.login()
|
||||
await self.api.login()
|
||||
raw_data_devices = await self.api.get_devices_data()
|
||||
data_sensors = await self.api.get_sensor_data()
|
||||
data_wifi = await self.api.get_wifi_data()
|
||||
await self.api.logout()
|
||||
except exceptions.CannotAuthenticate as err:
|
||||
raise ConfigEntryAuthFailed(
|
||||
translation_domain=DOMAIN,
|
||||
|
||||
@@ -104,7 +104,6 @@ class VodafoneSwitchEntity(CoordinatorEntity[VodafoneStationRouter], SwitchEntit
|
||||
await self.coordinator.api.set_wifi_status(
|
||||
status, self.entity_description.typology, self.entity_description.band
|
||||
)
|
||||
await self.coordinator.async_request_refresh()
|
||||
except CannotAuthenticate as err:
|
||||
self.coordinator.config_entry.async_start_reauth(self.hass)
|
||||
raise HomeAssistantError(
|
||||
|
||||
@@ -5,7 +5,7 @@ from __future__ import annotations
|
||||
from pathlib import Path
|
||||
from typing import cast
|
||||
|
||||
from aiohttp import ClientError
|
||||
from aiohttp import ClientResponseError
|
||||
from yalexs.const import Brand
|
||||
from yalexs.exceptions import YaleApiError
|
||||
from yalexs.manager.const import CONF_BRAND
|
||||
@@ -15,12 +15,7 @@ from yalexs.manager.gateway import Config as YaleXSConfig
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import (
|
||||
ConfigEntryAuthFailed,
|
||||
ConfigEntryNotReady,
|
||||
OAuth2TokenRequestError,
|
||||
OAuth2TokenRequestReauthError,
|
||||
)
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.config_entry_oauth2_flow import (
|
||||
ImplementationUnavailableError,
|
||||
@@ -47,18 +42,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: YaleConfigEntry) -> bool
|
||||
yale_gateway = YaleGateway(Path(hass.config.config_dir), session, oauth_session)
|
||||
try:
|
||||
await async_setup_yale(hass, entry, yale_gateway)
|
||||
except OAuth2TokenRequestReauthError as err:
|
||||
raise ConfigEntryAuthFailed from err
|
||||
except (RequireValidation, InvalidAuth) as err:
|
||||
raise ConfigEntryAuthFailed from err
|
||||
except TimeoutError as err:
|
||||
raise ConfigEntryNotReady("Timed out connecting to yale api") from err
|
||||
except (
|
||||
YaleApiError,
|
||||
OAuth2TokenRequestError,
|
||||
ClientError,
|
||||
CannotConnect,
|
||||
) as err:
|
||||
except (YaleApiError, ClientResponseError, CannotConnect) as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
@@ -14,5 +14,5 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["socketio", "engineio", "yalexs"],
|
||||
"requirements": ["yalexs==9.2.0", "yalexs-ble==3.2.8"]
|
||||
"requirements": ["yalexs==9.2.0", "yalexs-ble==3.2.7"]
|
||||
}
|
||||
|
||||
@@ -13,5 +13,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/yalexs_ble",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["yalexs-ble==3.2.8"]
|
||||
"requirements": ["yalexs-ble==3.2.7"]
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"universal_silabs_flasher",
|
||||
"serialx"
|
||||
],
|
||||
"requirements": ["zha==1.0.2", "serialx==0.6.2"],
|
||||
"requirements": ["zha==1.0.1", "serialx==0.6.2"],
|
||||
"usb": [
|
||||
{
|
||||
"description": "*2652*",
|
||||
|
||||
@@ -87,9 +87,6 @@ class CoverPositionMixin(ZWaveBaseEntity, CoverEntity):
|
||||
_current_position_value: ZwaveValue | None = None
|
||||
_target_position_value: ZwaveValue | None = None
|
||||
_stop_position_value: ZwaveValue | None = None
|
||||
# Keep track of the target position for legacy devices
|
||||
# that don't include the targetValue in their reports.
|
||||
_commanded_target_position: int | None = None
|
||||
|
||||
def _set_position_values(
|
||||
self,
|
||||
@@ -156,19 +153,12 @@ class CoverPositionMixin(ZWaveBaseEntity, CoverEntity):
|
||||
if not self._attr_is_opening and not self._attr_is_closing:
|
||||
return
|
||||
|
||||
if (current := self._current_position_value) is None or current.value is None:
|
||||
return
|
||||
|
||||
# Prefer the Z-Wave targetValue property when the device reports it.
|
||||
# Legacy multilevel switches only report currentValue, so fall back to
|
||||
# the target position we commanded when targetValue is not available.
|
||||
target_val = (
|
||||
t.value
|
||||
if (t := self._target_position_value) is not None and t.value is not None
|
||||
else self._commanded_target_position
|
||||
)
|
||||
|
||||
if target_val is not None and current.value == target_val:
|
||||
if (
|
||||
(current := self._current_position_value) is not None
|
||||
and (target := self._target_position_value) is not None
|
||||
and current.value is not None
|
||||
and current.value == target.value
|
||||
):
|
||||
self._attr_is_opening = False
|
||||
self._attr_is_closing = False
|
||||
|
||||
@@ -213,8 +203,6 @@ class CoverPositionMixin(ZWaveBaseEntity, CoverEntity):
|
||||
else:
|
||||
return
|
||||
|
||||
self._commanded_target_position = target_position
|
||||
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_set_cover_position(self, **kwargs: Any) -> None:
|
||||
|
||||
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
||||
APPLICATION_NAME: Final = "HomeAssistant"
|
||||
MAJOR_VERSION: Final = 2026
|
||||
MINOR_VERSION: Final = 3
|
||||
PATCH_VERSION: Final = "2"
|
||||
PATCH_VERSION: Final = "1"
|
||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 14, 2)
|
||||
|
||||
@@ -181,24 +181,15 @@ class RestoreStateData:
|
||||
}
|
||||
|
||||
# Start with the currently registered states
|
||||
stored_states: list[StoredState] = []
|
||||
for entity_id, entity in self.entities.items():
|
||||
if entity_id not in current_states_by_entity_id:
|
||||
continue
|
||||
try:
|
||||
extra_data = entity.extra_restore_state_data
|
||||
except Exception:
|
||||
_LOGGER.exception(
|
||||
"Error getting extra restore state data for %s", entity_id
|
||||
)
|
||||
continue
|
||||
stored_states.append(
|
||||
StoredState(
|
||||
current_states_by_entity_id[entity_id],
|
||||
extra_data,
|
||||
now,
|
||||
)
|
||||
stored_states = [
|
||||
StoredState(
|
||||
current_states_by_entity_id[entity_id],
|
||||
entity.extra_restore_state_data,
|
||||
now,
|
||||
)
|
||||
for entity_id, entity in self.entities.items()
|
||||
if entity_id in current_states_by_entity_id
|
||||
]
|
||||
expiration_time = now - STATE_EXPIRATION
|
||||
|
||||
for entity_id, stored_state in self.last_states.items():
|
||||
@@ -228,8 +219,6 @@ class RestoreStateData:
|
||||
)
|
||||
except HomeAssistantError as exc:
|
||||
_LOGGER.error("Error saving current states", exc_info=exc)
|
||||
except Exception:
|
||||
_LOGGER.exception("Unexpected error saving current states")
|
||||
|
||||
@callback
|
||||
def async_setup_dump(self, *args: Any) -> None:
|
||||
@@ -269,15 +258,13 @@ class RestoreStateData:
|
||||
|
||||
@callback
|
||||
def async_restore_entity_removed(
|
||||
self,
|
||||
entity_id: str,
|
||||
state: State | None,
|
||||
extra_data: ExtraStoredData | None,
|
||||
self, entity_id: str, extra_data: ExtraStoredData | None
|
||||
) -> None:
|
||||
"""Unregister this entity from saving state."""
|
||||
# When an entity is being removed from hass, store its last state. This
|
||||
# allows us to support state restoration if the entity is removed, then
|
||||
# re-added while hass is still running.
|
||||
state = self.hass.states.get(entity_id)
|
||||
# To fully mimic all the attribute data types when loaded from storage,
|
||||
# we're going to serialize it to JSON and then re-load it.
|
||||
if state is not None:
|
||||
@@ -300,18 +287,8 @@ class RestoreEntity(Entity):
|
||||
|
||||
async def async_internal_will_remove_from_hass(self) -> None:
|
||||
"""Run when entity will be removed from hass."""
|
||||
try:
|
||||
extra_data = self.extra_restore_state_data
|
||||
except Exception:
|
||||
_LOGGER.exception(
|
||||
"Error getting extra restore state data for %s", self.entity_id
|
||||
)
|
||||
state = None
|
||||
extra_data = None
|
||||
else:
|
||||
state = self.hass.states.get(self.entity_id)
|
||||
async_get(self.hass).async_restore_entity_removed(
|
||||
self.entity_id, state, extra_data
|
||||
self.entity_id, self.extra_restore_state_data
|
||||
)
|
||||
await super().async_internal_will_remove_from_hass()
|
||||
|
||||
|
||||
@@ -301,7 +301,6 @@ class AreaSelectorConfig(BaseSelectorConfig, total=False):
|
||||
entity: EntityFilterSelectorConfig | list[EntityFilterSelectorConfig]
|
||||
device: DeviceFilterSelectorConfig | list[DeviceFilterSelectorConfig]
|
||||
multiple: bool
|
||||
reorder: bool
|
||||
|
||||
|
||||
@SELECTORS.register("area")
|
||||
@@ -321,7 +320,6 @@ class AreaSelector(Selector[AreaSelectorConfig]):
|
||||
[DEVICE_FILTER_SELECTOR_CONFIG_SCHEMA],
|
||||
),
|
||||
vol.Optional("multiple", default=False): cv.boolean,
|
||||
vol.Optional("reorder", default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ habluetooth==5.8.0
|
||||
hass-nabucasa==1.15.0
|
||||
hassil==3.5.0
|
||||
home-assistant-bluetooth==1.13.1
|
||||
home-assistant-frontend==20260312.0
|
||||
home-assistant-frontend==20260304.0
|
||||
home-assistant-intents==2026.3.3
|
||||
httpx==0.28.1
|
||||
ifaddr==0.2.0
|
||||
@@ -48,7 +48,7 @@ Jinja2==3.1.6
|
||||
lru-dict==1.3.0
|
||||
mutagen==1.47.0
|
||||
openai==2.21.0
|
||||
orjson==3.11.7
|
||||
orjson==3.11.5
|
||||
packaging>=23.1
|
||||
paho-mqtt==2.1.0
|
||||
Pillow==12.1.1
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "homeassistant"
|
||||
version = "2026.3.2"
|
||||
version = "2026.3.1"
|
||||
license = "Apache-2.0"
|
||||
license-files = ["LICENSE*", "homeassistant/backports/LICENSE*"]
|
||||
description = "Open-source home automation platform running on Python 3."
|
||||
@@ -65,7 +65,7 @@ dependencies = [
|
||||
"Pillow==12.1.1",
|
||||
"propcache==0.4.1",
|
||||
"pyOpenSSL==25.3.0",
|
||||
"orjson==3.11.7",
|
||||
"orjson==3.11.5",
|
||||
"packaging>=23.1",
|
||||
"psutil-home-assistant==0.0.1",
|
||||
"python-slugify==8.0.4",
|
||||
|
||||
2
requirements.txt
generated
2
requirements.txt
generated
@@ -34,7 +34,7 @@ ifaddr==0.2.0
|
||||
Jinja2==3.1.6
|
||||
lru-dict==1.3.0
|
||||
mutagen==1.47.0
|
||||
orjson==3.11.7
|
||||
orjson==3.11.5
|
||||
packaging>=23.1
|
||||
Pillow==12.1.1
|
||||
propcache==0.4.1
|
||||
|
||||
30
requirements_all.txt
generated
30
requirements_all.txt
generated
@@ -47,7 +47,7 @@ PlexAPI==4.15.16
|
||||
ProgettiHWSW==0.1.3
|
||||
|
||||
# homeassistant.components.cast
|
||||
PyChromecast==14.0.10
|
||||
PyChromecast==14.0.9
|
||||
|
||||
# homeassistant.components.flume
|
||||
PyFlume==0.6.5
|
||||
@@ -190,7 +190,7 @@ aioairzone-cloud==0.7.2
|
||||
aioairzone==1.0.5
|
||||
|
||||
# homeassistant.components.alexa_devices
|
||||
aioamazondevices==13.0.1
|
||||
aioamazondevices==13.0.0
|
||||
|
||||
# homeassistant.components.ambient_network
|
||||
# homeassistant.components.ambient_station
|
||||
@@ -224,7 +224,7 @@ aiobafi6==0.9.0
|
||||
aiobotocore==2.21.1
|
||||
|
||||
# homeassistant.components.comelit
|
||||
aiocomelit==2.0.1
|
||||
aiocomelit==2.0.0
|
||||
|
||||
# homeassistant.components.dhcp
|
||||
aiodhcpwatcher==1.2.1
|
||||
@@ -1122,7 +1122,7 @@ gotailwind==0.3.0
|
||||
govee-ble==0.44.0
|
||||
|
||||
# homeassistant.components.govee_light_local
|
||||
govee-local-api==2.4.0
|
||||
govee-local-api==2.3.0
|
||||
|
||||
# homeassistant.components.remote_rpi_gpio
|
||||
gpiozero==1.6.2
|
||||
@@ -1226,7 +1226,7 @@ hole==0.9.0
|
||||
holidays==0.84
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20260312.0
|
||||
home-assistant-frontend==20260304.0
|
||||
|
||||
# homeassistant.components.conversation
|
||||
home-assistant-intents==2026.3.3
|
||||
@@ -1271,7 +1271,7 @@ ibeacon-ble==1.2.0
|
||||
# homeassistant.components.local_calendar
|
||||
# homeassistant.components.local_todo
|
||||
# homeassistant.components.remote_calendar
|
||||
ical==13.2.2
|
||||
ical==13.2.0
|
||||
|
||||
# homeassistant.components.caldav
|
||||
icalendar==6.3.1
|
||||
@@ -1663,7 +1663,7 @@ odp-amsterdam==6.1.2
|
||||
oemthermostat==1.1.1
|
||||
|
||||
# homeassistant.components.ohme
|
||||
ohme==1.7.0
|
||||
ohme==1.6.0
|
||||
|
||||
# homeassistant.components.ollama
|
||||
ollama==0.5.1
|
||||
@@ -1676,7 +1676,7 @@ ondilo==0.5.0
|
||||
|
||||
# homeassistant.components.onedrive
|
||||
# homeassistant.components.onedrive_for_business
|
||||
onedrive-personal-sdk==0.1.7
|
||||
onedrive-personal-sdk==0.1.5
|
||||
|
||||
# homeassistant.components.onvif
|
||||
onvif-zeep-async==4.0.4
|
||||
@@ -1938,7 +1938,7 @@ pyairobotrest==0.3.0
|
||||
pyairvisual==2023.08.1
|
||||
|
||||
# homeassistant.components.anglian_water
|
||||
pyanglianwater==3.1.1
|
||||
pyanglianwater==3.1.0
|
||||
|
||||
# homeassistant.components.aprilaire
|
||||
pyaprilaire==0.9.1
|
||||
@@ -2179,7 +2179,7 @@ pyitachip2ir==0.0.7
|
||||
pyituran==0.1.5
|
||||
|
||||
# homeassistant.components.jvc_projector
|
||||
pyjvcprojector==2.0.3
|
||||
pyjvcprojector==2.0.1
|
||||
|
||||
# homeassistant.components.kaleidescape
|
||||
pykaleidescape==1.1.3
|
||||
@@ -2370,7 +2370,7 @@ pyplaato==0.0.19
|
||||
pypoint==3.0.0
|
||||
|
||||
# homeassistant.components.portainer
|
||||
pyportainer==1.0.33
|
||||
pyportainer==1.0.31
|
||||
|
||||
# homeassistant.components.probe_plus
|
||||
pyprobeplus==1.1.2
|
||||
@@ -2473,7 +2473,7 @@ pysmappee==0.2.29
|
||||
pysmarlaapi==1.0.1
|
||||
|
||||
# homeassistant.components.smartthings
|
||||
pysmartthings==3.7.0
|
||||
pysmartthings==3.6.0
|
||||
|
||||
# homeassistant.components.smarty
|
||||
pysmarty2==0.10.3
|
||||
@@ -2612,7 +2612,7 @@ python-opensky==1.0.1
|
||||
|
||||
# homeassistant.components.otbr
|
||||
# homeassistant.components.thread
|
||||
python-otbr-api==2.9.0
|
||||
python-otbr-api==2.8.0
|
||||
|
||||
# homeassistant.components.overseerr
|
||||
python-overseerr==0.9.0
|
||||
@@ -3307,7 +3307,7 @@ yalesmartalarmclient==0.4.3
|
||||
# homeassistant.components.august
|
||||
# homeassistant.components.yale
|
||||
# homeassistant.components.yalexs_ble
|
||||
yalexs-ble==3.2.8
|
||||
yalexs-ble==3.2.7
|
||||
|
||||
# homeassistant.components.august
|
||||
# homeassistant.components.yale
|
||||
@@ -3347,7 +3347,7 @@ zeroconf==0.148.0
|
||||
zeversolar==0.3.2
|
||||
|
||||
# homeassistant.components.zha
|
||||
zha==1.0.2
|
||||
zha==1.0.1
|
||||
|
||||
# homeassistant.components.zhong_hong
|
||||
zhong-hong-hvac==1.0.13
|
||||
|
||||
30
requirements_test_all.txt
generated
30
requirements_test_all.txt
generated
@@ -47,7 +47,7 @@ PlexAPI==4.15.16
|
||||
ProgettiHWSW==0.1.3
|
||||
|
||||
# homeassistant.components.cast
|
||||
PyChromecast==14.0.10
|
||||
PyChromecast==14.0.9
|
||||
|
||||
# homeassistant.components.flume
|
||||
PyFlume==0.6.5
|
||||
@@ -181,7 +181,7 @@ aioairzone-cloud==0.7.2
|
||||
aioairzone==1.0.5
|
||||
|
||||
# homeassistant.components.alexa_devices
|
||||
aioamazondevices==13.0.1
|
||||
aioamazondevices==13.0.0
|
||||
|
||||
# homeassistant.components.ambient_network
|
||||
# homeassistant.components.ambient_station
|
||||
@@ -215,7 +215,7 @@ aiobafi6==0.9.0
|
||||
aiobotocore==2.21.1
|
||||
|
||||
# homeassistant.components.comelit
|
||||
aiocomelit==2.0.1
|
||||
aiocomelit==2.0.0
|
||||
|
||||
# homeassistant.components.dhcp
|
||||
aiodhcpwatcher==1.2.1
|
||||
@@ -998,7 +998,7 @@ gotailwind==0.3.0
|
||||
govee-ble==0.44.0
|
||||
|
||||
# homeassistant.components.govee_light_local
|
||||
govee-local-api==2.4.0
|
||||
govee-local-api==2.3.0
|
||||
|
||||
# homeassistant.components.gpsd
|
||||
gps3==0.33.3
|
||||
@@ -1087,7 +1087,7 @@ hole==0.9.0
|
||||
holidays==0.84
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20260312.0
|
||||
home-assistant-frontend==20260304.0
|
||||
|
||||
# homeassistant.components.conversation
|
||||
home-assistant-intents==2026.3.3
|
||||
@@ -1126,7 +1126,7 @@ ibeacon-ble==1.2.0
|
||||
# homeassistant.components.local_calendar
|
||||
# homeassistant.components.local_todo
|
||||
# homeassistant.components.remote_calendar
|
||||
ical==13.2.2
|
||||
ical==13.2.0
|
||||
|
||||
# homeassistant.components.caldav
|
||||
icalendar==6.3.1
|
||||
@@ -1449,7 +1449,7 @@ objgraph==3.5.0
|
||||
odp-amsterdam==6.1.2
|
||||
|
||||
# homeassistant.components.ohme
|
||||
ohme==1.7.0
|
||||
ohme==1.6.0
|
||||
|
||||
# homeassistant.components.ollama
|
||||
ollama==0.5.1
|
||||
@@ -1462,7 +1462,7 @@ ondilo==0.5.0
|
||||
|
||||
# homeassistant.components.onedrive
|
||||
# homeassistant.components.onedrive_for_business
|
||||
onedrive-personal-sdk==0.1.7
|
||||
onedrive-personal-sdk==0.1.5
|
||||
|
||||
# homeassistant.components.onvif
|
||||
onvif-zeep-async==4.0.4
|
||||
@@ -1669,7 +1669,7 @@ pyairobotrest==0.3.0
|
||||
pyairvisual==2023.08.1
|
||||
|
||||
# homeassistant.components.anglian_water
|
||||
pyanglianwater==3.1.1
|
||||
pyanglianwater==3.1.0
|
||||
|
||||
# homeassistant.components.aprilaire
|
||||
pyaprilaire==0.9.1
|
||||
@@ -1856,7 +1856,7 @@ pyisy==3.4.1
|
||||
pyituran==0.1.5
|
||||
|
||||
# homeassistant.components.jvc_projector
|
||||
pyjvcprojector==2.0.3
|
||||
pyjvcprojector==2.0.1
|
||||
|
||||
# homeassistant.components.kaleidescape
|
||||
pykaleidescape==1.1.3
|
||||
@@ -2020,7 +2020,7 @@ pyplaato==0.0.19
|
||||
pypoint==3.0.0
|
||||
|
||||
# homeassistant.components.portainer
|
||||
pyportainer==1.0.33
|
||||
pyportainer==1.0.31
|
||||
|
||||
# homeassistant.components.probe_plus
|
||||
pyprobeplus==1.1.2
|
||||
@@ -2102,7 +2102,7 @@ pysmappee==0.2.29
|
||||
pysmarlaapi==1.0.1
|
||||
|
||||
# homeassistant.components.smartthings
|
||||
pysmartthings==3.7.0
|
||||
pysmartthings==3.6.0
|
||||
|
||||
# homeassistant.components.smarty
|
||||
pysmarty2==0.10.3
|
||||
@@ -2208,7 +2208,7 @@ python-opensky==1.0.1
|
||||
|
||||
# homeassistant.components.otbr
|
||||
# homeassistant.components.thread
|
||||
python-otbr-api==2.9.0
|
||||
python-otbr-api==2.8.0
|
||||
|
||||
# homeassistant.components.overseerr
|
||||
python-overseerr==0.9.0
|
||||
@@ -2783,7 +2783,7 @@ yalesmartalarmclient==0.4.3
|
||||
# homeassistant.components.august
|
||||
# homeassistant.components.yale
|
||||
# homeassistant.components.yalexs_ble
|
||||
yalexs-ble==3.2.8
|
||||
yalexs-ble==3.2.7
|
||||
|
||||
# homeassistant.components.august
|
||||
# homeassistant.components.yale
|
||||
@@ -2817,7 +2817,7 @@ zeroconf==0.148.0
|
||||
zeversolar==0.3.2
|
||||
|
||||
# homeassistant.components.zha
|
||||
zha==1.0.2
|
||||
zha==1.0.1
|
||||
|
||||
# homeassistant.components.zinvolt
|
||||
zinvolt==0.3.0
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from aioamazondevices.const.devices import SPEAKER_GROUP_FAMILY
|
||||
from aioamazondevices.const.devices import (
|
||||
SPEAKER_GROUP_DEVICE_TYPE,
|
||||
SPEAKER_GROUP_FAMILY,
|
||||
)
|
||||
from aioamazondevices.exceptions import CannotConnect, CannotRetrieveData
|
||||
import pytest
|
||||
|
||||
@@ -114,7 +117,7 @@ async def test_alexa_dnd_group_removal(
|
||||
identifiers={(DOMAIN, mock_config_entry.entry_id)},
|
||||
name=mock_config_entry.title,
|
||||
manufacturer="Amazon",
|
||||
model="Speaker Group",
|
||||
model=SPEAKER_GROUP_DEVICE_TYPE,
|
||||
entry_type=dr.DeviceEntryType.SERVICE,
|
||||
)
|
||||
|
||||
@@ -153,7 +156,7 @@ async def test_alexa_unsupported_notification_sensor_removal(
|
||||
identifiers={(DOMAIN, mock_config_entry.entry_id)},
|
||||
name=mock_config_entry.title,
|
||||
manufacturer="Amazon",
|
||||
model="Speaker Group",
|
||||
model=SPEAKER_GROUP_DEVICE_TYPE,
|
||||
entry_type=dr.DeviceEntryType.SERVICE,
|
||||
)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from aiohttp import ClientError, ClientResponseError
|
||||
from aiohttp import ClientResponseError
|
||||
import pytest
|
||||
from yalexs.const import Brand
|
||||
from yalexs.exceptions import AugustApiAIOHTTPError, InvalidAuth
|
||||
@@ -18,11 +18,7 @@ from homeassistant.const import (
|
||||
STATE_ON,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import (
|
||||
HomeAssistantError,
|
||||
OAuth2TokenRequestReauthError,
|
||||
OAuth2TokenRequestTransientError,
|
||||
)
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import (
|
||||
device_registry as dr,
|
||||
entity_registry as er,
|
||||
@@ -308,59 +304,3 @@ async def test_oauth_implementation_not_available(hass: HomeAssistant) -> None:
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||||
|
||||
|
||||
async def test_oauth_token_request_reauth_error(hass: HomeAssistant) -> None:
|
||||
"""Test OAuth token request reauth error starts a reauth flow."""
|
||||
entry = await mock_august_config_entry(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.config_entry_oauth2_flow.OAuth2Session.async_ensure_token_valid",
|
||||
side_effect=OAuth2TokenRequestReauthError(
|
||||
request_info=Mock(real_url="https://auth.august.com/access_token"),
|
||||
status=401,
|
||||
domain=DOMAIN,
|
||||
),
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.SETUP_ERROR
|
||||
flows = hass.config_entries.flow.async_progress()
|
||||
assert len(flows) == 1
|
||||
assert flows[0]["step_id"] == "pick_implementation"
|
||||
assert flows[0]["context"]["source"] == "reauth"
|
||||
|
||||
|
||||
async def test_oauth_token_request_transient_error_is_retryable(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test OAuth token transient request error marks entry for setup retry."""
|
||||
entry = await mock_august_config_entry(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.config_entry_oauth2_flow.OAuth2Session.async_ensure_token_valid",
|
||||
side_effect=OAuth2TokenRequestTransientError(
|
||||
request_info=Mock(real_url="https://auth.august.com/access_token"),
|
||||
status=500,
|
||||
domain=DOMAIN,
|
||||
),
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||||
|
||||
|
||||
async def test_oauth_client_error_is_retryable(hass: HomeAssistant) -> None:
|
||||
"""Test OAuth transport client errors mark entry for setup retry."""
|
||||
entry = await mock_august_config_entry(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.config_entry_oauth2_flow.OAuth2Session.async_ensure_token_valid",
|
||||
side_effect=ClientError("connection error"),
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||||
|
||||
@@ -396,11 +396,6 @@ async def test_switch_device_no_ip_address(
|
||||
"async_set_deflection_enable",
|
||||
STATE_ON,
|
||||
),
|
||||
(
|
||||
"switch.mock_title_wi_fi_mywifi",
|
||||
"async_set_wlan_configuration",
|
||||
STATE_ON,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_switch_turn_on_off(
|
||||
|
||||
@@ -427,7 +427,9 @@
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
@@ -464,6 +466,7 @@
|
||||
'attribution': 'Data provided by unpublished Intellifire API',
|
||||
'device_class': 'timestamp',
|
||||
'friendly_name': 'IntelliFire Timer end',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.intellifire_timer_end',
|
||||
|
||||
@@ -644,31 +644,6 @@ async def test_setting_device_tracker_location_via_abbr_reset_message(
|
||||
assert state.attributes["source_type"] == "gps"
|
||||
assert state.state == STATE_HOME
|
||||
|
||||
# Override the GPS state via a direct state update
|
||||
async_fire_mqtt_message(hass, "test-topic", "office")
|
||||
state = hass.states.get("device_tracker.test")
|
||||
assert state.state == "office"
|
||||
|
||||
# Test a GPS attributes update without a reset
|
||||
async_fire_mqtt_message(
|
||||
hass,
|
||||
"attributes-topic",
|
||||
'{"latitude":32.87336,"longitude": -117.22743, "gps_accuracy":1.5}',
|
||||
)
|
||||
|
||||
state = hass.states.get("device_tracker.test")
|
||||
assert state.state == "office"
|
||||
|
||||
# Reset the manual set location
|
||||
# This should calculate the location from GPS attributes
|
||||
async_fire_mqtt_message(hass, "test-topic", "reset")
|
||||
state = hass.states.get("device_tracker.test")
|
||||
assert state.attributes["latitude"] == 32.87336
|
||||
assert state.attributes["longitude"] == -117.22743
|
||||
assert state.attributes["gps_accuracy"] == 1.5
|
||||
assert state.attributes["source_type"] == "gps"
|
||||
assert state.state == STATE_HOME
|
||||
|
||||
|
||||
async def test_setting_blocked_attribute_via_mqtt_json_message(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# name: test_diagnostics
|
||||
dict({
|
||||
'cap_available': True,
|
||||
'ct_connected': True,
|
||||
'device_info': dict({
|
||||
'model': 'Home Pro',
|
||||
'name': 'Ohme Home Pro',
|
||||
|
||||
@@ -263,28 +263,20 @@ async def test_full_flow_reconfigure_unique_id(
|
||||
) -> None:
|
||||
"""Test the full flow of the config flow, this time with a known unique ID."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
other_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="Portainer other",
|
||||
data=USER_INPUT_RECONFIGURE,
|
||||
unique_id=USER_INPUT_RECONFIGURE[CONF_API_TOKEN],
|
||||
)
|
||||
other_entry.add_to_hass(hass)
|
||||
|
||||
result = await mock_config_entry.start_reconfigure_flow(hass)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reconfigure"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=USER_INPUT_RECONFIGURE,
|
||||
user_input=MOCK_USER_SETUP,
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
assert mock_config_entry.data[CONF_API_TOKEN] == "test_api_token"
|
||||
assert mock_config_entry.data[CONF_URL] == "https://127.0.0.1:9000/"
|
||||
assert mock_config_entry.data[CONF_VERIFY_SSL] is True
|
||||
assert len(mock_setup_entry.mock_calls) == 0
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from aiohttp import ClientError, ClientResponseError
|
||||
from aiohttp import ClientResponseError
|
||||
import pytest
|
||||
from yalexs.exceptions import InvalidAuth, YaleApiError
|
||||
|
||||
@@ -17,11 +17,7 @@ from homeassistant.const import (
|
||||
STATE_ON,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import (
|
||||
HomeAssistantError,
|
||||
OAuth2TokenRequestReauthError,
|
||||
OAuth2TokenRequestTransientError,
|
||||
)
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.helpers.config_entry_oauth2_flow import (
|
||||
ImplementationUnavailableError,
|
||||
@@ -258,58 +254,3 @@ async def test_oauth_implementation_not_available(hass: HomeAssistant) -> None:
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||||
|
||||
|
||||
async def test_oauth_token_request_reauth_error(hass: HomeAssistant) -> None:
|
||||
"""Test OAuth token request reauth error starts a reauth flow."""
|
||||
entry = await mock_yale_config_entry(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.config_entry_oauth2_flow.OAuth2Session.async_ensure_token_valid",
|
||||
side_effect=OAuth2TokenRequestReauthError(
|
||||
request_info=Mock(real_url="https://auth.yale.com/access_token"),
|
||||
status=401,
|
||||
domain=DOMAIN,
|
||||
),
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.SETUP_ERROR
|
||||
flows = hass.config_entries.flow.async_progress()
|
||||
assert len(flows) == 1
|
||||
assert flows[0]["context"]["source"] == "reauth"
|
||||
|
||||
|
||||
async def test_oauth_token_request_transient_error_is_retryable(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test OAuth token transient request error marks entry for setup retry."""
|
||||
entry = await mock_yale_config_entry(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.config_entry_oauth2_flow.OAuth2Session.async_ensure_token_valid",
|
||||
side_effect=OAuth2TokenRequestTransientError(
|
||||
request_info=Mock(real_url="https://auth.yale.com/access_token"),
|
||||
status=500,
|
||||
domain=DOMAIN,
|
||||
),
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||||
|
||||
|
||||
async def test_oauth_client_error_is_retryable(hass: HomeAssistant) -> None:
|
||||
"""Test OAuth transport client errors mark entry for setup retry."""
|
||||
entry = await mock_yale_config_entry(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.config_entry_oauth2_flow.OAuth2Session.async_ensure_token_valid",
|
||||
side_effect=ClientError("connection error"),
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||||
|
||||
@@ -1676,173 +1676,3 @@ async def test_multilevel_switch_cover_moving_state_none_result(
|
||||
|
||||
state = hass.states.get(WINDOW_COVER_ENTITY)
|
||||
assert state.state == CoverState.CLOSED
|
||||
|
||||
|
||||
async def test_multilevel_switch_cover_unsupervised_no_target_value_update(
|
||||
hass: HomeAssistant,
|
||||
client: MagicMock,
|
||||
chain_actuator_zws12: Node,
|
||||
integration: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test cover transitions to CLOSED/OPEN without targetValue updates.
|
||||
|
||||
Regression test for issue #164915: covers with no supervision where the
|
||||
device only sends currentValue updates (no targetValue updates) should
|
||||
still properly transition from CLOSING/OPENING to CLOSED/OPEN once the
|
||||
current position reaches the fully closed or fully open position.
|
||||
"""
|
||||
node = chain_actuator_zws12
|
||||
state = hass.states.get(WINDOW_COVER_ENTITY)
|
||||
assert state
|
||||
assert state.state == CoverState.CLOSED
|
||||
|
||||
# Set cover to fully open position via an unsolicited device report.
|
||||
# This mirrors the log sequence: currentValue 0 => 99
|
||||
node.receive_event(
|
||||
Event(
|
||||
type="value updated",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value updated",
|
||||
"nodeId": node.node_id,
|
||||
"args": {
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"commandClass": 38,
|
||||
"endpoint": 0,
|
||||
"property": "currentValue",
|
||||
"newValue": 99,
|
||||
"prevValue": 0,
|
||||
"propertyName": "currentValue",
|
||||
},
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
state = hass.states.get(WINDOW_COVER_ENTITY)
|
||||
assert state.state == CoverState.OPEN
|
||||
|
||||
# Simulate SUCCESS_UNSUPERVISED (no Supervision CC on device).
|
||||
client.async_send_command.return_value = {
|
||||
"result": {"status": SetValueStatus.SUCCESS_UNSUPERVISED}
|
||||
}
|
||||
|
||||
# Close cover – should optimistically enter CLOSING.
|
||||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
SERVICE_CLOSE_COVER,
|
||||
{ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
state = hass.states.get(WINDOW_COVER_ENTITY)
|
||||
assert state.state == CoverState.CLOSING
|
||||
|
||||
# Simulate intermediate report from device (currentValue only, no targetValue).
|
||||
# Log sequence: currentValue 99 => 78
|
||||
node.receive_event(
|
||||
Event(
|
||||
type="value updated",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value updated",
|
||||
"nodeId": node.node_id,
|
||||
"args": {
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"commandClass": 38,
|
||||
"endpoint": 0,
|
||||
"property": "currentValue",
|
||||
"newValue": 78,
|
||||
"prevValue": 99,
|
||||
"propertyName": "currentValue",
|
||||
},
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
state = hass.states.get(WINDOW_COVER_ENTITY)
|
||||
assert state.state == CoverState.CLOSING
|
||||
|
||||
# Simulate device reaching the fully closed position.
|
||||
# Log sequence: currentValue 78 => 0
|
||||
node.receive_event(
|
||||
Event(
|
||||
type="value updated",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value updated",
|
||||
"nodeId": node.node_id,
|
||||
"args": {
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"commandClass": 38,
|
||||
"endpoint": 0,
|
||||
"property": "currentValue",
|
||||
"newValue": 0,
|
||||
"prevValue": 78,
|
||||
"propertyName": "currentValue",
|
||||
},
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
# Cover must leave CLOSING and report CLOSED.
|
||||
state = hass.states.get(WINDOW_COVER_ENTITY)
|
||||
assert state.state == CoverState.CLOSED
|
||||
|
||||
# Now test the opening direction with the same conditions.
|
||||
# Log sequence: currentValue 0 => 18 => 99
|
||||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
SERVICE_OPEN_COVER,
|
||||
{ATTR_ENTITY_ID: WINDOW_COVER_ENTITY},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
state = hass.states.get(WINDOW_COVER_ENTITY)
|
||||
assert state.state == CoverState.OPENING
|
||||
|
||||
node.receive_event(
|
||||
Event(
|
||||
type="value updated",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value updated",
|
||||
"nodeId": node.node_id,
|
||||
"args": {
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"commandClass": 38,
|
||||
"endpoint": 0,
|
||||
"property": "currentValue",
|
||||
"newValue": 18,
|
||||
"prevValue": 0,
|
||||
"propertyName": "currentValue",
|
||||
},
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
state = hass.states.get(WINDOW_COVER_ENTITY)
|
||||
assert state.state == CoverState.OPENING
|
||||
|
||||
node.receive_event(
|
||||
Event(
|
||||
type="value updated",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value updated",
|
||||
"nodeId": node.node_id,
|
||||
"args": {
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"commandClass": 38,
|
||||
"endpoint": 0,
|
||||
"property": "currentValue",
|
||||
"newValue": 99,
|
||||
"prevValue": 18,
|
||||
"propertyName": "currentValue",
|
||||
},
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
# Cover must leave OPENING and report OPEN.
|
||||
state = hass.states.get(WINDOW_COVER_ENTITY)
|
||||
assert state.state == CoverState.OPEN
|
||||
|
||||
@@ -6,8 +6,6 @@ import logging
|
||||
from typing import Any
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import CoreState, HomeAssistant, State
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
@@ -18,7 +16,6 @@ from homeassistant.helpers.reload import async_get_platform_without_config_entry
|
||||
from homeassistant.helpers.restore_state import (
|
||||
DATA_RESTORE_STATE,
|
||||
STORAGE_KEY,
|
||||
ExtraStoredData,
|
||||
RestoreEntity,
|
||||
RestoreStateData,
|
||||
StoredState,
|
||||
@@ -345,12 +342,8 @@ async def test_dump_data(hass: HomeAssistant) -> None:
|
||||
assert state1["state"]["state"] == "off"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"exception",
|
||||
[HomeAssistantError, RuntimeError],
|
||||
)
|
||||
async def test_dump_error(hass: HomeAssistant, exception: type[Exception]) -> None:
|
||||
"""Test that errors during save are caught."""
|
||||
async def test_dump_error(hass: HomeAssistant) -> None:
|
||||
"""Test that we cache data."""
|
||||
states = [
|
||||
State("input_boolean.b0", "on"),
|
||||
State("input_boolean.b1", "on"),
|
||||
@@ -375,7 +368,7 @@ async def test_dump_error(hass: HomeAssistant, exception: type[Exception]) -> No
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.restore_state.Store.async_save",
|
||||
side_effect=exception,
|
||||
side_effect=HomeAssistantError,
|
||||
) as mock_write_data:
|
||||
await data.async_dump_states()
|
||||
|
||||
@@ -541,89 +534,3 @@ async def test_restore_entity_end_to_end(
|
||||
assert len(storage_data) == 1
|
||||
assert storage_data[0]["state"]["entity_id"] == entity_id
|
||||
assert storage_data[0]["state"]["state"] == "stored"
|
||||
|
||||
|
||||
async def test_dump_states_with_failing_extra_data(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test that a failing extra_restore_state_data skips only that entity."""
|
||||
|
||||
class BadRestoreEntity(RestoreEntity):
|
||||
"""Entity that raises on extra_restore_state_data."""
|
||||
|
||||
@property
|
||||
def extra_restore_state_data(self) -> ExtraStoredData | None:
|
||||
raise RuntimeError("Unexpected error")
|
||||
|
||||
states = [
|
||||
State("input_boolean.good", "on"),
|
||||
State("input_boolean.bad", "on"),
|
||||
]
|
||||
|
||||
platform = MockEntityPlatform(hass, domain="input_boolean")
|
||||
|
||||
good_entity = RestoreEntity()
|
||||
good_entity.hass = hass
|
||||
good_entity.entity_id = "input_boolean.good"
|
||||
await platform.async_add_entities([good_entity])
|
||||
|
||||
bad_entity = BadRestoreEntity()
|
||||
bad_entity.hass = hass
|
||||
bad_entity.entity_id = "input_boolean.bad"
|
||||
await platform.async_add_entities([bad_entity])
|
||||
|
||||
for state in states:
|
||||
hass.states.async_set(state.entity_id, state.state, state.attributes)
|
||||
|
||||
data = async_get(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.restore_state.Store.async_save"
|
||||
) as mock_write_data:
|
||||
await data.async_dump_states()
|
||||
|
||||
assert mock_write_data.called
|
||||
written_states = mock_write_data.mock_calls[0][1][0]
|
||||
|
||||
# Only the good entity should be saved
|
||||
assert len(written_states) == 1
|
||||
state0 = json_round_trip(written_states[0])
|
||||
assert state0["state"]["entity_id"] == "input_boolean.good"
|
||||
assert state0["state"]["state"] == "on"
|
||||
|
||||
assert "Error getting extra restore state data for input_boolean.bad" in caplog.text
|
||||
|
||||
|
||||
async def test_entity_removal_with_failing_extra_data(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test that entity removal succeeds even if extra_restore_state_data raises."""
|
||||
|
||||
class BadRestoreEntity(RestoreEntity):
|
||||
"""Entity that raises on extra_restore_state_data."""
|
||||
|
||||
@property
|
||||
def extra_restore_state_data(self) -> ExtraStoredData | None:
|
||||
raise RuntimeError("Unexpected error")
|
||||
|
||||
platform = MockEntityPlatform(hass, domain="input_boolean")
|
||||
entity = BadRestoreEntity()
|
||||
entity.hass = hass
|
||||
entity.entity_id = "input_boolean.bad"
|
||||
await platform.async_add_entities([entity])
|
||||
|
||||
hass.states.async_set("input_boolean.bad", "on")
|
||||
|
||||
data = async_get(hass)
|
||||
assert "input_boolean.bad" in data.entities
|
||||
|
||||
await entity.async_remove()
|
||||
|
||||
# Entity should be unregistered
|
||||
assert "input_boolean.bad" not in data.entities
|
||||
# No last state should be saved since extra data failed
|
||||
assert "input_boolean.bad" not in data.last_states
|
||||
|
||||
assert "Error getting extra restore state data for input_boolean.bad" in caplog.text
|
||||
|
||||
@@ -383,7 +383,7 @@ def test_entity_selector_schema_error(schema) -> None:
|
||||
(None,),
|
||||
),
|
||||
(
|
||||
{"multiple": True, "reorder": True},
|
||||
{"multiple": True},
|
||||
((["abc123", "def456"],)),
|
||||
(None, "abc123", ["abc123", None]),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user