diff --git a/homeassistant/components/icloud/account.py b/homeassistant/components/icloud/account.py index 3006193a1ff6f4..35e04d20ecd155 100644 --- a/homeassistant/components/icloud/account.py +++ b/homeassistant/components/icloud/account.py @@ -125,9 +125,8 @@ def setup(self) -> None: return try: - api_devices = self.api.devices # Gets device owners infos - user_info = api_devices.response["userInfo"] + user_info = self.api.devices.user_info except ( PyiCloudServiceNotActivatedException, PyiCloudNoDevicesException, diff --git a/homeassistant/components/icloud/manifest.json b/homeassistant/components/icloud/manifest.json index 339404ba55853a..60a4063a079fad 100644 --- a/homeassistant/components/icloud/manifest.json +++ b/homeassistant/components/icloud/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/icloud", "iot_class": "cloud_polling", "loggers": ["keyrings.alt", "pyicloud"], - "requirements": ["pyicloud==2.0.3"] + "requirements": ["pyicloud==2.1.0"] } diff --git a/homeassistant/components/knx/binary_sensor.py b/homeassistant/components/knx/binary_sensor.py index d6f8fa62915b8f..471034851f0ca8 100644 --- a/homeassistant/components/knx/binary_sensor.py +++ b/homeassistant/components/knx/binary_sensor.py @@ -84,11 +84,11 @@ class _KnxBinarySensor(BinarySensorEntity, RestoreEntity): async def async_added_to_hass(self) -> None: """Restore last state.""" - await super().async_added_to_hass() if ( last_state := await self.async_get_last_state() ) and last_state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE): self._device.remote_value.update_value(last_state.state == STATE_ON) + await super().async_added_to_hass() @property def is_on(self) -> bool: diff --git a/homeassistant/components/knx/sensor.py b/homeassistant/components/knx/sensor.py index ccc0d27306f04e..2a3732e7fcdbd7 100644 --- a/homeassistant/components/knx/sensor.py +++ b/homeassistant/components/knx/sensor.py @@ -6,15 +6,15 @@ from dataclasses import dataclass from datetime import datetime, timedelta from functools import partial -from typing import Any from xknx import XKNX from xknx.core.connection_state import XknxConnectionState, XknxConnectionType -from xknx.devices import Sensor as XknxSensor +from xknx.devices import Device as XknxDevice, Sensor as XknxSensor from homeassistant import config_entries from homeassistant.components.sensor import ( CONF_STATE_CLASS, + RestoreSensor, SensorDeviceClass, SensorEntity, SensorEntityDescription, @@ -25,6 +25,8 @@ CONF_ENTITY_CATEGORY, CONF_NAME, CONF_TYPE, + STATE_UNAVAILABLE, + STATE_UNKNOWN, EntityCategory, Platform, ) @@ -141,7 +143,7 @@ def _create_sensor(xknx: XKNX, config: ConfigType) -> XknxSensor: ) -class KNXSensor(KnxYamlEntity, SensorEntity): +class KNXSensor(KnxYamlEntity, RestoreSensor): """Representation of a KNX sensor.""" _device: XknxSensor @@ -164,20 +166,30 @@ def __init__(self, knx_module: KNXModule, config: ConfigType) -> None: self._attr_unique_id = str(self._device.sensor_value.group_address_state) self._attr_native_unit_of_measurement = self._device.unit_of_measurement() self._attr_state_class = config.get(CONF_STATE_CLASS) + self._attr_extra_state_attributes = {} - @property - def native_value(self) -> StateType: - """Return the state of the sensor.""" - return self._device.resolve_state() - - @property - def extra_state_attributes(self) -> dict[str, Any] | None: - """Return device specific state attributes.""" - attr: dict[str, Any] = {} + async def async_added_to_hass(self) -> None: + """Restore last state.""" + if ( + (last_state := await self.async_get_last_state()) + and last_state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE) + and ( + (last_sensor_data := await self.async_get_last_sensor_data()) + is not None + ) + ): + self._attr_native_value = last_sensor_data.native_value + self._attr_extra_state_attributes.update(last_state.attributes) + await super().async_added_to_hass() - if self._device.last_telegram is not None: - attr[ATTR_SOURCE] = str(self._device.last_telegram.source_address) - return attr + def after_update_callback(self, device: XknxDevice) -> None: + """Call after device was updated.""" + self._attr_native_value = self._device.resolve_state() + if telegram := self._device.last_telegram: + self._attr_extra_state_attributes[ATTR_SOURCE] = str( + telegram.source_address + ) + super().after_update_callback(device) class KNXSystemSensor(SensorEntity): diff --git a/homeassistant/components/miele/manifest.json b/homeassistant/components/miele/manifest.json index 2ed00c564d10af..e8a1d08f28226f 100644 --- a/homeassistant/components/miele/manifest.json +++ b/homeassistant/components/miele/manifest.json @@ -5,6 +5,7 @@ "config_flow": true, "dependencies": ["application_credentials"], "documentation": "https://www.home-assistant.io/integrations/miele", + "integration_type": "hub", "iot_class": "cloud_push", "loggers": ["pymiele"], "quality_scale": "platinum", diff --git a/homeassistant/components/snapcast/media_player.py b/homeassistant/components/snapcast/media_player.py index ccb9d4c4c46e42..e99be58c7cc439 100644 --- a/homeassistant/components/snapcast/media_player.py +++ b/homeassistant/components/snapcast/media_player.py @@ -65,7 +65,7 @@ def register_services() -> None: """Register snapcast services.""" platform = entity_platform.async_get_current_platform() - platform.async_register_entity_service(SERVICE_SNAPSHOT, None, "snapshot") + platform.async_register_entity_service(SERVICE_SNAPSHOT, None, "async_snapshot") platform.async_register_entity_service(SERVICE_RESTORE, None, "async_restore") platform.async_register_entity_service( SERVICE_JOIN, {vol.Required(ATTR_MASTER): cv.entity_id}, "async_join" @@ -250,7 +250,7 @@ async def async_set_volume_level(self, volume: float) -> None: await self._device.set_volume(round(volume * 100)) self.async_write_ha_state() - def snapshot(self) -> None: + async def async_snapshot(self) -> None: """Snapshot the group state.""" self._device.snapshot() @@ -428,12 +428,12 @@ async def async_set_volume_level(self, volume: float) -> None: await super().async_set_volume_level(volume) - def snapshot(self) -> None: + async def async_snapshot(self) -> None: """Snapshot the group state.""" # Groups are deprecated, create an issue when used self._async_create_group_deprecation_issue() - super().snapshot() + await super().async_snapshot() async def async_restore(self) -> None: """Restore the group state.""" diff --git a/homeassistant/components/voip/manifest.json b/homeassistant/components/voip/manifest.json index ee98506e72896f..8c4dd6c267c856 100644 --- a/homeassistant/components/voip/manifest.json +++ b/homeassistant/components/voip/manifest.json @@ -5,6 +5,7 @@ "config_flow": true, "dependencies": ["assist_pipeline", "assist_satellite", "intent", "network"], "documentation": "https://www.home-assistant.io/integrations/voip", + "integration_type": "hub", "iot_class": "local_push", "loggers": ["voip_utils"], "quality_scale": "internal", diff --git a/requirements_all.txt b/requirements_all.txt index b3a130c61545f7..b870b0a70c7e1b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2072,7 +2072,7 @@ pyhomeworks==1.1.2 pyialarm==2.2.0 # homeassistant.components.icloud -pyicloud==2.0.3 +pyicloud==2.1.0 # homeassistant.components.insteon pyinsteon==1.6.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9827d6a562579b..cb8f3e1c8a50d9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1735,7 +1735,7 @@ pyhomeworks==1.1.2 pyialarm==2.2.0 # homeassistant.components.icloud -pyicloud==2.0.3 +pyicloud==2.1.0 # homeassistant.components.insteon pyinsteon==1.6.3 diff --git a/tests/components/google_assistant_sdk/test_config_flow.py b/tests/components/google_assistant_sdk/test_config_flow.py index dbecabdb35f1db..aa0c53cef15cd0 100644 --- a/tests/components/google_assistant_sdk/test_config_flow.py +++ b/tests/components/google_assistant_sdk/test_config_flow.py @@ -30,7 +30,7 @@ async def test_full_flow( ) -> None: """Check full flow.""" result = await hass.config_entries.flow.async_init( - "google_assistant_sdk", context={"source": config_entries.SOURCE_USER} + DOMAIN, context={"source": config_entries.SOURCE_USER} ) state = config_entry_oauth2_flow._encode_jwt( hass, @@ -88,25 +88,14 @@ async def test_reauth( hass_client_no_auth: ClientSessionGenerator, aioclient_mock: AiohttpClientMocker, setup_credentials: None, + config_entry: MockConfigEntry, ) -> None: """Test the reauthentication case updates the existing config entry.""" - - config_entry = MockConfigEntry( - domain=DOMAIN, - data={ - "token": { - "access_token": "mock-access-token", - }, - }, - ) config_entry.add_to_hass(hass) - config_entry.async_start_reauth(hass) - await hass.async_block_till_done() + result = await config_entry.start_reauth_flow(hass) - flows = hass.config_entries.flow.async_progress() - assert len(flows) == 1 - result = flows[0] + assert result["type"] is FlowResultType.FORM assert result["step_id"] == "reauth_confirm" result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) @@ -219,20 +208,13 @@ async def test_single_instance_allowed( hass_client_no_auth: ClientSessionGenerator, aioclient_mock: AiohttpClientMocker, setup_credentials: None, + config_entry: MockConfigEntry, ) -> None: """Test case where config flow allows a single test.""" - config_entry = MockConfigEntry( - domain=DOMAIN, - data={ - "token": { - "access_token": "mock-access-token", - }, - }, - ) config_entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( - "google_assistant_sdk", context={"source": config_entries.SOURCE_USER} + DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result.get("type") is FlowResultType.ABORT diff --git a/tests/components/knx/test_binary_sensor.py b/tests/components/knx/test_binary_sensor.py index 34382f742c821d..6025713dbc49c7 100644 --- a/tests/components/knx/test_binary_sensor.py +++ b/tests/components/knx/test_binary_sensor.py @@ -284,8 +284,8 @@ async def test_binary_sensor_reset( assert state.state is STATE_OFF -async def test_binary_sensor_restore_and_respond(hass: HomeAssistant, knx) -> None: - """Test restoring KNX binary sensor state and respond to read.""" +async def test_binary_sensor_restore(hass: HomeAssistant, knx: KNXTestKit) -> None: + """Test restoring KNX binary sensor state.""" _ADDRESS = "2/2/2" fake_state = State("binary_sensor.test", STATE_ON) mock_restore_cache(hass, (fake_state,)) @@ -312,7 +312,9 @@ async def test_binary_sensor_restore_and_respond(hass: HomeAssistant, knx) -> No assert state.state is STATE_OFF -async def test_binary_sensor_restore_invert(hass: HomeAssistant, knx) -> None: +async def test_binary_sensor_restore_invert( + hass: HomeAssistant, knx: KNXTestKit +) -> None: """Test restoring KNX binary sensor state with invert.""" _ADDRESS = "2/2/2" fake_state = State("binary_sensor.test", STATE_ON) diff --git a/tests/components/knx/test_sensor.py b/tests/components/knx/test_sensor.py index e5a1747962917b..b1168570337c87 100644 --- a/tests/components/knx/test_sensor.py +++ b/tests/components/knx/test_sensor.py @@ -2,14 +2,22 @@ from freezegun.api import FrozenDateTimeFactory -from homeassistant.components.knx.const import CONF_STATE_ADDRESS, CONF_SYNC_STATE +from homeassistant.components.knx.const import ( + ATTR_SOURCE, + CONF_STATE_ADDRESS, + CONF_SYNC_STATE, +) from homeassistant.components.knx.schema import SensorSchema from homeassistant.const import CONF_NAME, CONF_TYPE, STATE_UNKNOWN -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, State from .conftest import KNXTestKit -from tests.common import async_capture_events, async_fire_time_changed +from tests.common import ( + async_capture_events, + async_fire_time_changed, + mock_restore_cache_with_extra_data, +) async def test_sensor(hass: HomeAssistant, knx: KNXTestKit) -> None: @@ -43,6 +51,41 @@ async def test_sensor(hass: HomeAssistant, knx: KNXTestKit) -> None: await knx.assert_no_telegram() +async def test_sensor_restore(hass: HomeAssistant, knx: KNXTestKit) -> None: + """Test restoring KNX sensor state.""" + ADDRESS = "2/2/2" + RAW_FLOAT_21_0 = (0x0C, 0x1A) + RESTORED_STATE = "21.0" + RESTORED_STATE_ATTRIBUTES = {ATTR_SOURCE: knx.INDIVIDUAL_ADDRESS} + fake_state = State( + "sensor.test", "ignored in favour of native_value", RESTORED_STATE_ATTRIBUTES + ) + extra_data = {"native_value": RESTORED_STATE, "native_unit_of_measurement": "°C"} + mock_restore_cache_with_extra_data(hass, [(fake_state, extra_data)]) + + await knx.setup_integration( + { + SensorSchema.PLATFORM: [ + { + CONF_NAME: "test", + CONF_STATE_ADDRESS: ADDRESS, + CONF_TYPE: "temperature", # 2 byte float + CONF_SYNC_STATE: False, + }, + ] + } + ) + + # restored state - no read-response due to sync_state False + knx.assert_state("sensor.test", RESTORED_STATE, **RESTORED_STATE_ATTRIBUTES) + await knx.assert_telegram_count(0) + + # receiving the restored value from restored source does not trigger state_changed event + events = async_capture_events(hass, "state_changed") + await knx.receive_write(ADDRESS, RAW_FLOAT_21_0) + assert not events + + async def test_last_reported( hass: HomeAssistant, knx: KNXTestKit, diff --git a/tests/components/knx/test_switch.py b/tests/components/knx/test_switch.py index 969c11b8e1a804..581388658ac955 100644 --- a/tests/components/knx/test_switch.py +++ b/tests/components/knx/test_switch.py @@ -111,7 +111,7 @@ async def test_switch_state(hass: HomeAssistant, knx: KNXTestKit) -> None: await knx.assert_telegram_count(0) -async def test_switch_restore_and_respond(hass: HomeAssistant, knx) -> None: +async def test_switch_restore_and_respond(hass: HomeAssistant, knx: KNXTestKit) -> None: """Test restoring KNX switch state and respond to read.""" _ADDRESS = "1/1/1" fake_state = State("switch.test", "on")