Motionblinds Bluetooth options (#120110)
This commit is contained in:
parent
88039597e5
commit
32a94fc114
5 changed files with 138 additions and 3 deletions
|
@ -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."""
|
||||
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue