Skip to content

Commit

Permalink
Beta for controller events
Browse files Browse the repository at this point in the history
  • Loading branch information
c503ghosh committed Sep 11, 2024
1 parent 4dbceab commit e0451e2
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 19 deletions.
33 changes: 24 additions & 9 deletions custom_components/dirigera_platform/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
import logging

from dirigera import Hub
from dirigera.devices.scene import Scene as DirigeraScene

import voluptuous as vol

from homeassistant import config_entries, core
from homeassistant.components.light import PLATFORM_SCHEMA
from homeassistant.const import CONF_IP_ADDRESS, CONF_TOKEN
from homeassistant.const import CONF_IP_ADDRESS, CONF_TOKEN, CONF_ENTITY_ID, CONF_TYPE

# Import the device class from the component that you want to support
from homeassistant.core import HomeAssistant
Expand All @@ -34,9 +35,10 @@
hub_events = None

async def async_setup(hass: HomeAssistant, config: dict) -> bool:
logger.debug("Starting async_setup...")
logger.debug(config)
logger.debug("Complete async_setup...")
logger.error("Starting async_setup...")
for k in config.keys():
logger.error(f"config key: {k} value: {config[k]}")
logger.error("Complete async_setup...")

def handle_dump_data(call):
import dirigera
Expand Down Expand Up @@ -68,9 +70,9 @@ async def async_setup_entry(
) -> bool:
global hub_events
"""Set up platform from a ConfigEntry."""
logger.debug("Staring async_setup_entry in init...")
logger.debug(dict(entry.data))

logger.error("Staring async_setup_entry in init...")
logger.error(dict(entry.data))
logger.error(f"async_setup_entry {entry.unique_id} {entry.state} {entry.entry_id} {entry.title} {entry.domain}")
hass.data.setdefault(DOMAIN, {})
hass_data = dict(entry.data)

Expand Down Expand Up @@ -106,7 +108,7 @@ async def async_setup_entry(
hub = Hub(hass_data[CONF_TOKEN], hass_data[CONF_IP_ADDRESS])

if hass_data[CONF_IP_ADDRESS] != "mock":
hub_events = hub_event_listener(hub)
hub_events = hub_event_listener(hub, hass)
hub_events.start()

logger.debug("Complete async_setup_entry...")
Expand All @@ -121,7 +123,6 @@ async def options_update_listener(
"""Handle options update."""
await hass.config_entries.async_reload(config_entry.entry_id)


async def async_unload_entry(
hass: core.HomeAssistant, entry: config_entries.ConfigEntry
) -> bool:
Expand All @@ -134,6 +135,20 @@ async def async_unload_entry(
hub_events.stop()
hub_events = None

hass_data = dict(entry.data)
hub = Hub(hass_data[CONF_TOKEN], hass_data[CONF_IP_ADDRESS])

# For each controller if there is an empty scene delete it
logger.error("In unload so forcing delete of scenes...")
scenes: list[DirigeraScene] = await hass.async_add_executor_job(hub.get_scenes)
for scene in scenes:
if scene.info.name is None or not scene.info.name.startswith("dirigera_platform_empty_scene_"):
logger.error(f"Ignoring scene : {scene.info.name}, as not empty scene")
continue
logger.error(f"Deleting scene {scene.id}...")
await hass.async_add_executor_job(hass.delete_scene,scene.id)
logger.error("Done deleting scene....")

"""Unload a config entry."""
unload_ok = all(
[
Expand Down
14 changes: 14 additions & 0 deletions custom_components/dirigera_platform/dirigera_lib_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@ def get_controllers(self) -> List[ControllerX]:
devices = self.get("/devices")
controllers = list(filter(lambda x: x["type"] == "controller", devices))
return [dict_to_controller(controller, self) for controller in controllers]

def create_empty_scene(self, name:str):
data = {
"info": { "name" : name , "icon" : "scenes_trophy"},
"type": "customScene",
"triggers": [],
"actions": []
}

#data = camelize_dict(data) # type: ignore
response_dict = self.post(
"/scenes",
data=data,
)

class ControllerAttributesX(Attributes):
is_on: Optional[bool] = None
Expand Down
88 changes: 87 additions & 1 deletion custom_components/dirigera_platform/hub_event_listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
import re
import websocket
import ssl
import re
from typing import Any

from dirigera import Hub
from .const import DOMAIN

from homeassistant.const import ATTR_ENTITY_ID

logger = logging.getLogger("custom_components.dirigera_platform")

Expand Down Expand Up @@ -60,14 +64,96 @@ def get_registry_entry(id:str) -> registry_entry:
return None
return hub_event_listener.device_registry[id]

def __init__(self, hub : Hub):
def __init__(self, hub : Hub, hass):
super().__init__()
self._hub : Hub = hub
self._request_to_stop = False
self._hass = hass

def on_error(self, ws:Any, ws_msg:str):
logger.debug(f"on_error hub event listener {ws_msg}")

def parse_scene_update(self, msg):
# Verify that this is controller initiated
if "data" not in msg:
logger.error(f"discarding message as key 'data' not found: {msg}")
return

if "triggers" not in msg["data"]:
logger.error(f"discarding message as key 'data/triggers'")
return

triggers = msg["data"]["triggers"]

for trigger in triggers:
if "type" not in trigger:
logger.error(f"key 'type' not in trigger json : {trigger}")
continue

if trigger["type"] != "controller":
continue

if "trigger" not in trigger:
logger.error(f"key 'trigger' not found in trigger json: {trigger}")
continue

details = trigger["trigger"]

if "controllerType" not in details or "clickPattern" not in details or "deviceId" not in details:
logger.error(f"Required key controllerType/clickPattern/deviceId not in trigger json : {trigger}")
continue

controller_type = details["controllerType"]
click_pattern = details["clickPattern"]
device_id = details["deviceId"]

if controller_type != "shortcutController":
logger.error(f"controller type on message not compatible {controller_type}, ignoring...")
continue

if click_pattern == "singlePress":
trigger_type = "single_click"
elif click_pattern == "longPress":
trigger_type = "long_press"
elif click_pattern == "double_click":
trigger_type == "double_click"
else:
logger.error(f"click_pattern : {click_pattern} not in list of types...ignoring")
continue

device_id_for_registry = device_id

button_idx = 0
pattern = '(([0-9]|[a-z]|-)*)_([0-9])+'
match = re.search(pattern, device_id)
if match is not None:
device_id_for_registry = f"{match.groups()[0]}_1"
button_idx = int(match.groups()[2])
logger.error(f"Multi button controller, device_id effective : {device_id_for_registry} with buttons : {button_idx}")

if button_idx != 0:
trigger_type =f"button{button_idx}_{trigger_type}"

# Now look up the associated entity in our own registry
registry_value = hub_event_listener.get_registry_entry(device_id_for_registry)

if registry_value.__class__.__name__ != "registry_entry":
logger.error(f"id : {device_id_for_registry} listener registry is not correct : {registry_value.__class__.__name__}...")
continue

entity = registry_value.entity

# Now raise the bus event
event_data = {
"type": trigger_type,
"device_id": entity.registry_entry.device_id,
ATTR_ENTITY_ID: entity.registry_entry.entity_id
}

self._hass.bus.async_fire(event_type="dirigera_platform_event",event_data=event_data)
logger.error(f"Event fired.. {event_data}")
logger.error(f"{self.registry_entry}")

def on_message(self, ws:Any, ws_msg:str):

try:
Expand Down
6 changes: 4 additions & 2 deletions custom_components/dirigera_platform/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Create scene entities from Dirigera scene."""
logger.debug("async_setup_entry for scenes...")
config = hass.data[DOMAIN][entry.entry_id]
hub = Hub(config[CONF_TOKEN], config[CONF_IP_ADDRESS])

Expand All @@ -35,8 +36,9 @@ async def async_setup_entry(
return

scenes: list[DirigeraScene] = await hass.async_add_executor_job(hub.get_scenes)
logger.error(f"Found {len(scenes)} scenes...")
entities: list[IkeaScene] = [IkeaScene(hub, s) for s in scenes]
logger.debug("Found %d scenes", len(entities))
logger.error("Found %d scenes", len(entities))
async_add_entities(entities)


Expand Down Expand Up @@ -78,4 +80,4 @@ async def async_update(self) -> None:
"Error encountered on update of '%s' (%s)", self.name, self.unique_id
)
logger.error(ex)
raise HomeAssistantError from ex
raise HomeAssistantError from ex
41 changes: 36 additions & 5 deletions custom_components/dirigera_platform/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .dirigera_lib_patch import HubX
from dirigera.devices.environment_sensor import EnvironmentSensor
from dirigera.devices.controller import Controller
from dirigera.devices.scene import Info, Icon

from homeassistant.helpers.entity import Entity
from homeassistant import config_entries, core
Expand All @@ -25,6 +26,10 @@ async def async_setup_entry(
):
logger.debug("EnvSensor & Controllers Starting async_setup_entry")
"""Setup sensors from a config entry created in the integrations UI."""
logger.error("Staring async_setup_entry in SENSOR...")
logger.error(dict(config_entry.data))
logger.error(f"async_setup_entry SENSOR {config_entry.unique_id} {config_entry.state} {config_entry.entry_id} {config_entry.title} {config_entry.domain}")

config = hass.data[DOMAIN][config_entry.entry_id]
logger.debug(config)

Expand Down Expand Up @@ -57,13 +62,25 @@ async def async_setup_entry(
]

hub_controllers = await hass.async_add_executor_job(hub.get_controllers)
logger.error(f"Got {len(hub_controllers)} controllers...")
controller_devices = [
ikea_controller(hass, hub, controller_device)
for controller_device in hub_controllers
# Only create a battery sensor entity if the device reports battery percentage
# This is not the case of the second device for SOMRIG controllers
if controller_device.attributes.battery_percentage
]

for controller in controller_devices:
# Hack to create empty scene so that we can associate it the controller
# so that click of buttons on the controller can generate events on the hub
#hub.create(name=f"dirigera_platform_empty_scene_{controller.unique_id}",icon="scenes_heart")
scene_name=f"dirigera_platform_empty_scene_{controller.unique_id}"
#scene_info.name = f"dirigera_platform_empty_scene_{controller.unique_id}"
#scene_info.icon = "scenes_heart"
logger.error(f"Creating empty scene {scene_name} for controller...")
await hass.async_add_executor_job(hub.create_empty_scene,scene_name)


env_sensors = []
for env_device in env_devices:
Expand Down Expand Up @@ -201,9 +218,22 @@ def native_value(self) -> int:
def native_unit_of_measurement(self) -> str:
return "µg/m³"

# SOMRIG Controllers act differently in the gateway Hub
# While its one device but two id's are sent back each
# representing the two buttons on the controler. The id is
# all same except _1 and _2 suffix. The serial number on the
# controllers is same.

CONTROLLER_BUTTON_MAP = { "SOMRIG shortcut button" : 2 }

class ikea_controller(ikea_base_device, SensorEntity):
def __init__(self,hass:core.HomeAssistant, hub:Hub, json_data:Controller):
logger.debug("ikea_controller ctor...")
self._buttons = 1
if json_data.attributes.model in CONTROLLER_BUTTON_MAP:
self._buttons = CONTROLLER_BUTTON_MAP[json_data.attributes.model]
logger.error(f"Set #buttons to {self._buttons} as controller model is : {json_data.attributes.model}")

super().__init__(hass , hub, json_data, hub.get_controller_by_id)

@property
Expand All @@ -225,9 +255,10 @@ def native_unit_of_measurement(self) -> str:
@property
def device_class(self) -> str:
return SensorDeviceClass.BATTERY

@property
def number_of_buttons(self) -> int:
return self._buttons

# SOMRIG Controllers act differently in the gateway Hub
# While its one device but two id's are sent back each
# representing the two buttons on the controler. The id is
# all same except _1 and _2 suffix. The serial number on the
# controllers is same.
async def async_update(self):
pass
15 changes: 14 additions & 1 deletion custom_components/dirigera_platform/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,18 @@
"description": "Update IKEA Dirigera Hub Setting..."
}
}
}
},
"device_automation": {
"trigger_type": {
"single_click": "{entity_name} Single Click",
"long_press": "{entity_name} Long Press",
"double_click": "{entity_name} Double Click",
"button1_single_click": "{entity_name} Button 1 Single Click",
"button1_long_press": "{entity_name} Button 1 Long Press",
"button1_double_click": "{entity_name} Button 1 Double Click",
"button2_single_click": "{entity_name} Button 2 Single Click",
"button2_long_press": "{entity_name} Button 2 Long Press",
"button2_double_click": "{entity_name} Button 2 Double Click"
}
}
}
15 changes: 14 additions & 1 deletion custom_components/dirigera_platform/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,18 @@
"hub_exception": {
"message" : "Exception raised by Hub..."
}
}
},
"device_automation": {
"trigger_type": {
"single_click": "{entity_name} Single Click",
"long_press": "{entity_name} Long Press",
"double_click": "{entity_name} Double Click",
"button1_single_click": "{entity_name} Button 1 Single Click",
"button1_long_press": "{entity_name} Button 1 Long Press",
"button1_double_click": "{entity_name} Button 1 Double Click",
"button2_single_click": "{entity_name} Button 2 Single Click",
"button2_long_press": "{entity_name} Button 2 Long Press",
"button2_double_click": "{entity_name} Button 2 Double Click"
}
}
}

0 comments on commit e0451e2

Please sign in to comment.