Motionblinds Bluetooth options (#120110)

This commit is contained in:
Lenn 2024-06-22 09:13:14 +02:00 committed by GitHub
parent 88039597e5
commit 32a94fc114
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 138 additions and 3 deletions

View file

@ -24,7 +24,13 @@ from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.typing import ConfigType
from .const import CONF_BLIND_TYPE, CONF_MAC_CODE, DOMAIN
from .const import (
CONF_BLIND_TYPE,
CONF_MAC_CODE,
DOMAIN,
OPTION_DISCONNECT_TIME,
OPTION_PERMANENT_CONNECTION,
)
_LOGGER = logging.getLogger(__name__)
@ -86,13 +92,40 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = device
# Register OptionsFlow update listener
entry.async_on_unload(entry.add_update_listener(options_update_listener))
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
# Apply options
entry.async_create_background_task(
hass, apply_options(hass, entry), device.ble_device.address
)
_LOGGER.debug("(%s) Finished setting up device", entry.data[CONF_MAC_CODE])
return True
async def options_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle options update."""
_LOGGER.debug(
"(%s) Updated device options: %s", entry.data[CONF_MAC_CODE], entry.options
)
await apply_options(hass, entry)
async def apply_options(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Apply the options from the OptionsFlow."""
device: MotionDevice = hass.data[DOMAIN][entry.entry_id]
disconnect_time: float | None = entry.options.get(OPTION_DISCONNECT_TIME, None)
permanent_connection: bool = entry.options.get(OPTION_PERMANENT_CONNECTION, False)
device.set_custom_disconnect_time(disconnect_time)
await device.set_permanent_connection(permanent_connection)
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload Motionblinds Bluetooth device from a config entry."""

View file

@ -7,13 +7,19 @@ import re
from typing import TYPE_CHECKING, Any
from bleak.backends.device import BLEDevice
from motionblindsble.const import DISPLAY_NAME, MotionBlindType
from motionblindsble.const import DISPLAY_NAME, SETTING_DISCONNECT_TIME, MotionBlindType
import voluptuous as vol
from homeassistant.components import bluetooth
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
)
from homeassistant.const import CONF_ADDRESS
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.selector import (
SelectSelector,
@ -30,6 +36,8 @@ from .const import (
ERROR_INVALID_MAC_CODE,
ERROR_NO_BLUETOOTH_ADAPTER,
ERROR_NO_DEVICES_FOUND,
OPTION_DISCONNECT_TIME,
OPTION_PERMANENT_CONNECTION,
)
_LOGGER = logging.getLogger(__name__)
@ -174,6 +182,53 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
self._mac_code = mac_code.upper()
self._display_name = DISPLAY_NAME.format(mac_code=self._mac_code)
@staticmethod
@callback
def async_get_options_flow(
config_entry: ConfigEntry,
) -> OptionsFlow:
"""Create the options flow."""
return OptionsFlowHandler(config_entry)
class OptionsFlowHandler(OptionsFlow):
"""Handle an options flow for Motionblinds BLE."""
def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Manage the options."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Required(
OPTION_PERMANENT_CONNECTION,
default=(
self.config_entry.options.get(
OPTION_PERMANENT_CONNECTION, False
)
),
): bool,
vol.Optional(
OPTION_DISCONNECT_TIME,
default=(
self.config_entry.options.get(
OPTION_DISCONNECT_TIME, SETTING_DISCONNECT_TIME
)
),
): vol.All(vol.Coerce(int), vol.Range(min=0)),
}
),
)
def is_valid_mac(data: str) -> bool:
"""Validate the provided MAC address."""

View file

@ -19,3 +19,6 @@ ERROR_NO_DEVICES_FOUND = "no_devices_found"
ICON_VERTICAL_BLIND = "mdi:blinds-vertical-closed"
MANUFACTURER = "Motionblinds"
OPTION_DISCONNECT_TIME = "disconnect_time"
OPTION_PERMANENT_CONNECTION = "permanent_connection"

View file

@ -20,6 +20,18 @@
}
}
},
"options": {
"step": {
"init": {
"title": "Connection options",
"description": "The default disconnect time is 15 seconds, adjustable using the slider below. You may want to adjust this if you have larger blinds or other specific needs. You can also enable a permanent connection to the motor, which disables the disconnect time and automatically reconnects when the motor is disconnected for any reason.\n**WARNING**: Changing any of the below options may significantly reduce battery life of your motor!",
"data": {
"permanent_connection": "Permanent connection",
"disconnect_time": "Disconnect time (seconds)"
}
}
}
},
"selector": {
"blind_type": {
"options": {

View file

@ -14,6 +14,7 @@ from homeassistant.data_entry_flow import FlowResultType
from .conftest import TEST_ADDRESS, TEST_MAC, TEST_NAME
from tests.common import MockConfigEntry
from tests.components.bluetooth import generate_advertisement_data, generate_ble_device
TEST_BLIND_TYPE = MotionBlindType.ROLLER.name.lower()
@ -255,3 +256,34 @@ async def test_config_flow_bluetooth_success(hass: HomeAssistant) -> None:
const.CONF_BLIND_TYPE: TEST_BLIND_TYPE,
}
assert result["options"] == {}
async def test_options_flow(hass: HomeAssistant) -> None:
"""Test the options flow."""
entry = MockConfigEntry(
domain=const.DOMAIN,
unique_id="0123456789",
data={
const.CONF_BLIND_TYPE: MotionBlindType.ROLLER,
},
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
result = await hass.config_entries.options.async_init(entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "init"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
const.OPTION_PERMANENT_CONNECTION: True,
const.OPTION_DISCONNECT_TIME: 10,
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY