22
33from __future__ import annotations
44
5+ from typing import TYPE_CHECKING
6+
7+ from mozart_api .models import PairedRemote
8+
59from homeassistant .components .event import EventDeviceClass , EventEntity
610from homeassistant .const import CONF_MODEL
711from homeassistant .core import HomeAssistant , callback
12+ from homeassistant .helpers import device_registry as dr
13+ from homeassistant .helpers .device_registry import DeviceInfo
814from homeassistant .helpers .dispatcher import async_dispatcher_connect
915from homeassistant .helpers .entity_platform import AddConfigEntryEntitiesCallback
1016
1117from . import BangOlufsenConfigEntry
12- from .const import CONNECTION_STATUS , DEVICE_BUTTON_EVENTS , WebsocketNotification
18+ from .const import (
19+ BEO_REMOTE_CONTROL_KEYS ,
20+ BEO_REMOTE_KEY_EVENTS ,
21+ BEO_REMOTE_KEYS ,
22+ BEO_REMOTE_SUBMENU_CONTROL ,
23+ BEO_REMOTE_SUBMENU_LIGHT ,
24+ CONNECTION_STATUS ,
25+ DEVICE_BUTTON_EVENTS ,
26+ DOMAIN ,
27+ MANUFACTURER ,
28+ BangOlufsenModel ,
29+ WebsocketNotification ,
30+ )
1331from .entity import BangOlufsenEntity
14- from .util import get_device_buttons
32+ from .util import get_device_buttons , get_remotes
1533
1634PARALLEL_UPDATES = 0
1735
@@ -21,24 +39,87 @@ async def async_setup_entry(
2139 config_entry : BangOlufsenConfigEntry ,
2240 async_add_entities : AddConfigEntryEntitiesCallback ,
2341) -> None :
24- """Set up Sensor entities from config entry."""
42+ """Set up Event entities from config entry."""
43+ entities : list [BangOlufsenEvent ] = []
2544
2645 async_add_entities (
2746 BangOlufsenButtonEvent (config_entry , button_type )
2847 for button_type in get_device_buttons (config_entry .data [CONF_MODEL ])
2948 )
3049
50+ # Check for connected Beoremote One
51+ remotes = await get_remotes (config_entry .runtime_data .client )
52+
53+ for remote in remotes :
54+ # Add Light keys
55+ entities .extend (
56+ [
57+ BangOlufsenRemoteKeyEvent (
58+ config_entry ,
59+ remote ,
60+ f"{ BEO_REMOTE_SUBMENU_LIGHT } /{ key_type } " ,
61+ )
62+ for key_type in BEO_REMOTE_KEYS
63+ ]
64+ )
65+
66+ # Add Control keys
67+ entities .extend (
68+ [
69+ BangOlufsenRemoteKeyEvent (
70+ config_entry ,
71+ remote ,
72+ f"{ BEO_REMOTE_SUBMENU_CONTROL } /{ key_type } " ,
73+ )
74+ for key_type in (* BEO_REMOTE_KEYS , * BEO_REMOTE_CONTROL_KEYS )
75+ ]
76+ )
3177
32- class BangOlufsenButtonEvent (BangOlufsenEntity , EventEntity ):
33- """Event class for Button events."""
78+ # If the remote is no longer available, then delete the device.
79+ # The remote may appear as being available to the device after it has been unpaired on the remote
80+ # As it has to be removed from the device on the app.
81+
82+ device_registry = dr .async_get (hass )
83+ devices = device_registry .devices .get_devices_for_config_entry_id (
84+ config_entry .entry_id
85+ )
86+ for device in devices :
87+ if (
88+ device .model == BangOlufsenModel .BEOREMOTE_ONE
89+ and device .serial_number not in {remote .serial_number for remote in remotes }
90+ ):
91+ device_registry .async_update_device (
92+ device .id , remove_config_entry_id = config_entry .entry_id
93+ )
94+
95+ async_add_entities (new_entities = entities )
96+
97+
98+ class BangOlufsenEvent (BangOlufsenEntity , EventEntity ):
99+ """Base Event class."""
34100
35101 _attr_device_class = EventDeviceClass .BUTTON
36102 _attr_entity_registry_enabled_default = False
103+
104+ def __init__ (self , config_entry : BangOlufsenConfigEntry ) -> None :
105+ """Initialize Event."""
106+ super ().__init__ (config_entry , config_entry .runtime_data .client )
107+
108+ @callback
109+ def _async_handle_event (self , event : str ) -> None :
110+ """Handle event."""
111+ self ._trigger_event (event )
112+ self .async_write_ha_state ()
113+
114+
115+ class BangOlufsenButtonEvent (BangOlufsenEvent ):
116+ """Event class for Button events."""
117+
37118 _attr_event_types = DEVICE_BUTTON_EVENTS
38119
39120 def __init__ (self , config_entry : BangOlufsenConfigEntry , button_type : str ) -> None :
40121 """Initialize Button."""
41- super ().__init__ (config_entry , config_entry . runtime_data . client )
122+ super ().__init__ (config_entry )
42123
43124 self ._attr_unique_id = f"{ self ._unique_id } _{ button_type } "
44125
@@ -52,20 +133,65 @@ async def async_added_to_hass(self) -> None:
52133 self .async_on_remove (
53134 async_dispatcher_connect (
54135 self .hass ,
55- f"{ self ._unique_id } _{ CONNECTION_STATUS } " ,
136+ f"{ DOMAIN } _ { self ._unique_id } _{ CONNECTION_STATUS } " ,
56137 self ._async_update_connection_state ,
57138 )
58139 )
59140 self .async_on_remove (
60141 async_dispatcher_connect (
61142 self .hass ,
62- f"{ self ._unique_id } _{ WebsocketNotification .BUTTON } _{ self ._button_type } " ,
143+ f"{ DOMAIN } _ { self ._unique_id } _{ WebsocketNotification .BUTTON } _{ self ._button_type } " ,
63144 self ._async_handle_event ,
64145 )
65146 )
66147
67- @callback
68- def _async_handle_event (self , event : str ) -> None :
69- """Handle event."""
70- self ._trigger_event (event )
71- self .async_write_ha_state ()
148+
149+ class BangOlufsenRemoteKeyEvent (BangOlufsenEvent ):
150+ """Event class for Beoremote One key events."""
151+
152+ _attr_event_types = BEO_REMOTE_KEY_EVENTS
153+
154+ def __init__ (
155+ self ,
156+ config_entry : BangOlufsenConfigEntry ,
157+ remote : PairedRemote ,
158+ key_type : str ,
159+ ) -> None :
160+ """Initialize Beoremote One key."""
161+ super ().__init__ (config_entry )
162+
163+ if TYPE_CHECKING :
164+ assert remote .serial_number
165+
166+ self ._attr_unique_id = f"{ remote .serial_number } _{ self ._unique_id } _{ key_type } "
167+ self ._attr_device_info = DeviceInfo (
168+ identifiers = {(DOMAIN , f"{ remote .serial_number } _{ self ._unique_id } " )},
169+ name = f"{ BangOlufsenModel .BEOREMOTE_ONE } -{ remote .serial_number } -{ self ._unique_id } " ,
170+ model = BangOlufsenModel .BEOREMOTE_ONE ,
171+ serial_number = remote .serial_number ,
172+ sw_version = remote .app_version ,
173+ manufacturer = MANUFACTURER ,
174+ via_device = (DOMAIN , self ._unique_id ),
175+ )
176+
177+ # Make the native key name Home Assistant compatible
178+ self ._attr_translation_key = key_type .lower ().replace ("/" , "_" )
179+
180+ self ._key_type = key_type
181+
182+ async def async_added_to_hass (self ) -> None :
183+ """Listen to WebSocket Beoremote One key events."""
184+ self .async_on_remove (
185+ async_dispatcher_connect (
186+ self .hass ,
187+ f"{ DOMAIN } _{ self ._unique_id } _{ CONNECTION_STATUS } " ,
188+ self ._async_update_connection_state ,
189+ )
190+ )
191+ self .async_on_remove (
192+ async_dispatcher_connect (
193+ self .hass ,
194+ f"{ DOMAIN } _{ self ._unique_id } _{ WebsocketNotification .BEO_REMOTE_BUTTON } _{ self ._key_type } " ,
195+ self ._async_handle_event ,
196+ )
197+ )
0 commit comments