diff --git a/homeassistant/components/flic/binary_sensor.py b/homeassistant/components/flic/binary_sensor.py index 35398c99502..b0cac4ee1b5 100644 --- a/homeassistant/components/flic/binary_sensor.py +++ b/homeassistant/components/flic/binary_sensor.py @@ -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 ) diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a2f5d4503f0..be32d997470 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -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 diff --git a/tests/components/flic/__init__.py b/tests/components/flic/__init__.py new file mode 100644 index 00000000000..0e92d271a93 --- /dev/null +++ b/tests/components/flic/__init__.py @@ -0,0 +1 @@ +"""Tests for the flic integration.""" diff --git a/tests/components/flic/test_binary_sensor.py b/tests/components/flic/test_binary_sensor.py new file mode 100644 index 00000000000..463dbf4a9d7 --- /dev/null +++ b/tests/components/flic/test_binary_sensor.py @@ -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()