mirror of
https://github.com/home-assistant/core.git
synced 2025-07-30 16:57:19 +00:00
Merge pull request #76398 from home-assistant/rc
This commit is contained in:
commit
bfb2867e8d
@ -3,7 +3,7 @@
|
||||
"name": "Big Ass Fans",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/baf",
|
||||
"requirements": ["aiobafi6==0.7.0"],
|
||||
"requirements": ["aiobafi6==0.7.2"],
|
||||
"codeowners": ["@bdraco", "@jfroy"],
|
||||
"iot_class": "local_push",
|
||||
"zeroconf": [
|
||||
|
@ -8,6 +8,7 @@ from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
from enum import Enum
|
||||
import logging
|
||||
import time
|
||||
from typing import TYPE_CHECKING, Final
|
||||
|
||||
import async_timeout
|
||||
@ -56,6 +57,10 @@ START_TIMEOUT = 9
|
||||
|
||||
SOURCE_LOCAL: Final = "local"
|
||||
|
||||
SCANNER_WATCHDOG_TIMEOUT: Final = 60 * 5
|
||||
SCANNER_WATCHDOG_INTERVAL: Final = timedelta(seconds=SCANNER_WATCHDOG_TIMEOUT)
|
||||
MONOTONIC_TIME = time.monotonic
|
||||
|
||||
|
||||
@dataclass
|
||||
class BluetoothServiceInfoBleak(BluetoothServiceInfo):
|
||||
@ -252,6 +257,7 @@ async def async_setup_entry(
|
||||
) -> bool:
|
||||
"""Set up the bluetooth integration from a config entry."""
|
||||
manager: BluetoothManager = hass.data[DOMAIN]
|
||||
async with manager.start_stop_lock:
|
||||
await manager.async_start(
|
||||
BluetoothScanningMode.ACTIVE, entry.options.get(CONF_ADAPTER)
|
||||
)
|
||||
@ -263,8 +269,6 @@ async def _async_update_listener(
|
||||
hass: HomeAssistant, entry: config_entries.ConfigEntry
|
||||
) -> None:
|
||||
"""Handle options update."""
|
||||
manager: BluetoothManager = hass.data[DOMAIN]
|
||||
manager.async_start_reload()
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
@ -273,6 +277,8 @@ async def async_unload_entry(
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
manager: BluetoothManager = hass.data[DOMAIN]
|
||||
async with manager.start_stop_lock:
|
||||
manager.async_start_reload()
|
||||
await manager.async_stop()
|
||||
return True
|
||||
|
||||
@ -289,13 +295,19 @@ class BluetoothManager:
|
||||
self.hass = hass
|
||||
self._integration_matcher = integration_matcher
|
||||
self.scanner: HaBleakScanner | None = None
|
||||
self.start_stop_lock = asyncio.Lock()
|
||||
self._cancel_device_detected: CALLBACK_TYPE | None = None
|
||||
self._cancel_unavailable_tracking: CALLBACK_TYPE | None = None
|
||||
self._cancel_stop: CALLBACK_TYPE | None = None
|
||||
self._cancel_watchdog: CALLBACK_TYPE | None = None
|
||||
self._unavailable_callbacks: dict[str, list[Callable[[str], None]]] = {}
|
||||
self._callbacks: list[
|
||||
tuple[BluetoothCallback, BluetoothCallbackMatcher | None]
|
||||
] = []
|
||||
self._last_detection = 0.0
|
||||
self._reloading = False
|
||||
self._adapter: str | None = None
|
||||
self._scanning_mode = BluetoothScanningMode.ACTIVE
|
||||
|
||||
@hass_callback
|
||||
def async_setup(self) -> None:
|
||||
@ -317,6 +329,8 @@ class BluetoothManager:
|
||||
) -> None:
|
||||
"""Set up BT Discovery."""
|
||||
assert self.scanner is not None
|
||||
self._adapter = adapter
|
||||
self._scanning_mode = scanning_mode
|
||||
if self._reloading:
|
||||
# On reload, we need to reset the scanner instance
|
||||
# since the devices in its history may not be reachable
|
||||
@ -381,7 +395,32 @@ class BluetoothManager:
|
||||
_LOGGER.debug("BleakError while starting bluetooth: %s", ex, exc_info=True)
|
||||
raise ConfigEntryNotReady(f"Failed to start Bluetooth: {ex}") from ex
|
||||
self.async_setup_unavailable_tracking()
|
||||
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.async_stop)
|
||||
self._async_setup_scanner_watchdog()
|
||||
self._cancel_stop = self.hass.bus.async_listen_once(
|
||||
EVENT_HOMEASSISTANT_STOP, self._async_hass_stopping
|
||||
)
|
||||
|
||||
@hass_callback
|
||||
def _async_setup_scanner_watchdog(self) -> None:
|
||||
"""If Dbus gets restarted or updated, we need to restart the scanner."""
|
||||
self._last_detection = MONOTONIC_TIME()
|
||||
self._cancel_watchdog = async_track_time_interval(
|
||||
self.hass, self._async_scanner_watchdog, SCANNER_WATCHDOG_INTERVAL
|
||||
)
|
||||
|
||||
async def _async_scanner_watchdog(self, now: datetime) -> None:
|
||||
"""Check if the scanner is running."""
|
||||
time_since_last_detection = MONOTONIC_TIME() - self._last_detection
|
||||
if time_since_last_detection < SCANNER_WATCHDOG_TIMEOUT:
|
||||
return
|
||||
_LOGGER.info(
|
||||
"Bluetooth scanner has gone quiet for %s, restarting",
|
||||
SCANNER_WATCHDOG_INTERVAL,
|
||||
)
|
||||
async with self.start_stop_lock:
|
||||
self.async_start_reload()
|
||||
await self.async_stop()
|
||||
await self.async_start(self._scanning_mode, self._adapter)
|
||||
|
||||
@hass_callback
|
||||
def async_setup_unavailable_tracking(self) -> None:
|
||||
@ -416,6 +455,7 @@ class BluetoothManager:
|
||||
self, device: BLEDevice, advertisement_data: AdvertisementData
|
||||
) -> None:
|
||||
"""Handle a detected device."""
|
||||
self._last_detection = MONOTONIC_TIME()
|
||||
matched_domains = self._integration_matcher.match_domains(
|
||||
device, advertisement_data
|
||||
)
|
||||
@ -528,14 +568,26 @@ class BluetoothManager:
|
||||
for device_adv in self.scanner.history.values()
|
||||
]
|
||||
|
||||
async def async_stop(self, event: Event | None = None) -> None:
|
||||
async def _async_hass_stopping(self, event: Event) -> None:
|
||||
"""Stop the Bluetooth integration at shutdown."""
|
||||
self._cancel_stop = None
|
||||
await self.async_stop()
|
||||
|
||||
async def async_stop(self) -> None:
|
||||
"""Stop bluetooth discovery."""
|
||||
_LOGGER.debug("Stopping bluetooth discovery")
|
||||
if self._cancel_watchdog:
|
||||
self._cancel_watchdog()
|
||||
self._cancel_watchdog = None
|
||||
if self._cancel_device_detected:
|
||||
self._cancel_device_detected()
|
||||
self._cancel_device_detected = None
|
||||
if self._cancel_unavailable_tracking:
|
||||
self._cancel_unavailable_tracking()
|
||||
self._cancel_unavailable_tracking = None
|
||||
if self._cancel_stop:
|
||||
self._cancel_stop()
|
||||
self._cancel_stop = None
|
||||
if self.scanner:
|
||||
try:
|
||||
await self.scanner.stop() # type: ignore[no-untyped-call]
|
||||
|
@ -26,11 +26,13 @@ from homeassistant.const import (
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import PlatformNotReady
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import BroadlinkDevice
|
||||
from .const import DOMAIN
|
||||
from .entity import BroadlinkEntity
|
||||
from .helpers import data_packet, import_device, mac_address
|
||||
@ -80,8 +82,18 @@ async def async_setup_platform(
|
||||
host = config.get(CONF_HOST)
|
||||
|
||||
if switches := config.get(CONF_SWITCHES):
|
||||
platform_data = hass.data[DOMAIN].platforms.setdefault(Platform.SWITCH, {})
|
||||
platform_data.setdefault(mac_addr, []).extend(switches)
|
||||
platform_data = hass.data[DOMAIN].platforms.get(Platform.SWITCH, {})
|
||||
async_add_entities_config_entry: AddEntitiesCallback
|
||||
device: BroadlinkDevice
|
||||
async_add_entities_config_entry, device = platform_data.get(
|
||||
mac_addr, (None, None)
|
||||
)
|
||||
if not async_add_entities_config_entry:
|
||||
raise PlatformNotReady
|
||||
|
||||
async_add_entities_config_entry(
|
||||
BroadlinkRMSwitch(device, config) for config in switches
|
||||
)
|
||||
|
||||
else:
|
||||
_LOGGER.warning(
|
||||
@ -104,12 +116,8 @@ async def async_setup_entry(
|
||||
switches: list[BroadlinkSwitch] = []
|
||||
|
||||
if device.api.type in {"RM4MINI", "RM4PRO", "RMMINI", "RMMINIB", "RMPRO"}:
|
||||
platform_data = hass.data[DOMAIN].platforms.get(Platform.SWITCH, {})
|
||||
user_defined_switches = platform_data.get(device.api.mac, {})
|
||||
switches.extend(
|
||||
BroadlinkRMSwitch(device, config) for config in user_defined_switches
|
||||
)
|
||||
|
||||
platform_data = hass.data[DOMAIN].platforms.setdefault(Platform.SWITCH, {})
|
||||
platform_data[device.api.mac] = async_add_entities, device
|
||||
elif device.api.type == "SP1":
|
||||
switches.append(BroadlinkSP1Switch(device))
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
"name": "deCONZ",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/deconz",
|
||||
"requirements": ["pydeconz==101"],
|
||||
"requirements": ["pydeconz==102"],
|
||||
"ssdp": [
|
||||
{
|
||||
"manufacturer": "Royal Philips Electronics",
|
||||
|
@ -2,6 +2,7 @@
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from homeassistant.components.network import async_get_ipv4_broadcast_addresses
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
@ -32,7 +33,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
async def _async_scan_update(_=None):
|
||||
await gree_discovery.discovery.scan()
|
||||
bcast_addr = list(await async_get_ipv4_broadcast_addresses(hass))
|
||||
await gree_discovery.discovery.scan(0, bcast_ifaces=bcast_addr)
|
||||
|
||||
_LOGGER.debug("Scanning network for Gree devices")
|
||||
await _async_scan_update()
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Config flow for Gree."""
|
||||
from greeclimate.discovery import Discovery
|
||||
|
||||
from homeassistant.components.network import async_get_ipv4_broadcast_addresses
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import config_entry_flow
|
||||
|
||||
@ -10,7 +11,10 @@ from .const import DISCOVERY_TIMEOUT, DOMAIN
|
||||
async def _async_has_devices(hass: HomeAssistant) -> bool:
|
||||
"""Return if there are devices that can be discovered."""
|
||||
gree_discovery = Discovery(DISCOVERY_TIMEOUT)
|
||||
devices = await gree_discovery.scan(wait_for=DISCOVERY_TIMEOUT)
|
||||
bcast_addr = list(await async_get_ipv4_broadcast_addresses(hass))
|
||||
devices = await gree_discovery.scan(
|
||||
wait_for=DISCOVERY_TIMEOUT, bcast_ifaces=bcast_addr
|
||||
)
|
||||
return len(devices) > 0
|
||||
|
||||
|
||||
|
@ -3,7 +3,8 @@
|
||||
"name": "Gree Climate",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/gree",
|
||||
"requirements": ["greeclimate==1.2.0"],
|
||||
"requirements": ["greeclimate==1.3.0"],
|
||||
"dependencies": ["network"],
|
||||
"codeowners": ["@cmroche"],
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["greeclimate"]
|
||||
|
@ -135,7 +135,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:dns",
|
||||
name="TCP Queries",
|
||||
name="TCP queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.tcp_queries,
|
||||
@ -190,7 +190,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:dns",
|
||||
name="TCP Queries Ratio",
|
||||
name="TCP queries ratio",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value=lambda data: data.tcp_queries_ratio,
|
||||
|
@ -38,15 +38,22 @@ LOGGER = logging.getLogger(__name__)
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Philips TV from a config entry."""
|
||||
|
||||
system: SystemType | None = entry.data.get(CONF_SYSTEM)
|
||||
tvapi = PhilipsTV(
|
||||
entry.data[CONF_HOST],
|
||||
entry.data[CONF_API_VERSION],
|
||||
username=entry.data.get(CONF_USERNAME),
|
||||
password=entry.data.get(CONF_PASSWORD),
|
||||
system=system,
|
||||
)
|
||||
coordinator = PhilipsTVDataUpdateCoordinator(hass, tvapi, entry.options)
|
||||
|
||||
await coordinator.async_refresh()
|
||||
|
||||
if (actual_system := tvapi.system) and actual_system != system:
|
||||
data = {**entry.data, CONF_SYSTEM: actual_system}
|
||||
hass.config_entries.async_update_entry(entry, data=data)
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
"domain": "switchbot",
|
||||
"name": "SwitchBot",
|
||||
"documentation": "https://www.home-assistant.io/integrations/switchbot",
|
||||
"requirements": ["PySwitchbot==0.17.3"],
|
||||
"requirements": ["PySwitchbot==0.18.4"],
|
||||
"config_flow": true,
|
||||
"dependencies": ["bluetooth"],
|
||||
"codeowners": [
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""Lighting channels module for Zigbee Home Automation."""
|
||||
from __future__ import annotations
|
||||
|
||||
from contextlib import suppress
|
||||
from functools import cached_property
|
||||
|
||||
from zigpy.zcl.clusters import lighting
|
||||
|
||||
@ -46,17 +46,8 @@ class ColorChannel(ZigbeeChannel):
|
||||
"color_loop_active": False,
|
||||
}
|
||||
|
||||
@property
|
||||
def color_capabilities(self) -> int:
|
||||
"""Return color capabilities of the light."""
|
||||
with suppress(KeyError):
|
||||
return self.cluster["color_capabilities"]
|
||||
if self.cluster.get("color_temperature") is not None:
|
||||
return self.CAPABILITIES_COLOR_XY | self.CAPABILITIES_COLOR_TEMP
|
||||
return self.CAPABILITIES_COLOR_XY
|
||||
|
||||
@property
|
||||
def zcl_color_capabilities(self) -> lighting.Color.ColorCapabilities:
|
||||
@cached_property
|
||||
def color_capabilities(self) -> lighting.Color.ColorCapabilities:
|
||||
"""Return ZCL color capabilities of the light."""
|
||||
color_capabilities = self.cluster.get("color_capabilities")
|
||||
if color_capabilities is None:
|
||||
@ -117,43 +108,41 @@ class ColorChannel(ZigbeeChannel):
|
||||
def hs_supported(self) -> bool:
|
||||
"""Return True if the channel supports hue and saturation."""
|
||||
return (
|
||||
self.zcl_color_capabilities is not None
|
||||
self.color_capabilities is not None
|
||||
and lighting.Color.ColorCapabilities.Hue_and_saturation
|
||||
in self.zcl_color_capabilities
|
||||
in self.color_capabilities
|
||||
)
|
||||
|
||||
@property
|
||||
def enhanced_hue_supported(self) -> bool:
|
||||
"""Return True if the channel supports enhanced hue and saturation."""
|
||||
return (
|
||||
self.zcl_color_capabilities is not None
|
||||
and lighting.Color.ColorCapabilities.Enhanced_hue
|
||||
in self.zcl_color_capabilities
|
||||
self.color_capabilities is not None
|
||||
and lighting.Color.ColorCapabilities.Enhanced_hue in self.color_capabilities
|
||||
)
|
||||
|
||||
@property
|
||||
def xy_supported(self) -> bool:
|
||||
"""Return True if the channel supports xy."""
|
||||
return (
|
||||
self.zcl_color_capabilities is not None
|
||||
self.color_capabilities is not None
|
||||
and lighting.Color.ColorCapabilities.XY_attributes
|
||||
in self.zcl_color_capabilities
|
||||
in self.color_capabilities
|
||||
)
|
||||
|
||||
@property
|
||||
def color_temp_supported(self) -> bool:
|
||||
"""Return True if the channel supports color temperature."""
|
||||
return (
|
||||
self.zcl_color_capabilities is not None
|
||||
self.color_capabilities is not None
|
||||
and lighting.Color.ColorCapabilities.Color_temperature
|
||||
in self.zcl_color_capabilities
|
||||
)
|
||||
in self.color_capabilities
|
||||
) or self.color_temperature is not None
|
||||
|
||||
@property
|
||||
def color_loop_supported(self) -> bool:
|
||||
"""Return True if the channel supports color loop."""
|
||||
return (
|
||||
self.zcl_color_capabilities is not None
|
||||
and lighting.Color.ColorCapabilities.Color_loop
|
||||
in self.zcl_color_capabilities
|
||||
self.color_capabilities is not None
|
||||
and lighting.Color.ColorCapabilities.Color_loop in self.color_capabilities
|
||||
)
|
||||
|
@ -4,14 +4,14 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/zha",
|
||||
"requirements": [
|
||||
"bellows==0.31.2",
|
||||
"bellows==0.31.3",
|
||||
"pyserial==3.5",
|
||||
"pyserial-asyncio==0.6",
|
||||
"zha-quirks==0.0.78",
|
||||
"zigpy-deconz==0.18.0",
|
||||
"zigpy==0.48.0",
|
||||
"zigpy==0.49.0",
|
||||
"zigpy-xbee==0.15.0",
|
||||
"zigpy-zigate==0.9.0",
|
||||
"zigpy-zigate==0.9.1",
|
||||
"zigpy-znp==0.8.1"
|
||||
],
|
||||
"usb": [
|
||||
|
@ -7,7 +7,7 @@ from .backports.enum import StrEnum
|
||||
|
||||
MAJOR_VERSION: Final = 2022
|
||||
MINOR_VERSION: Final = 8
|
||||
PATCH_VERSION: Final = "1"
|
||||
PATCH_VERSION: Final = "2"
|
||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0)
|
||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "homeassistant"
|
||||
version = "2022.8.1"
|
||||
version = "2022.8.2"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "Open-source home automation platform running on Python 3."
|
||||
readme = "README.rst"
|
||||
|
@ -37,7 +37,7 @@ PyRMVtransport==0.3.3
|
||||
PySocks==1.7.1
|
||||
|
||||
# homeassistant.components.switchbot
|
||||
PySwitchbot==0.17.3
|
||||
PySwitchbot==0.18.4
|
||||
|
||||
# homeassistant.components.transport_nsw
|
||||
PyTransportNSW==0.1.1
|
||||
@ -128,7 +128,7 @@ aioasuswrt==1.4.0
|
||||
aioazuredevops==1.3.5
|
||||
|
||||
# homeassistant.components.baf
|
||||
aiobafi6==0.7.0
|
||||
aiobafi6==0.7.2
|
||||
|
||||
# homeassistant.components.aws
|
||||
aiobotocore==2.1.0
|
||||
@ -396,7 +396,7 @@ beautifulsoup4==4.11.1
|
||||
# beewi_smartclim==0.0.10
|
||||
|
||||
# homeassistant.components.zha
|
||||
bellows==0.31.2
|
||||
bellows==0.31.3
|
||||
|
||||
# homeassistant.components.bmw_connected_drive
|
||||
bimmer_connected==0.10.1
|
||||
@ -769,7 +769,7 @@ gpiozero==1.6.2
|
||||
gps3==0.33.3
|
||||
|
||||
# homeassistant.components.gree
|
||||
greeclimate==1.2.0
|
||||
greeclimate==1.3.0
|
||||
|
||||
# homeassistant.components.greeneye_monitor
|
||||
greeneye_monitor==3.0.3
|
||||
@ -1455,7 +1455,7 @@ pydaikin==2.7.0
|
||||
pydanfossair==0.1.0
|
||||
|
||||
# homeassistant.components.deconz
|
||||
pydeconz==101
|
||||
pydeconz==102
|
||||
|
||||
# homeassistant.components.delijn
|
||||
pydelijn==1.0.0
|
||||
@ -2529,13 +2529,13 @@ zigpy-deconz==0.18.0
|
||||
zigpy-xbee==0.15.0
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy-zigate==0.9.0
|
||||
zigpy-zigate==0.9.1
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy-znp==0.8.1
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy==0.48.0
|
||||
zigpy==0.49.0
|
||||
|
||||
# homeassistant.components.zoneminder
|
||||
zm-py==0.5.2
|
||||
|
@ -33,7 +33,7 @@ PyRMVtransport==0.3.3
|
||||
PySocks==1.7.1
|
||||
|
||||
# homeassistant.components.switchbot
|
||||
PySwitchbot==0.17.3
|
||||
PySwitchbot==0.18.4
|
||||
|
||||
# homeassistant.components.transport_nsw
|
||||
PyTransportNSW==0.1.1
|
||||
@ -115,7 +115,7 @@ aioasuswrt==1.4.0
|
||||
aioazuredevops==1.3.5
|
||||
|
||||
# homeassistant.components.baf
|
||||
aiobafi6==0.7.0
|
||||
aiobafi6==0.7.2
|
||||
|
||||
# homeassistant.components.aws
|
||||
aiobotocore==2.1.0
|
||||
@ -320,7 +320,7 @@ base36==0.1.1
|
||||
beautifulsoup4==4.11.1
|
||||
|
||||
# homeassistant.components.zha
|
||||
bellows==0.31.2
|
||||
bellows==0.31.3
|
||||
|
||||
# homeassistant.components.bmw_connected_drive
|
||||
bimmer_connected==0.10.1
|
||||
@ -564,7 +564,7 @@ googlemaps==2.5.1
|
||||
govee-ble==0.12.6
|
||||
|
||||
# homeassistant.components.gree
|
||||
greeclimate==1.2.0
|
||||
greeclimate==1.3.0
|
||||
|
||||
# homeassistant.components.greeneye_monitor
|
||||
greeneye_monitor==3.0.3
|
||||
@ -1001,7 +1001,7 @@ pycoolmasternet-async==0.1.2
|
||||
pydaikin==2.7.0
|
||||
|
||||
# homeassistant.components.deconz
|
||||
pydeconz==101
|
||||
pydeconz==102
|
||||
|
||||
# homeassistant.components.dexcom
|
||||
pydexcom==0.2.3
|
||||
@ -1703,13 +1703,13 @@ zigpy-deconz==0.18.0
|
||||
zigpy-xbee==0.15.0
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy-zigate==0.9.0
|
||||
zigpy-zigate==0.9.1
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy-znp==0.8.1
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy==0.48.0
|
||||
zigpy==0.49.0
|
||||
|
||||
# homeassistant.components.zwave_js
|
||||
zwave-js-server-python==0.39.0
|
||||
|
@ -10,6 +10,8 @@ import pytest
|
||||
|
||||
from homeassistant.components import bluetooth
|
||||
from homeassistant.components.bluetooth import (
|
||||
SCANNER_WATCHDOG_INTERVAL,
|
||||
SCANNER_WATCHDOG_TIMEOUT,
|
||||
SOURCE_LOCAL,
|
||||
UNAVAILABLE_TRACK_SECONDS,
|
||||
BluetoothChange,
|
||||
@ -1522,3 +1524,57 @@ async def test_invalid_dbus_message(hass, caplog):
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
|
||||
await hass.async_block_till_done()
|
||||
assert "dbus" in caplog.text
|
||||
|
||||
|
||||
async def test_recovery_from_dbus_restart(
|
||||
hass, mock_bleak_scanner_start, enable_bluetooth
|
||||
):
|
||||
"""Test we can recover when DBus gets restarted out from under us."""
|
||||
assert await async_setup_component(hass, bluetooth.DOMAIN, {bluetooth.DOMAIN: {}})
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_bleak_scanner_start.mock_calls) == 1
|
||||
|
||||
start_time_monotonic = 1000
|
||||
scanner = _get_underlying_scanner()
|
||||
mock_discovered = [MagicMock()]
|
||||
type(scanner).discovered_devices = mock_discovered
|
||||
|
||||
# Ensure we don't restart the scanner if we don't need to
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic + 10,
|
||||
):
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(mock_bleak_scanner_start.mock_calls) == 1
|
||||
|
||||
# Fire a callback to reset the timer
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic,
|
||||
):
|
||||
scanner._callback(
|
||||
BLEDevice("44:44:33:11:23:42", "any_name"),
|
||||
AdvertisementData(local_name="any_name"),
|
||||
)
|
||||
|
||||
# Ensure we don't restart the scanner if we don't need to
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic + 20,
|
||||
):
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(mock_bleak_scanner_start.mock_calls) == 1
|
||||
|
||||
# We hit the timer, so we restart the scanner
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic + SCANNER_WATCHDOG_TIMEOUT,
|
||||
):
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(mock_bleak_scanner_start.mock_calls) == 2
|
||||
|
@ -28,7 +28,7 @@ class FakeDiscovery:
|
||||
"""Add an event listener."""
|
||||
self._listeners.append(listener)
|
||||
|
||||
async def scan(self, wait_for: int = 0):
|
||||
async def scan(self, wait_for: int = 0, bcast_ifaces=None):
|
||||
"""Search for devices, return mocked data."""
|
||||
self.scan_count += 1
|
||||
_LOGGER.info("CALLED SCAN %d TIMES", self.scan_count)
|
||||
|
Loading…
x
Reference in New Issue
Block a user