mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 08:47:57 +00:00
Add unique id to flic buttons (#61496)
* Use bluetooth address as unique id for flic buttons. * Always lower case address for uid and add tests. * Update test to set up component. * Use format_mac(addr) as unique id. * Only patch pyflic objects and use query entity registry for buttons. * Replace ExitStack with patch.multiple, remove assert_setup_component. * Test binary sensor is present in state machine.
This commit is contained in:
parent
09456925e9
commit
ce138dd30e
@ -4,14 +4,7 @@ from __future__ import annotations
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from pyflic import (
|
||||
ButtonConnectionChannel,
|
||||
ClickType,
|
||||
ConnectionStatus,
|
||||
FlicClient,
|
||||
ScanWizard,
|
||||
ScanWizardResult,
|
||||
)
|
||||
import pyflic
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorEntity
|
||||
@ -24,6 +17,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.device_registry import format_mac
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
@ -75,7 +69,7 @@ def setup_platform(
|
||||
discovery = config.get(CONF_DISCOVERY)
|
||||
|
||||
try:
|
||||
client = FlicClient(host, port)
|
||||
client = pyflic.FlicClient(host, port)
|
||||
except ConnectionRefusedError:
|
||||
_LOGGER.error("Failed to connect to flic server")
|
||||
return
|
||||
@ -105,13 +99,13 @@ def setup_platform(
|
||||
|
||||
def start_scanning(config, add_entities, client):
|
||||
"""Start a new flic client for scanning and connecting to new buttons."""
|
||||
scan_wizard = ScanWizard()
|
||||
scan_wizard = pyflic.ScanWizard()
|
||||
|
||||
def scan_completed_callback(scan_wizard, result, address, name):
|
||||
"""Restart scan wizard to constantly check for new buttons."""
|
||||
if result == ScanWizardResult.WizardSuccess:
|
||||
if result == pyflic.ScanWizardResult.WizardSuccess:
|
||||
_LOGGER.info("Found new button %s", address)
|
||||
elif result != ScanWizardResult.WizardFailedTimeout:
|
||||
elif result != pyflic.ScanWizardResult.WizardFailedTimeout:
|
||||
_LOGGER.warning(
|
||||
"Failed to connect to button %s. Reason: %s", address, result
|
||||
)
|
||||
@ -139,16 +133,17 @@ class FlicButton(BinarySensorEntity):
|
||||
def __init__(self, hass, client, address, timeout, ignored_click_types):
|
||||
"""Initialize the flic button."""
|
||||
|
||||
self._attr_unique_id = format_mac(address)
|
||||
self._hass = hass
|
||||
self._address = address
|
||||
self._timeout = timeout
|
||||
self._is_down = False
|
||||
self._ignored_click_types = ignored_click_types or []
|
||||
self._hass_click_types = {
|
||||
ClickType.ButtonClick: CLICK_TYPE_SINGLE,
|
||||
ClickType.ButtonSingleClick: CLICK_TYPE_SINGLE,
|
||||
ClickType.ButtonDoubleClick: CLICK_TYPE_DOUBLE,
|
||||
ClickType.ButtonHold: CLICK_TYPE_HOLD,
|
||||
pyflic.ClickType.ButtonClick: CLICK_TYPE_SINGLE,
|
||||
pyflic.ClickType.ButtonSingleClick: CLICK_TYPE_SINGLE,
|
||||
pyflic.ClickType.ButtonDoubleClick: CLICK_TYPE_DOUBLE,
|
||||
pyflic.ClickType.ButtonHold: CLICK_TYPE_HOLD,
|
||||
}
|
||||
|
||||
self._channel = self._create_channel()
|
||||
@ -156,7 +151,7 @@ class FlicButton(BinarySensorEntity):
|
||||
|
||||
def _create_channel(self):
|
||||
"""Create a new connection channel to the button."""
|
||||
channel = ButtonConnectionChannel(self._address)
|
||||
channel = pyflic.ButtonConnectionChannel(self._address)
|
||||
channel.on_button_up_or_down = self._on_up_down
|
||||
|
||||
# If all types of clicks should be ignored, skip registering callbacks
|
||||
@ -225,7 +220,7 @@ class FlicButton(BinarySensorEntity):
|
||||
if was_queued and self._queued_event_check(click_type, time_diff):
|
||||
return
|
||||
|
||||
self._is_down = click_type == ClickType.ButtonDown
|
||||
self._is_down = click_type == pyflic.ClickType.ButtonDown
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def _on_click(self, channel, click_type, was_queued, time_diff):
|
||||
@ -251,7 +246,7 @@ class FlicButton(BinarySensorEntity):
|
||||
|
||||
def _connection_status_changed(self, channel, connection_status, disconnect_reason):
|
||||
"""Remove device, if button disconnects."""
|
||||
if connection_status == ConnectionStatus.Disconnected:
|
||||
if connection_status == pyflic.ConnectionStatus.Disconnected:
|
||||
_LOGGER.warning(
|
||||
"Button (%s) disconnected. Reason: %s", self.address, disconnect_reason
|
||||
)
|
||||
|
@ -916,6 +916,9 @@ pyfido==2.1.1
|
||||
# homeassistant.components.fireservicerota
|
||||
pyfireservicerota==0.0.43
|
||||
|
||||
# homeassistant.components.flic
|
||||
pyflic==2.0.3
|
||||
|
||||
# homeassistant.components.flume
|
||||
pyflume==0.6.5
|
||||
|
||||
|
1
tests/components/flic/__init__.py
Normal file
1
tests/components/flic/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Tests for the flic integration."""
|
63
tests/components/flic/test_binary_sensor.py
Normal file
63
tests/components/flic/test_binary_sensor.py
Normal file
@ -0,0 +1,63 @@
|
||||
"""Tests for Flic button integration."""
|
||||
from unittest import mock
|
||||
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
||||
class _MockFlicClient:
|
||||
def __init__(self, button_addresses):
|
||||
self.addresses = button_addresses
|
||||
self.get_info_callback = None
|
||||
self.scan_wizard = None
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def get_info(self, callback):
|
||||
self.get_info_callback = callback
|
||||
callback({"bd_addr_of_verified_buttons": self.addresses})
|
||||
|
||||
def handle_events(self):
|
||||
pass
|
||||
|
||||
def add_scan_wizard(self, wizard):
|
||||
self.scan_wizard = wizard
|
||||
|
||||
def add_connection_channel(self, channel):
|
||||
self.channel = channel
|
||||
|
||||
|
||||
async def test_button_uid(hass):
|
||||
"""Test UID assignment for Flic buttons."""
|
||||
address_to_name = {
|
||||
"80:e4:da:78:6e:11": "binary_sensor.flic_80e4da786e11",
|
||||
# Uppercase address should not change uid.
|
||||
"80:E4:DA:78:6E:12": "binary_sensor.flic_80e4da786e12",
|
||||
}
|
||||
|
||||
flic_client = _MockFlicClient(tuple(address_to_name))
|
||||
|
||||
with mock.patch.multiple(
|
||||
"pyflic",
|
||||
FlicClient=lambda _, __: flic_client,
|
||||
ButtonConnectionChannel=mock.DEFAULT,
|
||||
ScanWizard=mock.DEFAULT,
|
||||
):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
"binary_sensor",
|
||||
{"binary_sensor": [{"platform": "flic"}]},
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
for address, name in address_to_name.items():
|
||||
state = hass.states.get(name)
|
||||
assert state
|
||||
assert state.attributes.get("address") == address
|
||||
|
||||
entry = entity_registry.async_get(name)
|
||||
assert entry
|
||||
assert entry.unique_id == address.lower()
|
Loading…
x
Reference in New Issue
Block a user