Compare commits
19 commits
dev
...
lutron_but
Author | SHA1 | Date | |
---|---|---|---|
|
3e5d677047 | ||
|
c2abe47ede | ||
|
1186d9d07f | ||
|
2760bab3ba | ||
|
5ad70cdcc0 | ||
|
cd04d6f246 | ||
|
ca70853488 | ||
|
02ef593c58 | ||
|
8fe5eebc5d | ||
|
c5913fdfa8 | ||
|
f496fdd15f | ||
|
148bb05dea | ||
|
4d01e0a773 | ||
|
15d8b84074 | ||
|
2208262ca5 | ||
|
0c2a0118e2 | ||
|
93fe46509f | ||
|
581efad5a7 | ||
|
0492639d51 |
9 changed files with 289 additions and 106 deletions
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||
|
||||
import asyncio
|
||||
import contextlib
|
||||
from functools import partial
|
||||
from itertools import chain
|
||||
import logging
|
||||
import ssl
|
||||
|
@ -14,26 +15,19 @@ from pylutron_caseta.smartbridge import Smartbridge
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import ATTR_DEVICE_ID, ATTR_SUGGESTED_AREA, CONF_HOST, Platform
|
||||
from homeassistant.const import ATTR_SUGGESTED_AREA, CONF_HOST, Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import (
|
||||
ACTION_PRESS,
|
||||
ACTION_RELEASE,
|
||||
ATTR_ACTION,
|
||||
ATTR_AREA_NAME,
|
||||
ATTR_BUTTON_NUMBER,
|
||||
ATTR_BUTTON_TYPE,
|
||||
ATTR_DEVICE_NAME,
|
||||
ATTR_LEAP_BUTTON_NUMBER,
|
||||
ATTR_SERIAL,
|
||||
ATTR_TYPE,
|
||||
BRIDGE_DEVICE_ID,
|
||||
BRIDGE_TIMEOUT,
|
||||
CONF_CA_CERTS,
|
||||
|
@ -63,6 +57,8 @@ from .models import (
|
|||
LUTRON_KEYPAD_SERIAL,
|
||||
LUTRON_KEYPAD_TYPE,
|
||||
LutronButton,
|
||||
LutronCasetaButtonActionData,
|
||||
LutronCasetaButtonDevice,
|
||||
LutronCasetaConfigEntry,
|
||||
LutronCasetaData,
|
||||
LutronKeypad,
|
||||
|
@ -200,14 +196,72 @@ async def async_setup_entry(
|
|||
|
||||
# Store this bridge (keyed by entry_id) so it can be retrieved by the
|
||||
# platforms we're setting up.
|
||||
button_devices = _async_build_button_devices(bridge, keypad_data)
|
||||
|
||||
entry.runtime_data = LutronCasetaData(bridge, bridge_device, keypad_data)
|
||||
entry.runtime_data = LutronCasetaData(
|
||||
bridge, bridge_device, keypad_data, button_devices
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@callback
|
||||
def _async_build_button_devices(
|
||||
bridge: Smartbridge, keypad_data: LutronKeypadData
|
||||
) -> list[LutronCasetaButtonDevice]:
|
||||
button_devices = bridge.get_buttons()
|
||||
all_devices = bridge.get_devices()
|
||||
keypads = keypad_data.keypads
|
||||
buttons: list[LutronCasetaButtonDevice] = []
|
||||
|
||||
for button_id, device in button_devices.items():
|
||||
parent_keypad = keypads[device["parent_device"]]
|
||||
parent_device_info = parent_keypad["device_info"]
|
||||
parent_name = parent_device_info["name"]
|
||||
|
||||
button_key: str | None = None
|
||||
button_name: str
|
||||
device_name = cast(str | None, device.get("device_name"))
|
||||
user_defined_name = bool(device_name)
|
||||
if device_name:
|
||||
button_name = device_name
|
||||
else:
|
||||
# device name (button name) is missing, probably a caseta pico
|
||||
# try to get the name using the button number from the triggers
|
||||
# disable the button by default
|
||||
keypad_device = all_devices[device["parent_device"]]
|
||||
button_numbers = LEAP_TO_DEVICE_TYPE_SUBTYPE_MAP.get(
|
||||
keypad_device["type"],
|
||||
{},
|
||||
)
|
||||
button_key = button_numbers.get(int(device["button_number"]))
|
||||
button_name = (
|
||||
button_key
|
||||
or f"button {device['button_number']}".replace("_", " ").title()
|
||||
)
|
||||
|
||||
# Append the child device name to the end of the parent keypad
|
||||
# name to create the entity name
|
||||
full_name = f"{parent_name} {button_name}"
|
||||
# Set the device_info to the same as the Parent Keypad
|
||||
# The entities will be nested inside the keypad device
|
||||
buttons.append(
|
||||
LutronCasetaButtonDevice(
|
||||
button_id,
|
||||
device,
|
||||
button_key,
|
||||
button_name,
|
||||
full_name,
|
||||
user_defined_name,
|
||||
parent_device_info,
|
||||
),
|
||||
)
|
||||
|
||||
return buttons
|
||||
|
||||
|
||||
@callback
|
||||
def _async_register_bridge_device(
|
||||
hass: HomeAssistant, config_entry_id: str, bridge_device: dict, bridge: Smartbridge
|
||||
|
@ -301,6 +355,7 @@ def _async_setup_keypads(
|
|||
|
||||
_async_subscribe_keypad_events(
|
||||
hass=hass,
|
||||
config_entry_id=config_entry_id,
|
||||
bridge=bridge,
|
||||
keypads=keypads,
|
||||
keypad_buttons=keypad_buttons,
|
||||
|
@ -440,15 +495,16 @@ def async_get_lip_button(device_type: str, leap_button: int) -> int | None:
|
|||
@callback
|
||||
def _async_subscribe_keypad_events(
|
||||
hass: HomeAssistant,
|
||||
config_entry_id: str,
|
||||
bridge: Smartbridge,
|
||||
keypads: dict[int, LutronKeypad],
|
||||
keypad_buttons: dict[int, LutronButton],
|
||||
leap_to_keypad_button_names: dict[int, dict[int, str]],
|
||||
):
|
||||
) -> None:
|
||||
"""Subscribe to lutron events."""
|
||||
|
||||
@callback
|
||||
def _async_button_event(button_id, event_type):
|
||||
def _async_button_event(button_id: int, event_type: str) -> None:
|
||||
if not (button := keypad_buttons.get(button_id)) or not (
|
||||
keypad := keypads.get(button["parent_keypad"])
|
||||
):
|
||||
|
@ -467,27 +523,24 @@ def _async_subscribe_keypad_events(
|
|||
keypad_type, leap_to_keypad_button_names[keypad_device_id]
|
||||
)[leap_button_number]
|
||||
|
||||
hass.bus.async_fire(
|
||||
LUTRON_CASETA_BUTTON_EVENT,
|
||||
{
|
||||
ATTR_SERIAL: keypad[LUTRON_KEYPAD_SERIAL],
|
||||
ATTR_TYPE: keypad_type,
|
||||
ATTR_BUTTON_NUMBER: lip_button_number,
|
||||
ATTR_LEAP_BUTTON_NUMBER: leap_button_number,
|
||||
ATTR_DEVICE_NAME: keypad[LUTRON_KEYPAD_NAME],
|
||||
ATTR_DEVICE_ID: keypad[LUTRON_KEYPAD_DEVICE_REGISTRY_DEVICE_ID],
|
||||
ATTR_AREA_NAME: keypad[LUTRON_KEYPAD_AREA_NAME],
|
||||
ATTR_BUTTON_TYPE: button_type,
|
||||
ATTR_ACTION: action,
|
||||
},
|
||||
data = LutronCasetaButtonActionData(
|
||||
serial=keypad[LUTRON_KEYPAD_SERIAL],
|
||||
type=keypad_type,
|
||||
button_number=lip_button_number,
|
||||
leap_button_number=leap_button_number,
|
||||
device_name=keypad[LUTRON_KEYPAD_NAME],
|
||||
device_id=keypad[LUTRON_KEYPAD_DEVICE_REGISTRY_DEVICE_ID],
|
||||
area_name=keypad[LUTRON_KEYPAD_AREA_NAME],
|
||||
button_type=button_type,
|
||||
action=action,
|
||||
)
|
||||
signal = f"{DOMAIN}_{config_entry_id}_button_{button_id}"
|
||||
async_dispatcher_send(hass, signal, data)
|
||||
hass.bus.async_fire(LUTRON_CASETA_BUTTON_EVENT, data)
|
||||
|
||||
for button_id in keypad_buttons:
|
||||
bridge.add_button_subscriber(
|
||||
str(button_id),
|
||||
lambda event_type, button_id=button_id: _async_button_event(
|
||||
button_id, event_type
|
||||
),
|
||||
str(button_id), partial(_async_button_event, button_id)
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -7,13 +7,19 @@ from homeassistant.components.binary_sensor import (
|
|||
BinarySensorEntity,
|
||||
)
|
||||
from homeassistant.const import ATTR_SUGGESTED_AREA
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import DOMAIN as CASETA_DOMAIN, LutronCasetaDevice, _area_name_from_id
|
||||
from .const import CONFIG_URL, MANUFACTURER, UNASSIGNED_AREA
|
||||
from .models import LutronCasetaConfigEntry
|
||||
from .const import ACTION_PRESS, CONFIG_URL, DOMAIN, MANUFACTURER, UNASSIGNED_AREA
|
||||
from .models import (
|
||||
LutronCasetaButtonActionData,
|
||||
LutronCasetaButtonDevice,
|
||||
LutronCasetaConfigEntry,
|
||||
LutronCasetaData,
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
|
@ -29,10 +35,15 @@ async def async_setup_entry(
|
|||
data = config_entry.runtime_data
|
||||
bridge = data.bridge
|
||||
occupancy_groups = bridge.occupancy_groups
|
||||
async_add_entities(
|
||||
entities: list[BinarySensorEntity] = [
|
||||
LutronOccupancySensor(occupancy_group, data)
|
||||
for occupancy_group in occupancy_groups.values()
|
||||
]
|
||||
entities.extend(
|
||||
LutronCasetaButtonBinarySensor(data, button_device, config_entry.entry_id)
|
||||
for button_device in data.button_devices
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class LutronOccupancySensor(LutronCasetaDevice, BinarySensorEntity):
|
||||
|
@ -84,3 +95,54 @@ class LutronOccupancySensor(LutronCasetaDevice, BinarySensorEntity):
|
|||
def extra_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
return {"device_id": self.device_id}
|
||||
|
||||
|
||||
class LutronCasetaButtonBinarySensor(LutronCasetaDevice, BinarySensorEntity):
|
||||
"""Representation of a Lutron pico and keypad button action.
|
||||
|
||||
Lutron buttons send a press and a release action which means
|
||||
they have a duration to the press action. Because they have
|
||||
a duration, they are represented as binary sensors and not events.
|
||||
"""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_is_on = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data: LutronCasetaData,
|
||||
button_device: LutronCasetaButtonDevice,
|
||||
entry_id: str,
|
||||
) -> None:
|
||||
"""Init a button binary_sensor entity."""
|
||||
super().__init__(button_device.device, data)
|
||||
if translation_key := button_device.button_key:
|
||||
self._attr_translation_key = translation_key
|
||||
else:
|
||||
# User defined name
|
||||
self._attr_name = button_device.button_name
|
||||
self._attr_device_info = button_device.parent_device_info
|
||||
self._button_id = button_device.button_id
|
||||
self._entry_id = entry_id
|
||||
|
||||
@property
|
||||
def serial(self):
|
||||
"""Buttons shouldn't have serial numbers, Return None."""
|
||||
return None
|
||||
|
||||
@callback
|
||||
def _async_handle_button_action(self, data: LutronCasetaButtonActionData) -> None:
|
||||
"""Handle a button event."""
|
||||
self._attr_is_on = data["action"] == ACTION_PRESS
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to button actions."""
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
f"{DOMAIN}_{self._entry_id}_button_{self._button_id}",
|
||||
self._async_handle_button_action,
|
||||
)
|
||||
)
|
||||
await super().async_added_to_hass()
|
||||
|
|
|
@ -2,16 +2,12 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.button import ButtonEntity
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import LutronCasetaDevice
|
||||
from .device_trigger import LEAP_TO_DEVICE_TYPE_SUBTYPE_MAP
|
||||
from .models import LutronCasetaConfigEntry, LutronCasetaData
|
||||
from .models import LutronCasetaButtonDevice, LutronCasetaConfigEntry, LutronCasetaData
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
|
@ -21,66 +17,26 @@ async def async_setup_entry(
|
|||
) -> None:
|
||||
"""Set up Lutron pico and keypad buttons."""
|
||||
data = config_entry.runtime_data
|
||||
bridge = data.bridge
|
||||
button_devices = bridge.get_buttons()
|
||||
all_devices = data.bridge.get_devices()
|
||||
keypads = data.keypad_data.keypads
|
||||
entities: list[LutronCasetaButton] = []
|
||||
|
||||
for device in button_devices.values():
|
||||
parent_keypad = keypads[device["parent_device"]]
|
||||
parent_device_info = parent_keypad["device_info"]
|
||||
|
||||
enabled_default = True
|
||||
if not (device_name := device.get("device_name")):
|
||||
# device name (button name) is missing, probably a caseta pico
|
||||
# try to get the name using the button number from the triggers
|
||||
# disable the button by default
|
||||
enabled_default = False
|
||||
keypad_device = all_devices[device["parent_device"]]
|
||||
button_numbers = LEAP_TO_DEVICE_TYPE_SUBTYPE_MAP.get(
|
||||
keypad_device["type"],
|
||||
{},
|
||||
)
|
||||
device_name = (
|
||||
button_numbers.get(
|
||||
int(device["button_number"]),
|
||||
f"button {device['button_number']}",
|
||||
)
|
||||
.replace("_", " ")
|
||||
.title()
|
||||
)
|
||||
|
||||
# Append the child device name to the end of the parent keypad
|
||||
# name to create the entity name
|
||||
full_name = f'{parent_device_info.get("name")} {device_name}'
|
||||
# Set the device_info to the same as the Parent Keypad
|
||||
# The entities will be nested inside the keypad device
|
||||
entities.append(
|
||||
LutronCasetaButton(
|
||||
device, data, full_name, enabled_default, parent_device_info
|
||||
),
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
async_add_entities(
|
||||
LutronCasetaButton(data, button_device) for button_device in data.button_devices
|
||||
)
|
||||
|
||||
|
||||
class LutronCasetaButton(LutronCasetaDevice, ButtonEntity):
|
||||
"""Representation of a Lutron pico and keypad button."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
device: dict[str, Any],
|
||||
data: LutronCasetaData,
|
||||
full_name: str,
|
||||
enabled_default: bool,
|
||||
device_info: DeviceInfo,
|
||||
button_device: LutronCasetaButtonDevice,
|
||||
) -> None:
|
||||
"""Init a button entity."""
|
||||
super().__init__(device, data)
|
||||
self._attr_entity_registry_enabled_default = enabled_default
|
||||
self._attr_name = full_name
|
||||
self._attr_device_info = device_info
|
||||
super().__init__(button_device.device, data)
|
||||
self._attr_entity_registry_enabled_default = button_device.user_defined_name
|
||||
self._attr_name = button_device.button_name
|
||||
self._attr_device_info = button_device.parent_device_info
|
||||
|
||||
async def async_press(self) -> None:
|
||||
"""Send a button press event."""
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
"""Lutron Caseta constants."""
|
||||
|
||||
from typing import Final
|
||||
|
||||
DOMAIN = "lutron_caseta"
|
||||
|
||||
CONF_KEYFILE = "keyfile"
|
||||
|
@ -19,14 +21,14 @@ DEVICE_TYPE_SPECTRUM_TUNE = "SpectrumTune"
|
|||
|
||||
MANUFACTURER = "Lutron Electronics Co., Inc"
|
||||
|
||||
ATTR_SERIAL = "serial"
|
||||
ATTR_TYPE = "type"
|
||||
ATTR_BUTTON_TYPE = "button_type"
|
||||
ATTR_LEAP_BUTTON_NUMBER = "leap_button_number"
|
||||
ATTR_BUTTON_NUMBER = "button_number" # LIP button number
|
||||
ATTR_DEVICE_NAME = "device_name"
|
||||
ATTR_AREA_NAME = "area_name"
|
||||
ATTR_ACTION = "action"
|
||||
ATTR_SERIAL: Final = "serial"
|
||||
ATTR_TYPE: Final = "type"
|
||||
ATTR_BUTTON_TYPE: Final = "button_type"
|
||||
ATTR_LEAP_BUTTON_NUMBER: Final = "leap_button_number"
|
||||
ATTR_BUTTON_NUMBER: Final = "button_number" # LIP button number
|
||||
ATTR_DEVICE_NAME: Final = "device_name"
|
||||
ATTR_AREA_NAME: Final = "area_name"
|
||||
ATTR_ACTION: Final = "action"
|
||||
|
||||
ACTION_PRESS = "press"
|
||||
ACTION_RELEASE = "release"
|
||||
|
|
|
@ -14,16 +14,17 @@ from homeassistant.helpers.device_registry import DeviceInfo
|
|||
type LutronCasetaConfigEntry = ConfigEntry[LutronCasetaData]
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(slots=True)
|
||||
class LutronCasetaData:
|
||||
"""Data for the lutron_caseta integration."""
|
||||
|
||||
bridge: Smartbridge
|
||||
bridge_device: dict[str, Any]
|
||||
keypad_data: LutronKeypadData
|
||||
button_devices: list[LutronCasetaButtonDevice]
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(slots=True)
|
||||
class LutronKeypadData:
|
||||
"""Data for the lutron_caseta integration keypads."""
|
||||
|
||||
|
@ -34,6 +35,33 @@ class LutronKeypadData:
|
|||
trigger_schemas: dict[int, vol.Schema]
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class LutronCasetaButtonDevice:
|
||||
"""A lutron_caseta button device."""
|
||||
|
||||
button_id: int
|
||||
device: dict
|
||||
button_key: str | None
|
||||
button_name: str
|
||||
full_name: str
|
||||
user_defined_name: bool
|
||||
parent_device_info: DeviceInfo
|
||||
|
||||
|
||||
class LutronCasetaButtonActionData(TypedDict):
|
||||
"""A lutron_caseta button event data."""
|
||||
|
||||
serial: str
|
||||
type: str
|
||||
button_number: int | None
|
||||
leap_button_number: int
|
||||
device_name: str
|
||||
device_id: str
|
||||
area_name: str
|
||||
button_type: str
|
||||
action: str
|
||||
|
||||
|
||||
class LutronKeypad(TypedDict):
|
||||
"""A lutron_caseta keypad device."""
|
||||
|
||||
|
|
|
@ -30,6 +30,51 @@
|
|||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"binary_sensor": {
|
||||
"button_1": { "name": "First button" },
|
||||
"button_2": { "name": "Second button" },
|
||||
"button_3": { "name": "Third button" },
|
||||
"button_4": { "name": "Fourth button" },
|
||||
"button_5": { "name": "Fifth button" },
|
||||
"button_6": { "name": "Sixth button" },
|
||||
"button_7": { "name": "Seventh button" },
|
||||
"group_1_button_1": { "name": "First Group first button" },
|
||||
"group_1_button_2": { "name": "First Group second button" },
|
||||
"group_2_button_1": { "name": "Second Group first button" },
|
||||
"group_2_button_2": { "name": "Second Group second button" },
|
||||
"on": { "name": "[%key:common::state::on%]" },
|
||||
"stop": { "name": "Stop (favorite)" },
|
||||
"off": { "name": "[%key:common::state::off%]" },
|
||||
"raise": { "name": "Raise" },
|
||||
"lower": { "name": "Lower" },
|
||||
"open_all": { "name": "Open all" },
|
||||
"stop_all": { "name": "Stop all" },
|
||||
"close_all": { "name": "Close all" },
|
||||
"raise_all": { "name": "Raise all" },
|
||||
"lower_all": { "name": "Lower all" },
|
||||
"open_1": { "name": "Open 1" },
|
||||
"stop_1": { "name": "Stop 1" },
|
||||
"close_1": { "name": "Close 1" },
|
||||
"raise_1": { "name": "Raise 1" },
|
||||
"lower_1": { "name": "Lower 1" },
|
||||
"open_2": { "name": "Open 2" },
|
||||
"stop_2": { "name": "Stop 2" },
|
||||
"close_2": { "name": "Close 2" },
|
||||
"raise_2": { "name": "Raise 2" },
|
||||
"lower_2": { "name": "Lower 2" },
|
||||
"open_3": { "name": "Open 3" },
|
||||
"stop_3": { "name": "Stop 3" },
|
||||
"close_3": { "name": "Close 3" },
|
||||
"raise_3": { "name": "Raise 3" },
|
||||
"lower_3": { "name": "Lower 3" },
|
||||
"open_4": { "name": "Open 4" },
|
||||
"stop_4": { "name": "Stop 4" },
|
||||
"close_4": { "name": "Close 4" },
|
||||
"raise_4": { "name": "Raise 4" },
|
||||
"lower_4": { "name": "Lower 4" }
|
||||
}
|
||||
},
|
||||
"device_automation": {
|
||||
"trigger_subtype": {
|
||||
"button_1": "First button",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""Tests for the Lutron Caseta integration."""
|
||||
|
||||
from collections import defaultdict
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.lutron_caseta import DOMAIN
|
||||
|
@ -84,7 +85,9 @@ _LEAP_DEVICE_TYPES = {
|
|||
}
|
||||
|
||||
|
||||
async def async_setup_integration(hass: HomeAssistant, mock_bridge) -> MockConfigEntry:
|
||||
async def async_setup_integration(
|
||||
hass: HomeAssistant, mock_bridge
|
||||
) -> tuple[MockConfigEntry, "MockBridge"]:
|
||||
"""Set up a mock bridge."""
|
||||
mock_entry = MockConfigEntry(domain=DOMAIN, data=ENTRY_MOCK_DATA)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
@ -92,10 +95,12 @@ async def async_setup_integration(hass: HomeAssistant, mock_bridge) -> MockConfi
|
|||
with patch(
|
||||
"homeassistant.components.lutron_caseta.Smartbridge.create_tls"
|
||||
) as create_tls:
|
||||
create_tls.return_value = mock_bridge(can_connect=True)
|
||||
mocked_bridge = mock_bridge(can_connect=True)
|
||||
create_tls.return_value = mocked_bridge
|
||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
return mock_entry
|
||||
|
||||
return mock_entry, mocked_bridge
|
||||
|
||||
|
||||
class MockBridge:
|
||||
|
@ -110,6 +115,7 @@ class MockBridge:
|
|||
self.scenes = self.get_scenes()
|
||||
self.devices = self.load_devices()
|
||||
self.buttons = self.load_buttons()
|
||||
self.button_subscribers: defaultdict[str, list] = defaultdict(list)
|
||||
|
||||
async def connect(self):
|
||||
"""Connect the mock bridge."""
|
||||
|
@ -121,6 +127,7 @@ class MockBridge:
|
|||
|
||||
def add_button_subscriber(self, button_id: str, callback_):
|
||||
"""Mock a listener for button presses."""
|
||||
self.button_subscribers[button_id].append(callback_)
|
||||
|
||||
def is_connected(self):
|
||||
"""Return whether the mock bridge is connected."""
|
||||
|
|
31
tests/components/lutron_caseta/test_binary_sensor.py
Normal file
31
tests/components/lutron_caseta/test_binary_sensor.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
"""Tests for the Lutron Caseta integration."""
|
||||
|
||||
from homeassistant.const import STATE_OFF, STATE_ON
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import MockBridge, async_setup_integration
|
||||
|
||||
|
||||
async def test_button_press_and_release(hass: HomeAssistant) -> None:
|
||||
"""Test a button press and delayed release."""
|
||||
_, mock_bridge = await async_setup_integration(hass, MockBridge)
|
||||
|
||||
button_contact_sensor_entity_id = "binary_sensor.dining_room_pico_stop_favorite"
|
||||
|
||||
state = hass.states.get(button_contact_sensor_entity_id)
|
||||
assert state
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
callback = mock_bridge.button_subscribers["111"][0]
|
||||
|
||||
callback("Press")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(button_contact_sensor_entity_id)
|
||||
assert state.state == STATE_ON
|
||||
|
||||
callback("Released")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(button_contact_sensor_entity_id)
|
||||
assert state.state == STATE_OFF
|
|
@ -7,16 +7,14 @@ from pytest_unordered import unordered
|
|||
|
||||
from homeassistant.components import automation
|
||||
from homeassistant.components.device_automation import DeviceAutomationType
|
||||
from homeassistant.components.lutron_caseta import (
|
||||
from homeassistant.components.lutron_caseta.const import (
|
||||
ATTR_ACTION,
|
||||
ATTR_AREA_NAME,
|
||||
ATTR_BUTTON_TYPE,
|
||||
ATTR_DEVICE_NAME,
|
||||
ATTR_LEAP_BUTTON_NUMBER,
|
||||
ATTR_SERIAL,
|
||||
ATTR_TYPE,
|
||||
)
|
||||
from homeassistant.components.lutron_caseta.const import (
|
||||
ATTR_BUTTON_TYPE,
|
||||
ATTR_LEAP_BUTTON_NUMBER,
|
||||
CONF_CA_CERTS,
|
||||
CONF_CERTFILE,
|
||||
CONF_KEYFILE,
|
||||
|
@ -159,6 +157,7 @@ async def test_get_triggers(hass: HomeAssistant) -> None:
|
|||
triggers = await async_get_device_automations(
|
||||
hass, DeviceAutomationType.TRIGGER, device_id
|
||||
)
|
||||
triggers = [trigger for trigger in triggers if trigger[CONF_DOMAIN] == DOMAIN]
|
||||
|
||||
assert triggers == unordered(expected_triggers)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue