From 5ddf21e4daf7e6678e8dca36f5c8d220ec10b0bd Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 3 May 2024 00:04:58 +0200 Subject: [PATCH 1/9] Cleanup MQTT sensor last_reset_topic config parameter a year after removal (#116657) --- homeassistant/components/mqtt/abbreviations.py | 1 - homeassistant/components/mqtt/sensor.py | 7 ------- 2 files changed, 8 deletions(-) diff --git a/homeassistant/components/mqtt/abbreviations.py b/homeassistant/components/mqtt/abbreviations.py index 5fadf6ba590..c3efe5667ad 100644 --- a/homeassistant/components/mqtt/abbreviations.py +++ b/homeassistant/components/mqtt/abbreviations.py @@ -86,7 +86,6 @@ ABBREVIATIONS = { "json_attr": "json_attributes", "json_attr_t": "json_attributes_topic", "json_attr_tpl": "json_attributes_template", - "lrst_t": "last_reset_topic", "lrst_val_tpl": "last_reset_value_template", "max": "max", "min": "min", diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index 9ba6308e07c..5457011d122 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -58,7 +58,6 @@ from .models import ( _LOGGER = logging.getLogger(__name__) CONF_EXPIRE_AFTER = "expire_after" -CONF_LAST_RESET_TOPIC = "last_reset_topic" CONF_LAST_RESET_VALUE_TEMPLATE = "last_reset_value_template" CONF_SUGGESTED_DISPLAY_PRECISION = "suggested_display_precision" @@ -101,17 +100,11 @@ def validate_sensor_state_class_config(config: ConfigType) -> ConfigType: PLATFORM_SCHEMA_MODERN = vol.All( - # Deprecated in HA Core 2021.11.0 https://github.com/home-assistant/core/pull/54840 - # Removed in HA Core 2023.6.0 - cv.removed(CONF_LAST_RESET_TOPIC), _PLATFORM_SCHEMA_BASE, validate_sensor_state_class_config, ) DISCOVERY_SCHEMA = vol.All( - # Deprecated in HA Core 2021.11.0 https://github.com/home-assistant/core/pull/54840 - # Removed in HA Core 2023.6.0 - cv.removed(CONF_LAST_RESET_TOPIC), _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), validate_sensor_state_class_config, ) From 43a0c880eb315bb228839d866ab0068d032131e9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 2 May 2024 18:15:56 -0500 Subject: [PATCH 2/9] Bump habluetooth to 2.8.1 (#116661) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 4bb84ab6dc3..754e8faf996 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -20,6 +20,6 @@ "bluetooth-auto-recovery==1.4.2", "bluetooth-data-tools==1.19.0", "dbus-fast==2.21.1", - "habluetooth==2.8.0" + "habluetooth==2.8.1" ] } diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 21bd4fb2d1b..b743897e871 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -28,7 +28,7 @@ dbus-fast==2.21.1 fnv-hash-fast==0.5.0 ha-av==10.1.1 ha-ffmpeg==3.2.0 -habluetooth==2.8.0 +habluetooth==2.8.1 hass-nabucasa==0.78.0 hassil==1.6.1 home-assistant-bluetooth==1.12.0 diff --git a/requirements_all.txt b/requirements_all.txt index 92ad8e51fe9..f8405eb962c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1035,7 +1035,7 @@ ha-philipsjs==3.1.1 habitipy==0.2.0 # homeassistant.components.bluetooth -habluetooth==2.8.0 +habluetooth==2.8.1 # homeassistant.components.cloud hass-nabucasa==0.78.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0676ec622ce..70c492e2075 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -849,7 +849,7 @@ ha-philipsjs==3.1.1 habitipy==0.2.0 # homeassistant.components.bluetooth -habluetooth==2.8.0 +habluetooth==2.8.1 # homeassistant.components.cloud hass-nabucasa==0.78.0 From a3791fde09379d75bc9922c68fbe6ec26f337408 Mon Sep 17 00:00:00 2001 From: Glenn Waters Date: Thu, 2 May 2024 19:17:41 -0400 Subject: [PATCH 3/9] Bump env_canada lib to 0.6.2 (#116662) --- homeassistant/components/environment_canada/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/environment_canada/manifest.json b/homeassistant/components/environment_canada/manifest.json index d0c34b0cf9a..f29c8177dfd 100644 --- a/homeassistant/components/environment_canada/manifest.json +++ b/homeassistant/components/environment_canada/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/environment_canada", "iot_class": "cloud_polling", "loggers": ["env_canada"], - "requirements": ["env-canada==0.6.0"] + "requirements": ["env-canada==0.6.2"] } diff --git a/requirements_all.txt b/requirements_all.txt index f8405eb962c..a9b31fa2f03 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -804,7 +804,7 @@ enocean==0.50 enturclient==0.2.4 # homeassistant.components.environment_canada -env-canada==0.6.0 +env-canada==0.6.2 # homeassistant.components.season ephem==4.1.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 70c492e2075..938c0705bf9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -658,7 +658,7 @@ energyzero==2.1.0 enocean==0.50 # homeassistant.components.environment_canada -env-canada==0.6.0 +env-canada==0.6.2 # homeassistant.components.season ephem==4.1.5 From 0e23d0439b25ab18a44a08f5473bb530354c4217 Mon Sep 17 00:00:00 2001 From: Marc-Olivier Arsenault Date: Thu, 2 May 2024 20:08:25 -0400 Subject: [PATCH 4/9] Add ecobee ventilator 20 min timer (#115969) * add 20 min timer Ecobee * modify local value with estimated time * add ecobee test switch * removed manual setting of data * Add no throttle updates * add more test cases * move timezone calculation in update function * update attribute based on feedback * use timezone for time comparaison * add location data to tests * remove is_on function * update python-ecobee-api lib * remove uncessary checks --------- Co-authored-by: Martin Hjelmare --- homeassistant/components/ecobee/const.py | 1 + homeassistant/components/ecobee/manifest.json | 2 +- homeassistant/components/ecobee/switch.py | 90 ++++++++++++++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/ecobee/__init__.py | 6 +- .../ecobee/fixtures/ecobee-data.json | 6 + tests/components/ecobee/test_switch.py | 115 ++++++++++++++++++ 8 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 homeassistant/components/ecobee/switch.py create mode 100644 tests/components/ecobee/test_switch.py diff --git a/homeassistant/components/ecobee/const.py b/homeassistant/components/ecobee/const.py index 0eed0ab67f9..8adc7f9638b 100644 --- a/homeassistant/components/ecobee/const.py +++ b/homeassistant/components/ecobee/const.py @@ -49,6 +49,7 @@ PLATFORMS = [ Platform.NOTIFY, Platform.NUMBER, Platform.SENSOR, + Platform.SWITCH, Platform.WEATHER, ] diff --git a/homeassistant/components/ecobee/manifest.json b/homeassistant/components/ecobee/manifest.json index 7e461230600..b11bdf8afb0 100644 --- a/homeassistant/components/ecobee/manifest.json +++ b/homeassistant/components/ecobee/manifest.json @@ -10,7 +10,7 @@ }, "iot_class": "cloud_polling", "loggers": ["pyecobee"], - "requirements": ["python-ecobee-api==0.2.17"], + "requirements": ["python-ecobee-api==0.2.18"], "zeroconf": [ { "type": "_ecobee._tcp.local." diff --git a/homeassistant/components/ecobee/switch.py b/homeassistant/components/ecobee/switch.py new file mode 100644 index 00000000000..44528a5f421 --- /dev/null +++ b/homeassistant/components/ecobee/switch.py @@ -0,0 +1,90 @@ +"""Support for using switch with ecobee thermostats.""" + +from __future__ import annotations + +import logging +from typing import Any + +from homeassistant.components.switch import SwitchEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.util import dt as dt_util + +from . import EcobeeData +from .const import DOMAIN +from .entity import EcobeeBaseEntity + +_LOGGER = logging.getLogger(__name__) + +DATE_FORMAT = "%Y-%m-%d %H:%M:%S" + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the ecobee thermostat switch entity.""" + data: EcobeeData = hass.data[DOMAIN] + + async_add_entities( + ( + EcobeeVentilator20MinSwitch(data, index) + for index, thermostat in enumerate(data.ecobee.thermostats) + if thermostat["settings"]["ventilatorType"] != "none" + ), + True, + ) + + +class EcobeeVentilator20MinSwitch(EcobeeBaseEntity, SwitchEntity): + """A Switch class, representing 20 min timer for an ecobee thermostat with ventilator attached.""" + + _attr_has_entity_name = True + _attr_name = "Ventilator 20m Timer" + + def __init__( + self, + data: EcobeeData, + thermostat_index: int, + ) -> None: + """Initialize ecobee ventilator platform.""" + super().__init__(data, thermostat_index) + self._attr_unique_id = f"{self.base_unique_id}_ventilator_20m_timer" + self._attr_is_on = False + self.update_without_throttle = False + self._operating_timezone = dt_util.get_time_zone( + self.thermostat["location"]["timeZone"] + ) + + async def async_update(self) -> None: + """Get the latest state from the thermostat.""" + + if self.update_without_throttle: + await self.data.update(no_throttle=True) + self.update_without_throttle = False + else: + await self.data.update() + + ventilator_off_date_time = self.thermostat["settings"]["ventilatorOffDateTime"] + + self._attr_is_on = ventilator_off_date_time and dt_util.parse_datetime( + ventilator_off_date_time, raise_on_error=True + ).replace(tzinfo=self._operating_timezone) >= dt_util.now( + self._operating_timezone + ) + + async def async_turn_on(self, **kwargs: Any) -> None: + """Set ventilator 20 min timer on.""" + await self.hass.async_add_executor_job( + self.data.ecobee.set_ventilator_timer, self.thermostat_index, True + ) + self.update_without_throttle = True + + async def async_turn_off(self, **kwargs: Any) -> None: + """Set ventilator 20 min timer off.""" + await self.hass.async_add_executor_job( + self.data.ecobee.set_ventilator_timer, self.thermostat_index, False + ) + self.update_without_throttle = True diff --git a/requirements_all.txt b/requirements_all.txt index a9b31fa2f03..84e14c33e12 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2215,7 +2215,7 @@ python-clementine-remote==1.0.1 python-digitalocean==1.13.2 # homeassistant.components.ecobee -python-ecobee-api==0.2.17 +python-ecobee-api==0.2.18 # homeassistant.components.etherscan python-etherscan-api==0.0.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 938c0705bf9..0352fb0dac1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1730,7 +1730,7 @@ python-awair==0.2.4 python-bsblan==0.5.18 # homeassistant.components.ecobee -python-ecobee-api==0.2.17 +python-ecobee-api==0.2.18 # homeassistant.components.fully_kiosk python-fullykiosk==0.0.12 diff --git a/tests/components/ecobee/__init__.py b/tests/components/ecobee/__init__.py index 52c6fcc6a4e..f89729df9bb 100644 --- a/tests/components/ecobee/__init__.py +++ b/tests/components/ecobee/__init__.py @@ -65,6 +65,9 @@ GENERIC_THERMOSTAT_INFO_WITH_HEATPUMP = { "identifier": 8675309, "name": "ecobee", "modelNumber": "athenaSmart", + "utcTime": "2022-01-01 10:00:00", + "thermostatTime": "2022-01-01 6:00:00", + "location": {"timeZone": "America/Toronto"}, "program": { "climates": [ {"name": "Climate1", "climateRef": "c1"}, @@ -92,7 +95,8 @@ GENERIC_THERMOSTAT_INFO_WITH_HEATPUMP = { "humidifierMode": "manual", "humidity": "30", "hasHeatPump": True, - "ventilatorType": "none", + "ventilatorType": "hrv", + "ventilatorOffDateTime": "2022-01-01 6:00:00", }, "equipmentStatus": "fan", "events": [ diff --git a/tests/components/ecobee/fixtures/ecobee-data.json b/tests/components/ecobee/fixtures/ecobee-data.json index d8621bd8c4b..c86782d9c0b 100644 --- a/tests/components/ecobee/fixtures/ecobee-data.json +++ b/tests/components/ecobee/fixtures/ecobee-data.json @@ -4,6 +4,11 @@ "identifier": 8675309, "name": "ecobee", "modelNumber": "athenaSmart", + "utcTime": "2022-01-01 10:00:00", + "thermostatTime": "2022-01-01 6:00:00", + "location": { + "timeZone": "America/Toronto" + }, "program": { "climates": [ { "name": "Climate1", "climateRef": "c1" }, @@ -30,6 +35,7 @@ "ventilatorType": "hrv", "ventilatorMinOnTimeHome": 20, "ventilatorMinOnTimeAway": 10, + "ventilatorOffDateTime": "2022-01-01 6:00:00", "isVentilatorTimerOn": false, "hasHumidifier": true, "humidifierMode": "manual", diff --git a/tests/components/ecobee/test_switch.py b/tests/components/ecobee/test_switch.py new file mode 100644 index 00000000000..383abf9644c --- /dev/null +++ b/tests/components/ecobee/test_switch.py @@ -0,0 +1,115 @@ +"""The test for the ecobee thermostat switch module.""" + +import copy +from datetime import datetime, timedelta +from unittest import mock +from unittest.mock import patch + +import pytest + +from homeassistant.components.ecobee.switch import DATE_FORMAT +from homeassistant.components.switch import DOMAIN, SERVICE_TURN_OFF, SERVICE_TURN_ON +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.core import HomeAssistant + +from .common import setup_platform + +from tests.components.ecobee import GENERIC_THERMOSTAT_INFO_WITH_HEATPUMP + +VENTILATOR_20MIN_ID = "switch.ecobee_ventilator_20m_timer" +THERMOSTAT_ID = 0 + + +@pytest.fixture(name="data") +def data_fixture(): + """Set up data mock.""" + data = mock.Mock() + data.return_value = copy.deepcopy(GENERIC_THERMOSTAT_INFO_WITH_HEATPUMP) + return data + + +async def test_ventilator_20min_attributes(hass: HomeAssistant) -> None: + """Test the ventilator switch on home attributes are correct.""" + await setup_platform(hass, DOMAIN) + + state = hass.states.get(VENTILATOR_20MIN_ID) + assert state.state == "off" + + +async def test_ventilator_20min_when_on(hass: HomeAssistant, data) -> None: + """Test the ventilator switch goes on.""" + + data.return_value["settings"]["ventilatorOffDateTime"] = ( + datetime.now() + timedelta(days=1) + ).strftime(DATE_FORMAT) + with mock.patch("pyecobee.Ecobee.get_thermostat", data): + await setup_platform(hass, DOMAIN) + + state = hass.states.get(VENTILATOR_20MIN_ID) + assert state.state == "on" + + data.reset_mock() + + +async def test_ventilator_20min_when_off(hass: HomeAssistant, data) -> None: + """Test the ventilator switch goes on.""" + + data.return_value["settings"]["ventilatorOffDateTime"] = ( + datetime.now() - timedelta(days=1) + ).strftime(DATE_FORMAT) + with mock.patch("pyecobee.Ecobee.get_thermostat", data): + await setup_platform(hass, DOMAIN) + + state = hass.states.get(VENTILATOR_20MIN_ID) + assert state.state == "off" + + data.reset_mock() + + +async def test_ventilator_20min_when_empty(hass: HomeAssistant, data) -> None: + """Test the ventilator switch goes on.""" + + data.return_value["settings"]["ventilatorOffDateTime"] = "" + with mock.patch("pyecobee.Ecobee.get_thermostat", data): + await setup_platform(hass, DOMAIN) + + state = hass.states.get(VENTILATOR_20MIN_ID) + assert state.state == "off" + + data.reset_mock() + + +async def test_turn_on_20min_ventilator(hass: HomeAssistant) -> None: + """Test the switch 20 min timer (On).""" + + with patch( + "homeassistant.components.ecobee.Ecobee.set_ventilator_timer" + ) as mock_set_20min_ventilator: + await setup_platform(hass, DOMAIN) + + await hass.services.async_call( + DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: VENTILATOR_20MIN_ID}, + blocking=True, + ) + await hass.async_block_till_done() + mock_set_20min_ventilator.assert_called_once_with(THERMOSTAT_ID, True) + + +async def test_turn_off_20min_ventilator(hass: HomeAssistant) -> None: + """Test the switch 20 min timer (off).""" + + with patch( + "homeassistant.components.ecobee.Ecobee.set_ventilator_timer" + ) as mock_set_20min_ventilator: + await setup_platform(hass, DOMAIN) + + await hass.services.async_call( + DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: VENTILATOR_20MIN_ID}, + blocking=True, + ) + await hass.async_block_till_done() + mock_set_20min_ventilator.assert_called_once_with(THERMOSTAT_ID, False) From 007b15dc8b62a07dbe3508eedc60c6140eb7e5c4 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Thu, 2 May 2024 20:31:28 -0400 Subject: [PATCH 5/9] Bump ZHA dependency bellows to 0.38.4 (#116660) Bump ZHA dependencies Co-authored-by: TheJulianJES --- homeassistant/components/zha/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index b1511b2f5bb..7a407a2eb33 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -21,7 +21,7 @@ "universal_silabs_flasher" ], "requirements": [ - "bellows==0.38.3", + "bellows==0.38.4", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.115", diff --git a/requirements_all.txt b/requirements_all.txt index 84e14c33e12..4025cffa9ae 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -541,7 +541,7 @@ beautifulsoup4==4.12.3 # beewi-smartclim==0.0.10 # homeassistant.components.zha -bellows==0.38.3 +bellows==0.38.4 # homeassistant.components.bmw_connected_drive bimmer-connected[china]==0.15.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0352fb0dac1..bc80158d3fa 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -466,7 +466,7 @@ base36==0.1.1 beautifulsoup4==4.12.3 # homeassistant.components.zha -bellows==0.38.3 +bellows==0.38.4 # homeassistant.components.bmw_connected_drive bimmer-connected[china]==0.15.2 From c07f02534b26a2b6a6a21ccde4b4684d37d570d1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 2 May 2024 19:35:16 -0500 Subject: [PATCH 6/9] Migrate bluetooth to use the singleton helper (#116629) --- .../components/bluetooth/__init__.py | 8 +++---- homeassistant/components/bluetooth/api.py | 13 ++++------ .../components/bluetooth/config_flow.py | 4 ++-- homeassistant/components/bluetooth/models.py | 8 ------- tests/components/bluetooth/test_init.py | 24 ++++++++++++------- 5 files changed, 25 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index acc38cad58b..49fadd1892e 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -51,8 +51,9 @@ from homeassistant.helpers.event import async_call_later from homeassistant.helpers.issue_registry import async_delete_issue from homeassistant.loader import async_get_bluetooth -from . import models, passive_update_processor +from . import passive_update_processor from .api import ( + _get_manager, async_address_present, async_ble_device_from_address, async_discovered_service_info, @@ -76,7 +77,6 @@ from .const import ( CONF_ADAPTER, CONF_DETAILS, CONF_PASSIVE, - DATA_MANAGER, DOMAIN, FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS, @@ -230,10 +230,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: hass, integration_matcher, bluetooth_adapters, bluetooth_storage, slot_manager ) set_manager(manager) - await storage_setup_task await manager.async_setup() - hass.data[DATA_MANAGER] = models.MANAGER = manager hass.async_create_background_task( _async_start_adapter_discovery(hass, manager, bluetooth_adapters), @@ -314,7 +312,7 @@ async def async_update_device( async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up a config entry for a bluetooth scanner.""" - manager: HomeAssistantBluetoothManager = hass.data[DATA_MANAGER] + manager = _get_manager(hass) address = entry.unique_id assert address is not None adapter = await manager.async_get_adapter_from_address_or_recover(address) diff --git a/homeassistant/components/bluetooth/api.py b/homeassistant/components/bluetooth/api.py index b1a6bc87728..505651edafd 100644 --- a/homeassistant/components/bluetooth/api.py +++ b/homeassistant/components/bluetooth/api.py @@ -15,10 +15,12 @@ from habluetooth import ( BluetoothScannerDevice, BluetoothScanningMode, HaBleakScannerWrapper, + get_manager, ) from home_assistant_bluetooth import BluetoothServiceInfoBleak from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback +from homeassistant.helpers.singleton import singleton from .const import DATA_MANAGER from .manager import HomeAssistantBluetoothManager @@ -29,9 +31,10 @@ if TYPE_CHECKING: from bleak.backends.device import BLEDevice +@singleton(DATA_MANAGER) def _get_manager(hass: HomeAssistant) -> HomeAssistantBluetoothManager: """Get the bluetooth manager.""" - return cast(HomeAssistantBluetoothManager, hass.data[DATA_MANAGER]) + return cast(HomeAssistantBluetoothManager, get_manager()) @hass_callback @@ -68,8 +71,6 @@ def async_discovered_service_info( hass: HomeAssistant, connectable: bool = True ) -> Iterable[BluetoothServiceInfoBleak]: """Return the discovered devices list.""" - if DATA_MANAGER not in hass.data: - return [] return _get_manager(hass).async_discovered_service_info(connectable) @@ -78,8 +79,6 @@ def async_last_service_info( hass: HomeAssistant, address: str, connectable: bool = True ) -> BluetoothServiceInfoBleak | None: """Return the last service info for an address.""" - if DATA_MANAGER not in hass.data: - return None return _get_manager(hass).async_last_service_info(address, connectable) @@ -88,8 +87,6 @@ def async_ble_device_from_address( hass: HomeAssistant, address: str, connectable: bool = True ) -> BLEDevice | None: """Return BLEDevice for an address if its present.""" - if DATA_MANAGER not in hass.data: - return None return _get_manager(hass).async_ble_device_from_address(address, connectable) @@ -106,8 +103,6 @@ def async_address_present( hass: HomeAssistant, address: str, connectable: bool = True ) -> bool: """Check if an address is present in the bluetooth device list.""" - if DATA_MANAGER not in hass.data: - return False return _get_manager(hass).async_address_present(address, connectable) diff --git a/homeassistant/components/bluetooth/config_flow.py b/homeassistant/components/bluetooth/config_flow.py index 90d2624fb0f..37eefd2f265 100644 --- a/homeassistant/components/bluetooth/config_flow.py +++ b/homeassistant/components/bluetooth/config_flow.py @@ -14,6 +14,7 @@ from bluetooth_adapters import ( adapter_model, get_adapters, ) +from habluetooth import get_manager import voluptuous as vol from homeassistant.components import onboarding @@ -25,7 +26,6 @@ from homeassistant.helpers.schema_config_entry_flow import ( ) from homeassistant.helpers.typing import DiscoveryInfoType -from . import models from .const import CONF_ADAPTER, CONF_DETAILS, CONF_PASSIVE, DOMAIN from .util import adapter_title @@ -185,4 +185,4 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN): @callback def async_supports_options_flow(cls, config_entry: ConfigEntry) -> bool: """Return options flow support for this handler.""" - return bool(models.MANAGER and models.MANAGER.supports_passive_scan) + return bool((manager := get_manager()) and manager.supports_passive_scan) diff --git a/homeassistant/components/bluetooth/models.py b/homeassistant/components/bluetooth/models.py index a14aaf1d379..a97056e1f4b 100644 --- a/homeassistant/components/bluetooth/models.py +++ b/homeassistant/components/bluetooth/models.py @@ -4,17 +4,9 @@ from __future__ import annotations from collections.abc import Callable from enum import Enum -from typing import TYPE_CHECKING from home_assistant_bluetooth import BluetoothServiceInfoBleak -if TYPE_CHECKING: - from .manager import HomeAssistantBluetoothManager - - -MANAGER: HomeAssistantBluetoothManager | None = None - - BluetoothChange = Enum("BluetoothChange", "ADVERTISEMENT") BluetoothCallback = Callable[[BluetoothServiceInfoBleak, BluetoothChange], None] ProcessAdvertisementCallback = Callable[[BluetoothServiceInfoBleak], bool] diff --git a/tests/components/bluetooth/test_init.py b/tests/components/bluetooth/test_init.py index 8c26745d541..ebc50779c9c 100644 --- a/tests/components/bluetooth/test_init.py +++ b/tests/components/bluetooth/test_init.py @@ -8,7 +8,7 @@ from unittest.mock import ANY, AsyncMock, MagicMock, Mock, patch from bleak import BleakError from bleak.backends.scanner import AdvertisementData, BLEDevice from bluetooth_adapters import DEFAULT_ADDRESS -from habluetooth import scanner +from habluetooth import scanner, set_manager from habluetooth.wrappers import HaBleakScannerWrapper import pytest @@ -1154,6 +1154,7 @@ async def test_async_discovered_device_api( ) -> None: """Test the async_discovered_device API.""" mock_bt = [] + set_manager(None) with ( patch( "homeassistant.components.bluetooth.async_get_bluetooth", @@ -1169,8 +1170,10 @@ async def test_async_discovered_device_api( }, ), ): - assert not bluetooth.async_discovered_service_info(hass) - assert not bluetooth.async_address_present(hass, "44:44:22:22:11:22") + with pytest.raises(RuntimeError, match="BluetoothManager has not been set"): + assert not bluetooth.async_discovered_service_info(hass) + with pytest.raises(RuntimeError, match="BluetoothManager has not been set"): + assert not bluetooth.async_address_present(hass, "44:44:22:22:11:22") await async_setup_with_default_adapter(hass) with patch.object(hass.config_entries.flow, "async_init"): @@ -2744,6 +2747,7 @@ async def test_async_ble_device_from_address( hass: HomeAssistant, mock_bleak_scanner_start: MagicMock, macos_adapter: None ) -> None: """Test the async_ble_device_from_address api.""" + set_manager(None) mock_bt = [] with ( patch( @@ -2760,11 +2764,15 @@ async def test_async_ble_device_from_address( }, ), ): - assert not bluetooth.async_discovered_service_info(hass) - assert not bluetooth.async_address_present(hass, "44:44:22:22:11:22") - assert ( - bluetooth.async_ble_device_from_address(hass, "44:44:33:11:23:45") is None - ) + with pytest.raises(RuntimeError, match="BluetoothManager has not been set"): + assert not bluetooth.async_discovered_service_info(hass) + with pytest.raises(RuntimeError, match="BluetoothManager has not been set"): + assert not bluetooth.async_address_present(hass, "44:44:22:22:11:22") + with pytest.raises(RuntimeError, match="BluetoothManager has not been set"): + assert ( + bluetooth.async_ble_device_from_address(hass, "44:44:33:11:23:45") + is None + ) await async_setup_with_default_adapter(hass) From 897794f53b7ecf08fe7b82020228f3201ac5e7d1 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Thu, 2 May 2024 17:38:12 -0700 Subject: [PATCH 7/9] Clean up small changes from OpenAI conversation entity change (#116395) Small cleanups in OpenAI conversation --- homeassistant/components/openai_conversation/__init__.py | 2 -- homeassistant/components/openai_conversation/const.py | 2 +- homeassistant/components/openai_conversation/conversation.py | 2 ++ 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/openai_conversation/__init__.py b/homeassistant/components/openai_conversation/__init__.py index ffbfc1799c5..2a91f1b1b38 100644 --- a/homeassistant/components/openai_conversation/__init__.py +++ b/homeassistant/components/openai_conversation/__init__.py @@ -5,7 +5,6 @@ from __future__ import annotations import openai import voluptuous as vol -from homeassistant.components import conversation from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_API_KEY, Platform from homeassistant.core import ( @@ -115,5 +114,4 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return False hass.data[DOMAIN].pop(entry.entry_id) - conversation.async_unset_agent(hass, entry) return True diff --git a/homeassistant/components/openai_conversation/const.py b/homeassistant/components/openai_conversation/const.py index ee4a107c241..f992849f9b1 100644 --- a/homeassistant/components/openai_conversation/const.py +++ b/homeassistant/components/openai_conversation/const.py @@ -3,7 +3,7 @@ import logging DOMAIN = "openai_conversation" -LOGGER = logging.getLogger(__name__) +LOGGER = logging.getLogger(__package__) CONF_PROMPT = "prompt" DEFAULT_PROMPT = """This smart home is controlled by Home Assistant. diff --git a/homeassistant/components/openai_conversation/conversation.py b/homeassistant/components/openai_conversation/conversation.py index 158b155c75d..3c94d66ee4a 100644 --- a/homeassistant/components/openai_conversation/conversation.py +++ b/homeassistant/components/openai_conversation/conversation.py @@ -44,6 +44,8 @@ class OpenAIConversationEntity( ): """OpenAI conversation agent.""" + _attr_has_entity_name = True + def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None: """Initialize the agent.""" self.hass = hass From 27fcf722757531c35340a2b0eca9f89181781255 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 3 May 2024 08:14:46 +0200 Subject: [PATCH 8/9] Convert history tests to use async API (#116447) * Convert history tests to use async API * Add new fixture to help patch recorder * Modify * Modify * Update tests/conftest.py Co-authored-by: Martin Hjelmare * Rename fixture --------- Co-authored-by: Martin Hjelmare --- tests/components/history/conftest.py | 21 ++- tests/components/history/test_init.py | 143 +++++++++-------- .../history/test_init_db_schema_30.py | 148 ++++++++++-------- .../components/history/test_websocket_api.py | 46 +++--- .../history/test_websocket_api_schema_32.py | 2 +- tests/components/recorder/test_init.py | 2 +- tests/conftest.py | 10 ++ 7 files changed, 209 insertions(+), 163 deletions(-) diff --git a/tests/components/history/conftest.py b/tests/components/history/conftest.py index 0ce6a190f55..075909dfd63 100644 --- a/tests/components/history/conftest.py +++ b/tests/components/history/conftest.py @@ -3,15 +3,24 @@ import pytest from homeassistant.components import history +from homeassistant.components.recorder import Recorder from homeassistant.const import CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, CONF_INCLUDE -from homeassistant.setup import setup_component +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from tests.typing import RecorderInstanceGenerator @pytest.fixture -def hass_history(hass_recorder): - """Home Assistant fixture with history.""" - hass = hass_recorder() +async def mock_recorder_before_hass( + async_setup_recorder_instance: RecorderInstanceGenerator, +) -> None: + """Set up recorder.""" + +@pytest.fixture +async def hass_history(hass: HomeAssistant, recorder_mock: Recorder) -> None: + """Home Assistant fixture with history.""" config = history.CONFIG_SCHEMA( { history.DOMAIN: { @@ -26,6 +35,4 @@ def hass_history(hass_recorder): } } ) - assert setup_component(hass, history.DOMAIN, config) - - return hass + assert await async_setup_component(hass, history.DOMAIN, config) diff --git a/tests/components/history/test_init.py b/tests/components/history/test_init.py index d0712b968bc..7806b7c9ef4 100644 --- a/tests/components/history/test_init.py +++ b/tests/components/history/test_init.py @@ -24,7 +24,6 @@ from tests.components.recorder.common import ( assert_multiple_states_equal_without_context_and_last_changed, assert_states_equal_without_context, async_wait_recording_done, - wait_recording_done, ) from tests.typing import ClientSessionGenerator @@ -39,25 +38,26 @@ def listeners_without_writes(listeners: dict[str, int]) -> dict[str, int]: @pytest.mark.usefixtures("hass_history") -def test_setup() -> None: +async def test_setup() -> None: """Test setup method of history.""" # Verification occurs in the fixture -def test_get_significant_states(hass_history) -> None: +async def test_get_significant_states(hass: HomeAssistant, hass_history) -> None: """Test that only significant states are returned. We should get back every thermostat change that includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ - hass = hass_history - zero, four, states = record_states(hass) + zero, four, states = await async_record_states(hass) hist = get_significant_states(hass, zero, four, entity_ids=list(states)) assert_dict_of_states_equal_without_context_and_last_changed(states, hist) -def test_get_significant_states_minimal_response(hass_history) -> None: +async def test_get_significant_states_minimal_response( + hass: HomeAssistant, hass_history +) -> None: """Test that only significant states are returned. When minimal responses is set only the first and @@ -67,8 +67,7 @@ def test_get_significant_states_minimal_response(hass_history) -> None: includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ - hass = hass_history - zero, four, states = record_states(hass) + zero, four, states = await async_record_states(hass) hist = get_significant_states( hass, zero, four, minimal_response=True, entity_ids=list(states) ) @@ -122,15 +121,16 @@ def test_get_significant_states_minimal_response(hass_history) -> None: ) -def test_get_significant_states_with_initial(hass_history) -> None: +async def test_get_significant_states_with_initial( + hass: HomeAssistant, hass_history +) -> None: """Test that only significant states are returned. We should get back every thermostat change that includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ - hass = hass_history - zero, four, states = record_states(hass) + zero, four, states = await async_record_states(hass) one_and_half = zero + timedelta(seconds=1.5) for entity_id in states: if entity_id == "media_player.test": @@ -149,15 +149,16 @@ def test_get_significant_states_with_initial(hass_history) -> None: assert_dict_of_states_equal_without_context_and_last_changed(states, hist) -def test_get_significant_states_without_initial(hass_history) -> None: +async def test_get_significant_states_without_initial( + hass: HomeAssistant, hass_history +) -> None: """Test that only significant states are returned. We should get back every thermostat change that includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ - hass = hass_history - zero, four, states = record_states(hass) + zero, four, states = await async_record_states(hass) one = zero + timedelta(seconds=1) one_with_microsecond = zero + timedelta(seconds=1, microseconds=1) one_and_half = zero + timedelta(seconds=1.5) @@ -179,10 +180,11 @@ def test_get_significant_states_without_initial(hass_history) -> None: assert_dict_of_states_equal_without_context_and_last_changed(states, hist) -def test_get_significant_states_entity_id(hass_history) -> None: +async def test_get_significant_states_entity_id( + hass: HomeAssistant, hass_history +) -> None: """Test that only significant states are returned for one entity.""" - hass = hass_history - zero, four, states = record_states(hass) + zero, four, states = await async_record_states(hass) del states["media_player.test2"] del states["media_player.test3"] del states["thermostat.test"] @@ -193,10 +195,11 @@ def test_get_significant_states_entity_id(hass_history) -> None: assert_dict_of_states_equal_without_context_and_last_changed(states, hist) -def test_get_significant_states_multiple_entity_ids(hass_history) -> None: +async def test_get_significant_states_multiple_entity_ids( + hass: HomeAssistant, hass_history +) -> None: """Test that only significant states are returned for one entity.""" - hass = hass_history - zero, four, states = record_states(hass) + zero, four, states = await async_record_states(hass) del states["media_player.test2"] del states["media_player.test3"] del states["thermostat.test2"] @@ -211,14 +214,15 @@ def test_get_significant_states_multiple_entity_ids(hass_history) -> None: assert_dict_of_states_equal_without_context_and_last_changed(states, hist) -def test_get_significant_states_are_ordered(hass_history) -> None: +async def test_get_significant_states_are_ordered( + hass: HomeAssistant, hass_history +) -> None: """Test order of results from get_significant_states. When entity ids are given, the results should be returned with the data in the same order. """ - hass = hass_history - zero, four, _states = record_states(hass) + zero, four, _states = await async_record_states(hass) entity_ids = ["media_player.test", "media_player.test2"] hist = get_significant_states(hass, zero, four, entity_ids) assert list(hist.keys()) == entity_ids @@ -227,15 +231,14 @@ def test_get_significant_states_are_ordered(hass_history) -> None: assert list(hist.keys()) == entity_ids -def test_get_significant_states_only(hass_history) -> None: +async def test_get_significant_states_only(hass: HomeAssistant, hass_history) -> None: """Test significant states when significant_states_only is set.""" - hass = hass_history entity_id = "sensor.test" - def set_state(state, **kwargs): + async def set_state(state, **kwargs): """Set the state.""" - hass.states.set(entity_id, state, **kwargs) - wait_recording_done(hass) + hass.states.async_set(entity_id, state, **kwargs) + await async_wait_recording_done(hass) return hass.states.get(entity_id) start = dt_util.utcnow() - timedelta(minutes=4) @@ -243,19 +246,19 @@ def test_get_significant_states_only(hass_history) -> None: states = [] with freeze_time(start) as freezer: - set_state("123", attributes={"attribute": 10.64}) + await set_state("123", attributes={"attribute": 10.64}) freezer.move_to(points[0]) # Attributes are different, state not - states.append(set_state("123", attributes={"attribute": 21.42})) + states.append(await set_state("123", attributes={"attribute": 21.42})) freezer.move_to(points[1]) # state is different, attributes not - states.append(set_state("32", attributes={"attribute": 21.42})) + states.append(await set_state("32", attributes={"attribute": 21.42})) freezer.move_to(points[2]) # everything is different - states.append(set_state("412", attributes={"attribute": 54.23})) + states.append(await set_state("412", attributes={"attribute": 54.23})) hist = get_significant_states( hass, @@ -288,13 +291,13 @@ def test_get_significant_states_only(hass_history) -> None: ) -def check_significant_states(hass, zero, four, states, config): +async def check_significant_states(hass, zero, four, states, config): """Check if significant states are retrieved.""" hist = get_significant_states(hass, zero, four) assert_dict_of_states_equal_without_context_and_last_changed(states, hist) -def record_states(hass): +async def async_record_states(hass): """Record some test states. We inject a bunch of state updates from media player, zone and @@ -308,10 +311,10 @@ def record_states(hass): zone = "zone.home" script_c = "script.can_cancel_this_one" - def set_state(entity_id, state, **kwargs): + async def set_state(entity_id, state, **kwargs): """Set the state.""" - hass.states.set(entity_id, state, **kwargs) - wait_recording_done(hass) + hass.states.async_set(entity_id, state, **kwargs) + await async_wait_recording_done(hass) return hass.states.get(entity_id) zero = dt_util.utcnow() @@ -323,55 +326,63 @@ def record_states(hass): states = {therm: [], therm2: [], mp: [], mp2: [], mp3: [], script_c: []} with freeze_time(one) as freezer: states[mp].append( - set_state(mp, "idle", attributes={"media_title": str(sentinel.mt1)}) + await set_state(mp, "idle", attributes={"media_title": str(sentinel.mt1)}) ) states[mp2].append( - set_state(mp2, "YouTube", attributes={"media_title": str(sentinel.mt2)}) + await set_state( + mp2, "YouTube", attributes={"media_title": str(sentinel.mt2)} + ) ) states[mp3].append( - set_state(mp3, "idle", attributes={"media_title": str(sentinel.mt1)}) + await set_state(mp3, "idle", attributes={"media_title": str(sentinel.mt1)}) ) states[therm].append( - set_state(therm, 20, attributes={"current_temperature": 19.5}) + await set_state(therm, 20, attributes={"current_temperature": 19.5}) ) freezer.move_to(one + timedelta(microseconds=1)) states[mp].append( - set_state(mp, "YouTube", attributes={"media_title": str(sentinel.mt2)}) + await set_state( + mp, "YouTube", attributes={"media_title": str(sentinel.mt2)} + ) ) freezer.move_to(two) # This state will be skipped only different in time - set_state(mp, "YouTube", attributes={"media_title": str(sentinel.mt3)}) + await set_state(mp, "YouTube", attributes={"media_title": str(sentinel.mt3)}) # This state will be skipped because domain is excluded - set_state(zone, "zoning") + await set_state(zone, "zoning") states[script_c].append( - set_state(script_c, "off", attributes={"can_cancel": True}) + await set_state(script_c, "off", attributes={"can_cancel": True}) ) states[therm].append( - set_state(therm, 21, attributes={"current_temperature": 19.8}) + await set_state(therm, 21, attributes={"current_temperature": 19.8}) ) states[therm2].append( - set_state(therm2, 20, attributes={"current_temperature": 19}) + await set_state(therm2, 20, attributes={"current_temperature": 19}) ) freezer.move_to(three) states[mp].append( - set_state(mp, "Netflix", attributes={"media_title": str(sentinel.mt4)}) + await set_state( + mp, "Netflix", attributes={"media_title": str(sentinel.mt4)} + ) ) states[mp3].append( - set_state(mp3, "Netflix", attributes={"media_title": str(sentinel.mt3)}) + await set_state( + mp3, "Netflix", attributes={"media_title": str(sentinel.mt3)} + ) ) # Attributes changed even though state is the same states[therm].append( - set_state(therm, 21, attributes={"current_temperature": 20}) + await set_state(therm, 21, attributes={"current_temperature": 20}) ) return zero, four, states async def test_fetch_period_api( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test the fetch period view for history.""" await async_setup_component(hass, "history", {}) @@ -383,8 +394,8 @@ async def test_fetch_period_api( async def test_fetch_period_api_with_use_include_order( - recorder_mock: Recorder, hass: HomeAssistant, + recorder_mock: Recorder, hass_client: ClientSessionGenerator, caplog: pytest.LogCaptureFixture, ) -> None: @@ -402,7 +413,7 @@ async def test_fetch_period_api_with_use_include_order( async def test_fetch_period_api_with_minimal_response( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test the fetch period view for history with minimal_response.""" now = dt_util.utcnow() @@ -444,7 +455,7 @@ async def test_fetch_period_api_with_minimal_response( async def test_fetch_period_api_with_no_timestamp( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test the fetch period view for history with no timestamp.""" await async_setup_component(hass, "history", {}) @@ -454,8 +465,8 @@ async def test_fetch_period_api_with_no_timestamp( async def test_fetch_period_api_with_include_order( - recorder_mock: Recorder, hass: HomeAssistant, + recorder_mock: Recorder, hass_client: ClientSessionGenerator, caplog: pytest.LogCaptureFixture, ) -> None: @@ -482,7 +493,7 @@ async def test_fetch_period_api_with_include_order( async def test_entity_ids_limit_via_api( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test limiting history to entity_ids.""" await async_setup_component( @@ -508,7 +519,7 @@ async def test_entity_ids_limit_via_api( async def test_entity_ids_limit_via_api_with_skip_initial_state( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test limiting history to entity_ids with skip_initial_state.""" await async_setup_component( @@ -542,7 +553,7 @@ async def test_entity_ids_limit_via_api_with_skip_initial_state( async def test_fetch_period_api_before_history_started( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test the fetch period view for history for the far past.""" await async_setup_component( @@ -563,7 +574,7 @@ async def test_fetch_period_api_before_history_started( async def test_fetch_period_api_far_future( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test the fetch period view for history for the far future.""" await async_setup_component( @@ -584,7 +595,7 @@ async def test_fetch_period_api_far_future( async def test_fetch_period_api_with_invalid_datetime( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test the fetch period view for history with an invalid date time.""" await async_setup_component( @@ -603,7 +614,7 @@ async def test_fetch_period_api_with_invalid_datetime( async def test_fetch_period_api_invalid_end_time( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test the fetch period view for history with an invalid end time.""" await async_setup_component( @@ -625,7 +636,7 @@ async def test_fetch_period_api_invalid_end_time( async def test_entity_ids_limit_via_api_with_end_time( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test limiting history to entity_ids with end_time.""" await async_setup_component( @@ -671,7 +682,7 @@ async def test_entity_ids_limit_via_api_with_end_time( async def test_fetch_period_api_with_no_entity_ids( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test the fetch period view for history with minimal_response.""" await async_setup_component(hass, "history", {}) @@ -724,13 +735,13 @@ async def test_fetch_period_api_with_no_entity_ids( ], ) async def test_history_with_invalid_entity_ids( + hass: HomeAssistant, + recorder_mock: Recorder, + hass_client: ClientSessionGenerator, filter_entity_id, status_code, response_contains1, response_contains2, - recorder_mock: Recorder, - hass: HomeAssistant, - hass_client: ClientSessionGenerator, ) -> None: """Test sending valid and invalid entity_ids to the API.""" await async_setup_component( diff --git a/tests/components/history/test_init_db_schema_30.py b/tests/components/history/test_init_db_schema_30.py index 2e26256da90..1b867cea584 100644 --- a/tests/components/history/test_init_db_schema_30.py +++ b/tests/components/history/test_init_db_schema_30.py @@ -27,7 +27,6 @@ from tests.components.recorder.common import ( async_recorder_block_till_done, async_wait_recording_done, old_db_schema, - wait_recording_done, ) from tests.typing import ClientSessionGenerator, WebSocketGenerator @@ -40,33 +39,34 @@ def db_schema_30(): @pytest.fixture -def legacy_hass_history(hass_history): +def legacy_hass_history(hass: HomeAssistant, hass_history): """Home Assistant fixture to use legacy history recording.""" - instance = recorder.get_instance(hass_history) + instance = recorder.get_instance(hass) with patch.object(instance.states_meta_manager, "active", False): - yield hass_history + yield @pytest.mark.usefixtures("legacy_hass_history") -def test_setup() -> None: +async def test_setup() -> None: """Test setup method of history.""" # Verification occurs in the fixture -def test_get_significant_states(legacy_hass_history) -> None: +async def test_get_significant_states(hass: HomeAssistant, legacy_hass_history) -> None: """Test that only significant states are returned. We should get back every thermostat change that includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ - hass = legacy_hass_history - zero, four, states = record_states(hass) + zero, four, states = await async_record_states(hass) hist = get_significant_states(hass, zero, four, entity_ids=list(states)) assert_dict_of_states_equal_without_context_and_last_changed(states, hist) -def test_get_significant_states_minimal_response(legacy_hass_history) -> None: +async def test_get_significant_states_minimal_response( + hass: HomeAssistant, legacy_hass_history +) -> None: """Test that only significant states are returned. When minimal responses is set only the first and @@ -76,8 +76,7 @@ def test_get_significant_states_minimal_response(legacy_hass_history) -> None: includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ - hass = legacy_hass_history - zero, four, states = record_states(hass) + zero, four, states = await async_record_states(hass) hist = get_significant_states( hass, zero, four, minimal_response=True, entity_ids=list(states) ) @@ -132,15 +131,16 @@ def test_get_significant_states_minimal_response(legacy_hass_history) -> None: ) -def test_get_significant_states_with_initial(legacy_hass_history) -> None: +async def test_get_significant_states_with_initial( + hass: HomeAssistant, legacy_hass_history +) -> None: """Test that only significant states are returned. We should get back every thermostat change that includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ - hass = legacy_hass_history - zero, four, states = record_states(hass) + zero, four, states = await async_record_states(hass) one = zero + timedelta(seconds=1) one_with_microsecond = zero + timedelta(seconds=1, microseconds=1) one_and_half = zero + timedelta(seconds=1.5) @@ -162,15 +162,16 @@ def test_get_significant_states_with_initial(legacy_hass_history) -> None: assert_dict_of_states_equal_without_context_and_last_changed(states, hist) -def test_get_significant_states_without_initial(legacy_hass_history) -> None: +async def test_get_significant_states_without_initial( + hass: HomeAssistant, legacy_hass_history +) -> None: """Test that only significant states are returned. We should get back every thermostat change that includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ - hass = legacy_hass_history - zero, four, states = record_states(hass) + zero, four, states = await async_record_states(hass) one = zero + timedelta(seconds=1) one_with_microsecond = zero + timedelta(seconds=1, microseconds=1) one_and_half = zero + timedelta(seconds=1.5) @@ -193,13 +194,13 @@ def test_get_significant_states_without_initial(legacy_hass_history) -> None: assert_dict_of_states_equal_without_context_and_last_changed(states, hist) -def test_get_significant_states_entity_id(hass_history) -> None: +async def test_get_significant_states_entity_id( + hass: HomeAssistant, hass_history +) -> None: """Test that only significant states are returned for one entity.""" - hass = hass_history - instance = recorder.get_instance(hass) with patch.object(instance.states_meta_manager, "active", False): - zero, four, states = record_states(hass) + zero, four, states = await async_record_states(hass) del states["media_player.test2"] del states["media_player.test3"] del states["thermostat.test"] @@ -210,10 +211,11 @@ def test_get_significant_states_entity_id(hass_history) -> None: assert_dict_of_states_equal_without_context_and_last_changed(states, hist) -def test_get_significant_states_multiple_entity_ids(legacy_hass_history) -> None: +async def test_get_significant_states_multiple_entity_ids( + hass: HomeAssistant, legacy_hass_history +) -> None: """Test that only significant states are returned for one entity.""" - hass = legacy_hass_history - zero, four, states = record_states(hass) + zero, four, states = await async_record_states(hass) del states["media_player.test2"] del states["media_player.test3"] del states["thermostat.test2"] @@ -228,14 +230,15 @@ def test_get_significant_states_multiple_entity_ids(legacy_hass_history) -> None assert_dict_of_states_equal_without_context_and_last_changed(states, hist) -def test_get_significant_states_are_ordered(legacy_hass_history) -> None: +async def test_get_significant_states_are_ordered( + hass: HomeAssistant, legacy_hass_history +) -> None: """Test order of results from get_significant_states. When entity ids are given, the results should be returned with the data in the same order. """ - hass = legacy_hass_history - zero, four, _states = record_states(hass) + zero, four, _states = await async_record_states(hass) entity_ids = ["media_player.test", "media_player.test2"] hist = get_significant_states(hass, zero, four, entity_ids) assert list(hist.keys()) == entity_ids @@ -244,15 +247,16 @@ def test_get_significant_states_are_ordered(legacy_hass_history) -> None: assert list(hist.keys()) == entity_ids -def test_get_significant_states_only(legacy_hass_history) -> None: +async def test_get_significant_states_only( + hass: HomeAssistant, legacy_hass_history +) -> None: """Test significant states when significant_states_only is set.""" - hass = legacy_hass_history entity_id = "sensor.test" - def set_state(state, **kwargs): + async def set_state(state, **kwargs): """Set the state.""" - hass.states.set(entity_id, state, **kwargs) - wait_recording_done(hass) + hass.states.async_set(entity_id, state, **kwargs) + await async_wait_recording_done(hass) return hass.states.get(entity_id) start = dt_util.utcnow() - timedelta(minutes=4) @@ -260,19 +264,19 @@ def test_get_significant_states_only(legacy_hass_history) -> None: states = [] with freeze_time(start) as freezer: - set_state("123", attributes={"attribute": 10.64}) + await set_state("123", attributes={"attribute": 10.64}) freezer.move_to(points[0]) # Attributes are different, state not - states.append(set_state("123", attributes={"attribute": 21.42})) + states.append(await set_state("123", attributes={"attribute": 21.42})) freezer.move_to(points[1]) # state is different, attributes not - states.append(set_state("32", attributes={"attribute": 21.42})) + states.append(await set_state("32", attributes={"attribute": 21.42})) freezer.move_to(points[2]) # everything is different - states.append(set_state("412", attributes={"attribute": 54.23})) + states.append(await set_state("412", attributes={"attribute": 54.23})) hist = get_significant_states( hass, @@ -311,7 +315,7 @@ def check_significant_states(hass, zero, four, states, config): assert_dict_of_states_equal_without_context_and_last_changed(states, hist) -def record_states(hass): +async def async_record_states(hass): """Record some test states. We inject a bunch of state updates from media player, zone and @@ -325,10 +329,10 @@ def record_states(hass): zone = "zone.home" script_c = "script.can_cancel_this_one" - def set_state(entity_id, state, **kwargs): + async def async_set_state(entity_id, state, **kwargs): """Set the state.""" - hass.states.set(entity_id, state, **kwargs) - wait_recording_done(hass) + hass.states.async_set(entity_id, state, **kwargs) + await async_wait_recording_done(hass) return hass.states.get(entity_id) zero = dt_util.utcnow() @@ -340,55 +344,69 @@ def record_states(hass): states = {therm: [], therm2: [], mp: [], mp2: [], mp3: [], script_c: []} with freeze_time(one) as freezer: states[mp].append( - set_state(mp, "idle", attributes={"media_title": str(sentinel.mt1)}) + await async_set_state( + mp, "idle", attributes={"media_title": str(sentinel.mt1)} + ) ) states[mp2].append( - set_state(mp2, "YouTube", attributes={"media_title": str(sentinel.mt2)}) + await async_set_state( + mp2, "YouTube", attributes={"media_title": str(sentinel.mt2)} + ) ) states[mp3].append( - set_state(mp3, "idle", attributes={"media_title": str(sentinel.mt1)}) + await async_set_state( + mp3, "idle", attributes={"media_title": str(sentinel.mt1)} + ) ) states[therm].append( - set_state(therm, 20, attributes={"current_temperature": 19.5}) + await async_set_state(therm, 20, attributes={"current_temperature": 19.5}) ) freezer.move_to(one + timedelta(microseconds=1)) states[mp].append( - set_state(mp, "YouTube", attributes={"media_title": str(sentinel.mt2)}) + await async_set_state( + mp, "YouTube", attributes={"media_title": str(sentinel.mt2)} + ) ) freezer.move_to(two) # This state will be skipped only different in time - set_state(mp, "YouTube", attributes={"media_title": str(sentinel.mt3)}) + await async_set_state( + mp, "YouTube", attributes={"media_title": str(sentinel.mt3)} + ) # This state will be skipped because domain is excluded - set_state(zone, "zoning") + await async_set_state(zone, "zoning") states[script_c].append( - set_state(script_c, "off", attributes={"can_cancel": True}) + await async_set_state(script_c, "off", attributes={"can_cancel": True}) ) states[therm].append( - set_state(therm, 21, attributes={"current_temperature": 19.8}) + await async_set_state(therm, 21, attributes={"current_temperature": 19.8}) ) states[therm2].append( - set_state(therm2, 20, attributes={"current_temperature": 19}) + await async_set_state(therm2, 20, attributes={"current_temperature": 19}) ) freezer.move_to(three) states[mp].append( - set_state(mp, "Netflix", attributes={"media_title": str(sentinel.mt4)}) + await async_set_state( + mp, "Netflix", attributes={"media_title": str(sentinel.mt4)} + ) ) states[mp3].append( - set_state(mp3, "Netflix", attributes={"media_title": str(sentinel.mt3)}) + await async_set_state( + mp3, "Netflix", attributes={"media_title": str(sentinel.mt3)} + ) ) # Attributes changed even though state is the same states[therm].append( - set_state(therm, 21, attributes={"current_temperature": 20}) + await async_set_state(therm, 21, attributes={"current_temperature": 20}) ) return zero, four, states async def test_fetch_period_api( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test the fetch period view for history.""" await async_setup_component(hass, "history", {}) @@ -402,7 +420,7 @@ async def test_fetch_period_api( async def test_fetch_period_api_with_minimal_response( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test the fetch period view for history with minimal_response.""" now = dt_util.utcnow() @@ -445,7 +463,7 @@ async def test_fetch_period_api_with_minimal_response( async def test_fetch_period_api_with_no_timestamp( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test the fetch period view for history with no timestamp.""" await async_setup_component(hass, "history", {}) @@ -457,7 +475,7 @@ async def test_fetch_period_api_with_no_timestamp( async def test_fetch_period_api_with_include_order( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test the fetch period view for history.""" await async_setup_component( @@ -481,7 +499,7 @@ async def test_fetch_period_api_with_include_order( async def test_entity_ids_limit_via_api( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test limiting history to entity_ids.""" await async_setup_component( @@ -509,7 +527,7 @@ async def test_entity_ids_limit_via_api( async def test_entity_ids_limit_via_api_with_skip_initial_state( - recorder_mock: Recorder, hass: HomeAssistant, hass_client: ClientSessionGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_client: ClientSessionGenerator ) -> None: """Test limiting history to entity_ids with skip_initial_state.""" await async_setup_component( @@ -545,7 +563,7 @@ async def test_entity_ids_limit_via_api_with_skip_initial_state( async def test_history_during_period( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history_during_period.""" now = dt_util.utcnow() @@ -693,7 +711,7 @@ async def test_history_during_period( async def test_history_during_period_impossible_conditions( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history_during_period returns when condition cannot be true.""" await async_setup_component(hass, "history", {}) @@ -757,10 +775,10 @@ async def test_history_during_period_impossible_conditions( "time_zone", ["UTC", "Europe/Berlin", "America/Chicago", "US/Hawaii"] ) async def test_history_during_period_significant_domain( - time_zone, - recorder_mock: Recorder, hass: HomeAssistant, + recorder_mock: Recorder, hass_ws_client: WebSocketGenerator, + time_zone, ) -> None: """Test history_during_period with climate domain.""" hass.config.set_time_zone(time_zone) @@ -941,7 +959,7 @@ async def test_history_during_period_significant_domain( async def test_history_during_period_bad_start_time( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history_during_period bad state time.""" await async_setup_component( @@ -966,7 +984,7 @@ async def test_history_during_period_bad_start_time( async def test_history_during_period_bad_end_time( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history_during_period bad end time.""" now = dt_util.utcnow() diff --git a/tests/components/history/test_websocket_api.py b/tests/components/history/test_websocket_api.py index 70e2eb9470a..8ff3c91a3fc 100644 --- a/tests/components/history/test_websocket_api.py +++ b/tests/components/history/test_websocket_api.py @@ -39,7 +39,7 @@ def test_setup() -> None: async def test_history_during_period( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history_during_period.""" now = dt_util.utcnow() @@ -173,7 +173,7 @@ async def test_history_during_period( async def test_history_during_period_impossible_conditions( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history_during_period returns when condition cannot be true.""" await async_setup_component(hass, "history", {}) @@ -235,10 +235,10 @@ async def test_history_during_period_impossible_conditions( "time_zone", ["UTC", "Europe/Berlin", "America/Chicago", "US/Hawaii"] ) async def test_history_during_period_significant_domain( - time_zone, - recorder_mock: Recorder, hass: HomeAssistant, + recorder_mock: Recorder, hass_ws_client: WebSocketGenerator, + time_zone, ) -> None: """Test history_during_period with climate domain.""" hass.config.set_time_zone(time_zone) @@ -403,7 +403,7 @@ async def test_history_during_period_significant_domain( async def test_history_during_period_bad_start_time( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history_during_period bad state time.""" await async_setup_component( @@ -427,7 +427,7 @@ async def test_history_during_period_bad_start_time( async def test_history_during_period_bad_end_time( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history_during_period bad end time.""" now = dt_util.utcnow() @@ -454,7 +454,7 @@ async def test_history_during_period_bad_end_time( async def test_history_stream_historical_only( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history stream.""" now = dt_util.utcnow() @@ -525,7 +525,7 @@ async def test_history_stream_historical_only( async def test_history_stream_significant_domain_historical_only( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test the stream with climate domain with historical states only.""" now = dt_util.utcnow() @@ -726,7 +726,7 @@ async def test_history_stream_significant_domain_historical_only( async def test_history_stream_bad_start_time( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history stream bad state time.""" await async_setup_component( @@ -750,7 +750,7 @@ async def test_history_stream_bad_start_time( async def test_history_stream_end_time_before_start_time( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history stream with an end_time before the start_time.""" end_time = dt_util.utcnow() - timedelta(seconds=2) @@ -778,7 +778,7 @@ async def test_history_stream_end_time_before_start_time( async def test_history_stream_bad_end_time( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history stream bad end time.""" now = dt_util.utcnow() @@ -805,7 +805,7 @@ async def test_history_stream_bad_end_time( async def test_history_stream_live_no_attributes_minimal_response( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history stream with history and live data and no_attributes and minimal_response.""" now = dt_util.utcnow() @@ -882,7 +882,7 @@ async def test_history_stream_live_no_attributes_minimal_response( async def test_history_stream_live( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history stream with history and live data.""" now = dt_util.utcnow() @@ -985,7 +985,7 @@ async def test_history_stream_live( async def test_history_stream_live_minimal_response( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history stream with history and live data and minimal_response.""" now = dt_util.utcnow() @@ -1082,7 +1082,7 @@ async def test_history_stream_live_minimal_response( async def test_history_stream_live_no_attributes( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history stream with history and live data and no_attributes.""" now = dt_util.utcnow() @@ -1163,7 +1163,7 @@ async def test_history_stream_live_no_attributes( async def test_history_stream_live_no_attributes_minimal_response_specific_entities( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history stream with history and live data and no_attributes and minimal_response with specific entities.""" now = dt_util.utcnow() @@ -1241,7 +1241,7 @@ async def test_history_stream_live_no_attributes_minimal_response_specific_entit async def test_history_stream_live_with_future_end_time( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history stream with history and live data with future end time.""" now = dt_util.utcnow() @@ -1334,8 +1334,8 @@ async def test_history_stream_live_with_future_end_time( @pytest.mark.parametrize("include_start_time_state", [True, False]) async def test_history_stream_before_history_starts( - recorder_mock: Recorder, hass: HomeAssistant, + recorder_mock: Recorder, hass_ws_client: WebSocketGenerator, include_start_time_state, ) -> None: @@ -1385,7 +1385,7 @@ async def test_history_stream_before_history_starts( async def test_history_stream_for_entity_with_no_possible_changes( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history stream for future with no possible changes where end time is less than or equal to now.""" await async_setup_component( @@ -1436,7 +1436,7 @@ async def test_history_stream_for_entity_with_no_possible_changes( async def test_overflow_queue( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test overflowing the history stream queue.""" now = dt_util.utcnow() @@ -1513,7 +1513,7 @@ async def test_overflow_queue( async def test_history_during_period_for_invalid_entity_ids( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history_during_period for valid and invalid entity ids.""" now = dt_util.utcnow() @@ -1656,7 +1656,7 @@ async def test_history_during_period_for_invalid_entity_ids( async def test_history_stream_for_invalid_entity_ids( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history stream for invalid and valid entity ids.""" @@ -1824,7 +1824,7 @@ async def test_history_stream_for_invalid_entity_ids( async def test_history_stream_historical_only_with_start_time_state_past( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history stream.""" await async_setup_component( diff --git a/tests/components/history/test_websocket_api_schema_32.py b/tests/components/history/test_websocket_api_schema_32.py index 6ef6f7225c1..301de387c80 100644 --- a/tests/components/history/test_websocket_api_schema_32.py +++ b/tests/components/history/test_websocket_api_schema_32.py @@ -24,7 +24,7 @@ def db_schema_32(): async def test_history_during_period( - recorder_mock: Recorder, hass: HomeAssistant, hass_ws_client: WebSocketGenerator + hass: HomeAssistant, recorder_mock: Recorder, hass_ws_client: WebSocketGenerator ) -> None: """Test history_during_period.""" now = dt_util.utcnow() diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index f0609f82229..d9f0e7d296f 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -198,7 +198,7 @@ async def test_shutdown_closes_connections( hass.set_state(CoreState.not_running) - instance = get_instance(hass) + instance = recorder.get_instance(hass) await instance.async_db_ready await hass.async_block_till_done() pool = instance.engine.pool diff --git a/tests/conftest.py b/tests/conftest.py index 4852a41c061..031469848ca 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -526,6 +526,7 @@ async def hass( load_registries: bool, hass_storage: dict[str, Any], request: pytest.FixtureRequest, + mock_recorder_before_hass: None, ) -> AsyncGenerator[HomeAssistant, None]: """Create a test instance of Home Assistant.""" @@ -1577,6 +1578,15 @@ async def recorder_mock( return await async_setup_recorder_instance(hass, recorder_config) +@pytest.fixture +def mock_recorder_before_hass() -> None: + """Mock the recorder. + + Override or parametrize this fixture with a fixture that mocks the recorder, + in the tests that need to test the recorder. + """ + + @pytest.fixture(name="enable_bluetooth") async def mock_enable_bluetooth( hass: HomeAssistant, From ecdad1929621a19a2cf0db4acffefd2e7b2a1bfc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 May 2024 02:12:28 -0500 Subject: [PATCH 9/9] Drop pyserial-asyncio from zha (#116638) --- homeassistant/components/zha/manifest.json | 1 - requirements_all.txt | 3 --- requirements_test_all.txt | 3 --- 3 files changed, 7 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 7a407a2eb33..9a0ca62542e 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -23,7 +23,6 @@ "requirements": [ "bellows==0.38.4", "pyserial==3.5", - "pyserial-asyncio==0.6", "zha-quirks==0.0.115", "zigpy-deconz==0.23.1", "zigpy==0.64.0", diff --git a/requirements_all.txt b/requirements_all.txt index 4025cffa9ae..d2950403d4c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2123,9 +2123,6 @@ pysensibo==1.0.36 # homeassistant.components.zha pyserial-asyncio-fast==0.11 -# homeassistant.components.zha -pyserial-asyncio==0.6 - # homeassistant.components.acer_projector # homeassistant.components.crownstone # homeassistant.components.usb diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bc80158d3fa..065b4f63aa3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1662,9 +1662,6 @@ pysensibo==1.0.36 # homeassistant.components.zha pyserial-asyncio-fast==0.11 -# homeassistant.components.zha -pyserial-asyncio==0.6 - # homeassistant.components.acer_projector # homeassistant.components.crownstone # homeassistant.components.usb