diff --git a/homeassistant/components/apcupsd/quality_scale.yaml b/homeassistant/components/apcupsd/quality_scale.yaml index 18fc15cd614b56..f6fd7e0c2d30af 100644 --- a/homeassistant/components/apcupsd/quality_scale.yaml +++ b/homeassistant/components/apcupsd/quality_scale.yaml @@ -13,7 +13,6 @@ rules: Consider looking into making a `mock_setup_entry` fixture that just automatically do this. `test_config_flow_cannot_connect`: Needs to end in CREATE_ENTRY to test that its able to recover. `test_config_flow_duplicate`: this test should be split in 2, one for testing duplicate host/port and one for duplicate serial number. - `test_flow_works`: Should also test unique id. config-flow: done dependency-transparency: done docs-actions: diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index 7abc929fde5e4b..e3428eb9b869ed 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -388,12 +388,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: mode = BluetoothScanningMode.PASSIVE if passive else BluetoothScanningMode.ACTIVE scanner = HaScanner(mode, adapter, address) scanner.async_setup() - try: - await scanner.async_start() - except (RuntimeError, ScannerStartError) as err: - raise ConfigEntryNotReady( - f"{adapter_human_name(adapter, address)}: {err}" - ) from err adapters = await manager.async_get_bluetooth_adapters() details = adapters[adapter] if entry.title == address: @@ -401,8 +395,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: entry, title=adapter_title(adapter, details) ) slots: int = details.get(ADAPTER_CONNECTION_SLOTS) or DEFAULT_CONNECTION_SLOTS + # Register the scanner before starting so + # any raw advertisement data can be processed entry.async_on_unload(async_register_scanner(hass, scanner, connection_slots=slots)) await async_update_device(hass, entry, adapter, details) + try: + await scanner.async_start() + except (RuntimeError, ScannerStartError) as err: + raise ConfigEntryNotReady( + f"{adapter_human_name(adapter, address)}: {err}" + ) from err entry.async_on_unload(entry.add_update_listener(async_update_listener)) entry.async_on_unload(scanner.async_stop) return True diff --git a/homeassistant/components/bluetooth/manager.py b/homeassistant/components/bluetooth/manager.py index 46c5425c730177..5f3cb62c158fad 100644 --- a/homeassistant/components/bluetooth/manager.py +++ b/homeassistant/components/bluetooth/manager.py @@ -235,10 +235,9 @@ def _async_save_scanner_histories(self) -> None: def _async_save_scanner_history(self, scanner: BaseHaScanner) -> None: """Save the scanner history.""" - if isinstance(scanner, BaseHaRemoteScanner): - self.storage.async_set_advertisement_history( - scanner.source, scanner.serialize_discovered_devices() - ) + self.storage.async_set_advertisement_history( + scanner.source, scanner.serialize_discovered_devices() + ) def _async_unregister_scanner( self, scanner: BaseHaScanner, unregister: CALLBACK_TYPE @@ -285,9 +284,8 @@ def async_register_scanner( connection_slots: int | None = None, ) -> CALLBACK_TYPE: """Register a scanner.""" - if isinstance(scanner, BaseHaRemoteScanner): - if history := self.storage.async_get_advertisement_history(scanner.source): - scanner.restore_discovered_devices(history) + if history := self.storage.async_get_advertisement_history(scanner.source): + scanner.restore_discovered_devices(history) unregister = super().async_register_scanner(scanner, connection_slots) return partial(self._async_unregister_scanner, scanner, unregister) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index ce5d98f8edbaa2..aa0d2076c3f5b0 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -21,6 +21,6 @@ "bluetooth-auto-recovery==1.5.2", "bluetooth-data-tools==1.28.2", "dbus-fast==2.44.3", - "habluetooth==4.0.2" + "habluetooth==5.0.1" ] } diff --git a/homeassistant/components/bluetooth/websocket_api.py b/homeassistant/components/bluetooth/websocket_api.py index d21b11b050fff8..9022d98bf06f39 100644 --- a/homeassistant/components/bluetooth/websocket_api.py +++ b/homeassistant/components/bluetooth/websocket_api.py @@ -39,7 +39,13 @@ def async_setup(hass: HomeAssistant) -> None: def serialize_service_info( service_info: BluetoothServiceInfoBleak, time_diff: float ) -> dict[str, Any]: - """Serialize a BluetoothServiceInfoBleak object.""" + """Serialize a BluetoothServiceInfoBleak object. + + The raw field is included for: + 1. Debugging - to see the actual advertisement packet + 2. Data freshness - manufacturer_data and service_data are aggregated + across multiple advertisements, raw shows the latest packet only + """ return { "name": service_info.name, "address": service_info.address, @@ -57,6 +63,7 @@ def serialize_service_info( "connectable": service_info.connectable, "time": service_info.time + time_diff, "tx_power": service_info.tx_power, + "raw": service_info.raw.hex() if service_info.raw else None, } diff --git a/homeassistant/components/emoncms/const.py b/homeassistant/components/emoncms/const.py index a3b4629493f738..329ec9e3a12dc0 100644 --- a/homeassistant/components/emoncms/const.py +++ b/homeassistant/components/emoncms/const.py @@ -2,7 +2,6 @@ import logging -CONF_EXCLUDE_FEEDID = "exclude_feed_id" CONF_ONLY_INCLUDE_FEEDID = "include_only_feed_id" CONF_MESSAGE = "message" CONF_SUCCESS = "success" diff --git a/homeassistant/components/emoncms/sensor.py b/homeassistant/components/emoncms/sensor.py index c5a25104549a4c..3cb3959d3f2166 100644 --- a/homeassistant/components/emoncms/sensor.py +++ b/homeassistant/components/emoncms/sensor.py @@ -34,13 +34,7 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity from .config_flow import sensor_name -from .const import ( - CONF_EXCLUDE_FEEDID, - CONF_ONLY_INCLUDE_FEEDID, - FEED_ID, - FEED_NAME, - FEED_TAG, -) +from .const import CONF_ONLY_INCLUDE_FEEDID, FEED_ID, FEED_NAME, FEED_TAG from .coordinator import EmonCMSConfigEntry, EmoncmsCoordinator SENSORS: dict[str | None, SensorEntityDescription] = { @@ -200,12 +194,11 @@ async def async_setup_entry( ) -> None: """Set up the emoncms sensors.""" name = sensor_name(entry.data[CONF_URL]) - exclude_feeds = entry.data.get(CONF_EXCLUDE_FEEDID) include_only_feeds = entry.options.get( CONF_ONLY_INCLUDE_FEEDID, entry.data.get(CONF_ONLY_INCLUDE_FEEDID) ) - if exclude_feeds is None and include_only_feeds is None: + if include_only_feeds is None: return coordinator = entry.runtime_data diff --git a/homeassistant/components/mercury_nz/__init__.py b/homeassistant/components/mercury_nz/__init__.py deleted file mode 100644 index ff22fc5ce4ad3b..00000000000000 --- a/homeassistant/components/mercury_nz/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Virtual integration: Mercury NZ Limited.""" diff --git a/homeassistant/components/mercury_nz/manifest.json b/homeassistant/components/mercury_nz/manifest.json deleted file mode 100644 index d9d3078706791d..00000000000000 --- a/homeassistant/components/mercury_nz/manifest.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "domain": "mercury_nz", - "name": "Mercury NZ Limited", - "integration_type": "virtual", - "supported_by": "opower" -} diff --git a/homeassistant/components/modbus/const.py b/homeassistant/components/modbus/const.py index ada56c46f79eb1..dafc604e7812a6 100644 --- a/homeassistant/components/modbus/const.py +++ b/homeassistant/components/modbus/const.py @@ -97,6 +97,7 @@ CONF_WRITE_TYPE = "write_type" CONF_ZERO_SUPPRESS = "zero_suppress" +DEVICE_ID = "device_id" RTUOVERTCP = "rtuovertcp" SERIAL = "serial" TCP = "tcp" diff --git a/homeassistant/components/modbus/modbus.py b/homeassistant/components/modbus/modbus.py index 6f1a4bfd5b1456..5ddde2973ee232 100644 --- a/homeassistant/components/modbus/modbus.py +++ b/homeassistant/components/modbus/modbus.py @@ -57,6 +57,7 @@ CONF_PARITY, CONF_STOPBITS, DEFAULT_HUB, + DEVICE_ID, MODBUS_DOMAIN as DOMAIN, PLATFORMS, RTUOVERTCP, @@ -380,7 +381,7 @@ async def low_level_pb_call( ) -> ModbusPDU | None: """Call sync. pymodbus.""" kwargs: dict[str, Any] = ( - {ATTR_SLAVE: slave} if slave is not None else {ATTR_SLAVE: 1} + {DEVICE_ID: slave} if slave is not None else {DEVICE_ID: 1} ) entry = self._pb_request[use_call] diff --git a/homeassistant/components/open_router/manifest.json b/homeassistant/components/open_router/manifest.json index 7dd824c2587c01..4a406e0613960b 100644 --- a/homeassistant/components/open_router/manifest.json +++ b/homeassistant/components/open_router/manifest.json @@ -9,5 +9,5 @@ "integration_type": "service", "iot_class": "cloud_polling", "quality_scale": "bronze", - "requirements": ["openai==1.99.3", "python-open-router==0.3.1"] + "requirements": ["openai==1.99.5", "python-open-router==0.3.1"] } diff --git a/homeassistant/components/openai_conversation/manifest.json b/homeassistant/components/openai_conversation/manifest.json index 12ee6278c5d47b..38ebe205bd373f 100644 --- a/homeassistant/components/openai_conversation/manifest.json +++ b/homeassistant/components/openai_conversation/manifest.json @@ -8,5 +8,5 @@ "documentation": "https://www.home-assistant.io/integrations/openai_conversation", "integration_type": "service", "iot_class": "cloud_polling", - "requirements": ["openai==1.99.3"] + "requirements": ["openai==1.99.5"] } diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index d40f882240b477..c656958b3cc5e9 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -3833,11 +3833,6 @@ "config_flow": false, "iot_class": "cloud_polling" }, - "mercury_nz": { - "name": "Mercury NZ Limited", - "integration_type": "virtual", - "supported_by": "opower" - }, "message_bird": { "name": "MessageBird", "integration_type": "hub", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index b79b0ecf6be543..ef3843e7da4b9a 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -34,7 +34,7 @@ dbus-fast==2.44.3 fnv-hash-fast==1.5.0 go2rtc-client==0.2.1 ha-ffmpeg==3.2.2 -habluetooth==4.0.2 +habluetooth==5.0.1 hass-nabucasa==0.111.2 hassil==2.2.3 home-assistant-bluetooth==1.13.1 diff --git a/requirements_all.txt b/requirements_all.txt index f3ee946e44383a..0e32d350bbed28 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1130,7 +1130,7 @@ ha-silabs-firmware-client==0.2.0 habiticalib==0.4.1 # homeassistant.components.bluetooth -habluetooth==4.0.2 +habluetooth==5.0.1 # homeassistant.components.cloud hass-nabucasa==0.111.2 @@ -1601,7 +1601,7 @@ open-meteo==0.3.2 # homeassistant.components.open_router # homeassistant.components.openai_conversation -openai==1.99.3 +openai==1.99.5 # homeassistant.components.openerz openerz-api==0.3.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4d26a57143713f..fa1ead8b83be43 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -991,7 +991,7 @@ ha-silabs-firmware-client==0.2.0 habiticalib==0.4.1 # homeassistant.components.bluetooth -habluetooth==4.0.2 +habluetooth==5.0.1 # homeassistant.components.cloud hass-nabucasa==0.111.2 @@ -1369,7 +1369,7 @@ open-meteo==0.3.2 # homeassistant.components.open_router # homeassistant.components.openai_conversation -openai==1.99.3 +openai==1.99.5 # homeassistant.components.openerz openerz-api==0.3.0 diff --git a/tests/components/apcupsd/test_config_flow.py b/tests/components/apcupsd/test_config_flow.py index e635b7d6681a91..6263b5646e562f 100644 --- a/tests/components/apcupsd/test_config_flow.py +++ b/tests/components/apcupsd/test_config_flow.py @@ -123,6 +123,7 @@ async def test_flow_works(hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.CREATE_ENTRY assert result["title"] == MOCK_STATUS["UPSNAME"] assert result["data"] == CONF_DATA + assert result["result"].unique_id == MOCK_STATUS["SERIALNO"] mock_setup.assert_called_once() diff --git a/tests/components/bluetooth/__init__.py b/tests/components/bluetooth/__init__.py index d439f46bb7162b..6951a2ce4cc384 100644 --- a/tests/components/bluetooth/__init__.py +++ b/tests/components/bluetooth/__init__.py @@ -145,6 +145,7 @@ def inject_advertisement_with_time_and_source_connectable( time: float, source: str, connectable: bool, + raw: bytes | None = None, ) -> None: """Inject an advertisement into the manager from a specific source at a time and connectable status.""" async_get_advertisement_callback(hass)( @@ -161,6 +162,7 @@ def inject_advertisement_with_time_and_source_connectable( connectable=connectable, time=time, tx_power=adv.tx_power, + raw=raw, ) ) diff --git a/tests/components/bluetooth/test_websocket_api.py b/tests/components/bluetooth/test_websocket_api.py index 2e613932f3cdc1..f12d77913a93a2 100644 --- a/tests/components/bluetooth/test_websocket_api.py +++ b/tests/components/bluetooth/test_websocket_api.py @@ -22,6 +22,7 @@ generate_advertisement_data, generate_ble_device, inject_advertisement_with_source, + inject_advertisement_with_time_and_source_connectable, ) from tests.common import MockConfigEntry, async_fire_time_changed @@ -72,6 +73,7 @@ async def test_subscribe_advertisements( "source": HCI0_SOURCE_ADDRESS, "time": ANY, "tx_power": -127, + "raw": None, } ] } @@ -83,8 +85,15 @@ async def test_subscribe_advertisements( service_uuids=[], rssi=-80, ) - inject_advertisement_with_source( - hass, switchbot_device_signal_100, switchbot_adv_signal_100, HCI1_SOURCE_ADDRESS + # Inject with raw bytes data + inject_advertisement_with_time_and_source_connectable( + hass, + switchbot_device_signal_100, + switchbot_adv_signal_100, + time.monotonic(), + HCI1_SOURCE_ADDRESS, + True, + raw=b"\x02\x01\x06\x03\x03\x0f\x18", ) async with asyncio.timeout(1): response = await client.receive_json() @@ -101,6 +110,7 @@ async def test_subscribe_advertisements( "source": HCI1_SOURCE_ADDRESS, "time": ANY, "tx_power": -127, + "raw": "02010603030f18", } ] } diff --git a/tests/components/modbus/test_climate.py b/tests/components/modbus/test_climate.py index 54d4c5f6666683..38f2aa3337f32d 100644 --- a/tests/components/modbus/test_climate.py +++ b/tests/components/modbus/test_climate.py @@ -467,7 +467,7 @@ async def test_hvac_onoff_values(hass: HomeAssistant, mock_modbus) -> None: ) await hass.async_block_till_done() - mock_modbus.write_register.assert_called_with(11, value=0xAA, slave=10) + mock_modbus.write_register.assert_called_with(11, value=0xAA, device_id=10) await hass.services.async_call( CLIMATE_DOMAIN, @@ -477,7 +477,7 @@ async def test_hvac_onoff_values(hass: HomeAssistant, mock_modbus) -> None: ) await hass.async_block_till_done() - mock_modbus.write_register.assert_called_with(11, value=0xFF, slave=10) + mock_modbus.write_register.assert_called_with(11, value=0xFF, device_id=10) @pytest.mark.parametrize( @@ -506,7 +506,7 @@ async def test_hvac_onoff_coil(hass: HomeAssistant, mock_modbus) -> None: ) await hass.async_block_till_done() - mock_modbus.write_coil.assert_called_with(11, value=1, slave=10) + mock_modbus.write_coil.assert_called_with(11, value=1, device_id=10) await hass.services.async_call( CLIMATE_DOMAIN, @@ -516,7 +516,7 @@ async def test_hvac_onoff_coil(hass: HomeAssistant, mock_modbus) -> None: ) await hass.async_block_till_done() - mock_modbus.write_coil.assert_called_with(11, value=0, slave=10) + mock_modbus.write_coil.assert_called_with(11, value=0, device_id=10) @pytest.mark.parametrize( diff --git a/tests/components/modbus/test_init.py b/tests/components/modbus/test_init.py index 4c0a8bd8f6e57a..be92e12c700197 100644 --- a/tests/components/modbus/test_init.py +++ b/tests/components/modbus/test_init.py @@ -63,6 +63,7 @@ CONF_SWING_MODE_VALUES, CONF_VIRTUAL_COUNT, DEFAULT_SCAN_INTERVAL, + DEVICE_ID, MODBUS_DOMAIN as DOMAIN, RTUOVERTCP, SERIAL, @@ -867,7 +868,7 @@ async def test_pb_service_write( assert func_name[do_write[FUNC]].called assert func_name[do_write[FUNC]].call_args.args == (data[ATTR_ADDRESS],) assert func_name[do_write[FUNC]].call_args.kwargs == { - "slave": 17, + DEVICE_ID: 17, value_arg_name[do_write[FUNC]]: data[do_write[DATA]], } @@ -1326,7 +1327,7 @@ async def test_check_default_slave( """Test default slave.""" assert mock_modbus.read_holding_registers.mock_calls first_call = mock_modbus.read_holding_registers.mock_calls[0] - assert first_call.kwargs["slave"] == expected_slave_value + assert first_call.kwargs[DEVICE_ID] == expected_slave_value @pytest.mark.parametrize( @@ -1407,7 +1408,7 @@ async def test_pb_service_write_no_slave( assert func_name[do_write[FUNC]].called assert func_name[do_write[FUNC]].call_args.args == (data[ATTR_ADDRESS],) assert func_name[do_write[FUNC]].call_args.kwargs == { - "slave": 1, + DEVICE_ID: 1, value_arg_name[do_write[FUNC]]: data[do_write[DATA]], }