Update PySwitchbot to improve connection reliability (#75692)

This commit is contained in:
J. Nick Koston 2022-07-24 16:38:07 -05:00 committed by GitHub
parent c9ae409d9a
commit d890598da7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 16 additions and 80 deletions

View File

@ -1,9 +1,6 @@
"""Support for Switchbot devices.""" """Support for Switchbot devices."""
from collections.abc import Mapping
import logging import logging
from types import MappingProxyType
from typing import Any
import switchbot import switchbot
@ -24,11 +21,8 @@ from .const import (
ATTR_BOT, ATTR_BOT,
ATTR_CURTAIN, ATTR_CURTAIN,
ATTR_HYGROMETER, ATTR_HYGROMETER,
COMMON_OPTIONS,
CONF_RETRY_COUNT, CONF_RETRY_COUNT,
CONF_RETRY_TIMEOUT,
DEFAULT_RETRY_COUNT, DEFAULT_RETRY_COUNT,
DEFAULT_RETRY_TIMEOUT,
DOMAIN, DOMAIN,
) )
from .coordinator import SwitchbotDataUpdateCoordinator from .coordinator import SwitchbotDataUpdateCoordinator
@ -49,8 +43,6 @@ _LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Switchbot from a config entry.""" """Set up Switchbot from a config entry."""
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
domain_data = hass.data[DOMAIN]
if CONF_ADDRESS not in entry.data and CONF_MAC in entry.data: if CONF_ADDRESS not in entry.data and CONF_MAC in entry.data:
# Bleak uses addresses not mac addresses which are are actually # Bleak uses addresses not mac addresses which are are actually
# UUIDs on some platforms (MacOS). # UUIDs on some platforms (MacOS).
@ -65,10 +57,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if not entry.options: if not entry.options:
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
entry, entry,
options={ options={CONF_RETRY_COUNT: DEFAULT_RETRY_COUNT},
CONF_RETRY_COUNT: DEFAULT_RETRY_COUNT,
CONF_RETRY_TIMEOUT: DEFAULT_RETRY_TIMEOUT,
},
) )
sensor_type: str = entry.data[CONF_SENSOR_TYPE] sensor_type: str = entry.data[CONF_SENSOR_TYPE]
@ -78,13 +67,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
raise ConfigEntryNotReady( raise ConfigEntryNotReady(
f"Could not find Switchbot {sensor_type} with address {address}" f"Could not find Switchbot {sensor_type} with address {address}"
) )
if COMMON_OPTIONS not in domain_data:
domain_data[COMMON_OPTIONS] = entry.options
common_options: Mapping[str, int] = domain_data[COMMON_OPTIONS]
switchbot.DEFAULT_RETRY_TIMEOUT = common_options[CONF_RETRY_TIMEOUT]
cls = CLASS_BY_DEVICE.get(sensor_type, switchbot.SwitchbotDevice) cls = CLASS_BY_DEVICE.get(sensor_type, switchbot.SwitchbotDevice)
device = cls( device = cls(
device=ble_device, device=ble_device,
@ -92,7 +74,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
retry_count=entry.options[CONF_RETRY_COUNT], retry_count=entry.options[CONF_RETRY_COUNT],
) )
coordinator = hass.data[DOMAIN][entry.entry_id] = SwitchbotDataUpdateCoordinator( coordinator = hass.data[DOMAIN][entry.entry_id] = SwitchbotDataUpdateCoordinator(
hass, _LOGGER, ble_device, device, common_options hass, _LOGGER, ble_device, device
) )
entry.async_on_unload(coordinator.async_start()) entry.async_on_unload(coordinator.async_start())
if not await coordinator.async_wait_ready(): if not await coordinator.async_wait_ready():
@ -106,6 +88,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return True return True
async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle options update."""
await hass.config_entries.async_reload(entry.entry_id)
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
sensor_type = entry.data[CONF_SENSOR_TYPE] sensor_type = entry.data[CONF_SENSOR_TYPE]
@ -119,11 +106,3 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data.pop(DOMAIN) hass.data.pop(DOMAIN)
return unload_ok return unload_ok
async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle options update."""
# Update entity options stored in hass.
common_options: MappingProxyType[str, Any] = hass.data[DOMAIN][COMMON_OPTIONS]
if entry.options != common_options:
await hass.config_entries.async_reload(entry.entry_id)

View File

@ -17,14 +17,7 @@ from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
from .const import ( from .const import CONF_RETRY_COUNT, DEFAULT_RETRY_COUNT, DOMAIN, SUPPORTED_MODEL_TYPES
CONF_RETRY_COUNT,
CONF_RETRY_TIMEOUT,
DEFAULT_RETRY_COUNT,
DEFAULT_RETRY_TIMEOUT,
DOMAIN,
SUPPORTED_MODEL_TYPES,
)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -140,11 +133,6 @@ class SwitchbotOptionsFlowHandler(OptionsFlow):
"""Manage Switchbot options.""" """Manage Switchbot options."""
if user_input is not None: if user_input is not None:
# Update common entity options for all other entities. # Update common entity options for all other entities.
for entry in self.hass.config_entries.async_entries(DOMAIN):
if entry.unique_id != self.config_entry.unique_id:
self.hass.config_entries.async_update_entry(
entry, options=user_input
)
return self.async_create_entry(title="", data=user_input) return self.async_create_entry(title="", data=user_input)
options = { options = {
@ -153,13 +141,7 @@ class SwitchbotOptionsFlowHandler(OptionsFlow):
default=self.config_entry.options.get( default=self.config_entry.options.get(
CONF_RETRY_COUNT, DEFAULT_RETRY_COUNT CONF_RETRY_COUNT, DEFAULT_RETRY_COUNT
), ),
): int, ): int
vol.Optional(
CONF_RETRY_TIMEOUT,
default=self.config_entry.options.get(
CONF_RETRY_TIMEOUT, DEFAULT_RETRY_TIMEOUT
),
): int,
} }
return self.async_show_form(step_id="init", data_schema=vol.Schema(options)) return self.async_show_form(step_id="init", data_schema=vol.Schema(options))

View File

@ -15,15 +15,11 @@ SUPPORTED_MODEL_TYPES = {
# Config Defaults # Config Defaults
DEFAULT_RETRY_COUNT = 3 DEFAULT_RETRY_COUNT = 3
DEFAULT_RETRY_TIMEOUT = 5
# Config Options # Config Options
CONF_RETRY_COUNT = "retry_count" CONF_RETRY_COUNT = "retry_count"
CONF_SCAN_TIMEOUT = "scan_timeout"
# Deprecated config Entry Options to be removed in 2023.4 # Deprecated config Entry Options to be removed in 2023.4
CONF_TIME_BETWEEN_UPDATE_COMMAND = "update_time" CONF_TIME_BETWEEN_UPDATE_COMMAND = "update_time"
CONF_RETRY_TIMEOUT = "retry_timeout" CONF_RETRY_TIMEOUT = "retry_timeout"
CONF_SCAN_TIMEOUT = "scan_timeout"
# Data
COMMON_OPTIONS = "common_options"

View File

@ -2,7 +2,6 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
from collections.abc import Mapping
import logging import logging
from typing import Any, cast from typing import Any, cast
@ -16,8 +15,6 @@ from homeassistant.components.bluetooth.passive_update_coordinator import (
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from .const import CONF_RETRY_COUNT
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -38,21 +35,14 @@ class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator):
logger: logging.Logger, logger: logging.Logger,
ble_device: BLEDevice, ble_device: BLEDevice,
device: switchbot.SwitchbotDevice, device: switchbot.SwitchbotDevice,
common_options: Mapping[str, int],
) -> None: ) -> None:
"""Initialize global switchbot data updater.""" """Initialize global switchbot data updater."""
super().__init__(hass, logger, ble_device.address) super().__init__(hass, logger, ble_device.address)
self.ble_device = ble_device self.ble_device = ble_device
self.device = device self.device = device
self.common_options = common_options
self.data: dict[str, Any] = {} self.data: dict[str, Any] = {}
self._ready_event = asyncio.Event() self._ready_event = asyncio.Event()
@property
def retry_count(self) -> int:
"""Return retry count."""
return self.common_options[CONF_RETRY_COUNT]
@callback @callback
def _async_handle_bluetooth_event( def _async_handle_bluetooth_event(
self, self,

View File

@ -2,7 +2,7 @@
"domain": "switchbot", "domain": "switchbot",
"name": "SwitchBot", "name": "SwitchBot",
"documentation": "https://www.home-assistant.io/integrations/switchbot", "documentation": "https://www.home-assistant.io/integrations/switchbot",
"requirements": ["PySwitchbot==0.15.0"], "requirements": ["PySwitchbot==0.15.1"],
"config_flow": true, "config_flow": true,
"dependencies": ["bluetooth"], "dependencies": ["bluetooth"],
"codeowners": ["@danielhiversen", "@RenierM26", "@murtas"], "codeowners": ["@danielhiversen", "@RenierM26", "@murtas"],

View File

@ -24,8 +24,7 @@
"step": { "step": {
"init": { "init": {
"data": { "data": {
"retry_count": "Retry count", "retry_count": "Retry count"
"retry_timeout": "Timeout between retries"
} }
} }
} }

View File

@ -24,8 +24,7 @@
"step": { "step": {
"init": { "init": {
"data": { "data": {
"retry_count": "Retry count", "retry_count": "Retry count"
"retry_timeout": "Timeout between retries"
} }
} }
} }

View File

@ -37,7 +37,7 @@ PyRMVtransport==0.3.3
PySocks==1.7.1 PySocks==1.7.1
# homeassistant.components.switchbot # homeassistant.components.switchbot
PySwitchbot==0.15.0 PySwitchbot==0.15.1
# homeassistant.components.transport_nsw # homeassistant.components.transport_nsw
PyTransportNSW==0.1.1 PyTransportNSW==0.1.1

View File

@ -33,7 +33,7 @@ PyRMVtransport==0.3.3
PySocks==1.7.1 PySocks==1.7.1
# homeassistant.components.switchbot # homeassistant.components.switchbot
PySwitchbot==0.15.0 PySwitchbot==0.15.1
# homeassistant.components.transport_nsw # homeassistant.components.transport_nsw
PyTransportNSW==0.1.1 PyTransportNSW==0.1.1

View File

@ -2,10 +2,7 @@
from unittest.mock import patch from unittest.mock import patch
from homeassistant.components.switchbot.const import ( from homeassistant.components.switchbot.const import CONF_RETRY_COUNT
CONF_RETRY_COUNT,
CONF_RETRY_TIMEOUT,
)
from homeassistant.config_entries import SOURCE_BLUETOOTH, SOURCE_USER from homeassistant.config_entries import SOURCE_BLUETOOTH, SOURCE_USER
from homeassistant.const import CONF_ADDRESS, CONF_NAME, CONF_PASSWORD, CONF_SENSOR_TYPE from homeassistant.const import CONF_ADDRESS, CONF_NAME, CONF_PASSWORD, CONF_SENSOR_TYPE
from homeassistant.data_entry_flow import FlowResultType from homeassistant.data_entry_flow import FlowResultType
@ -276,7 +273,6 @@ async def test_options_flow(hass):
}, },
options={ options={
CONF_RETRY_COUNT: 10, CONF_RETRY_COUNT: 10,
CONF_RETRY_TIMEOUT: 10,
}, },
unique_id="aabbccddeeff", unique_id="aabbccddeeff",
) )
@ -294,14 +290,12 @@ async def test_options_flow(hass):
result["flow_id"], result["flow_id"],
user_input={ user_input={
CONF_RETRY_COUNT: 3, CONF_RETRY_COUNT: 3,
CONF_RETRY_TIMEOUT: 5,
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert result["type"] == FlowResultType.CREATE_ENTRY assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["data"][CONF_RETRY_COUNT] == 3 assert result["data"][CONF_RETRY_COUNT] == 3
assert result["data"][CONF_RETRY_TIMEOUT] == 5
assert len(mock_setup_entry.mock_calls) == 2 assert len(mock_setup_entry.mock_calls) == 2
@ -319,16 +313,13 @@ async def test_options_flow(hass):
result["flow_id"], result["flow_id"],
user_input={ user_input={
CONF_RETRY_COUNT: 6, CONF_RETRY_COUNT: 6,
CONF_RETRY_TIMEOUT: 6,
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert result["type"] == FlowResultType.CREATE_ENTRY assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["data"][CONF_RETRY_COUNT] == 6 assert result["data"][CONF_RETRY_COUNT] == 6
assert result["data"][CONF_RETRY_TIMEOUT] == 6
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
assert entry.options[CONF_RETRY_COUNT] == 6 assert entry.options[CONF_RETRY_COUNT] == 6
assert entry.options[CONF_RETRY_TIMEOUT] == 6