Skip to content

Commit

Permalink
Controller Event Support
Browse files Browse the repository at this point in the history
  • Loading branch information
c503ghosh committed Oct 14, 2024
1 parent d8ec5a7 commit 96db9af
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 62 deletions.
32 changes: 14 additions & 18 deletions custom_components/dirigera_platform/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import logging

from dirigera import Hub
from .dirigera_lib_patch import HubX

from dirigera.devices.scene import Scene as DirigeraScene

import voluptuous as vol
Expand Down Expand Up @@ -35,10 +37,10 @@
hub_events = None

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

def handle_dump_data(call):
import dirigera
Expand Down Expand Up @@ -70,13 +72,13 @@ async def async_setup_entry(
) -> bool:
global hub_events
"""Set up platform from a ConfigEntry."""
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}")
logger.debug("Staring async_setup_entry in init...")
logger.debug(dict(entry.data))
logger.debug(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)

logger.error(f"hass_data : {hass_data}")
logger.debug(f"hass_data : {hass_data}")

# for backward compatibility
hide_device_set_bulbs : bool = True
Expand Down Expand Up @@ -111,7 +113,7 @@ async def async_setup_entry(
hub_events = hub_event_listener(hub, hass)
hub_events.start()

logger.error("Complete async_setup_entry...")
logger.debug("Complete async_setup_entry...")

return True

Expand All @@ -137,18 +139,12 @@ async def async_unload_entry(
hub_events = None

hass_data = dict(entry.data)
hub = Hub(hass_data[CONF_TOKEN], hass_data[CONF_IP_ADDRESS])
hub = HubX(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....")
logger.debug("In unload so forcing delete of scenes...")
await hass.async_add_executor_job(hub.delete_empty_scenes)
logger.debug("Done deleting empty scenes....")

"""Unload a config entry."""
unload_ok = all(
Expand Down
62 changes: 41 additions & 21 deletions custom_components/dirigera_platform/dirigera_lib_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

from dirigera.devices.device import Attributes, Device
from dirigera.hub.abstract_smart_home_hub import AbstractSmartHomeHub
from dirigera.devices.scene import Info, Icon, SceneType, Trigger, TriggerDetails, ControllerType
import logging

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

# Patch to fix issues with motion sensor
class HubX(Hub):
Expand All @@ -22,28 +26,44 @@ def get_controllers(self) -> List[ControllerX]:
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, controller_id: str):
data = {
"info": { "name" : name , "icon" : "scenes_trophy"},
"type": "customScene",
"triggers": [ {
"type" : "controller",
"disabled": False,
"trigger": {
"controllerType" : "shortcutController",
"buttonIndex" : 0,
"device_id" : controller_id
}
}
],
"actions": []
}

response_dict = self.post(
"/scenes",
data=data,
)
def create_empty_scene(self, controller_id: str, clicks_supported:list):
logging.debug(f"Creating empty scene for controller : {controller_id} with clicks : {clicks_supported}")
for click in clicks_supported:
scene_name = f'dirigera_integration_empty_scene_{controller_id}_{click}'
info = Info(name=f'dirigera_integration_empty_scene_{controller_id}_{click}', icon=Icon.SCENES_CAKE)
device_trigger = Trigger(type="controller", disabled=False,
trigger=TriggerDetails(clickPattern=click, buttonIndex=0, deviceId=controller_id, controllerType=ControllerType.SHORTCUT_CONTROLLER))

logger.debug(f"Creating empty scene : {info.name}")
#self.create_scene(info=info, scene_type=SceneType.USER_SCENE,triggers=[device_trigger])
data = {
"info": {"name" : scene_name, "icon" : "scenes_cake"},
"type": "customScene",
"triggers":[
{
"type": "controller",
"disabled": False,
"trigger":
{
"controllerType": "shortcutController",
"clickPattern": click,
"buttonIndex": 0,
"deviceId": controller_id
}
}
],
"actions": []
}

self.post("/scenes/", data=data)

def delete_empty_scenes(self):
scenes = self.get_scenes()
for scene in scenes:
if scene.info.name.startswith("dirigera_integration_empty_scene_"):
logging.debug(f"Deleting Scene id: {scene.id} name: {scene.info.name}...")
self.delete_scene(scene.id)

class ControllerAttributesX(Attributes):
is_on: Optional[bool] = None
battery_percentage: Optional[int] = None
Expand Down
30 changes: 19 additions & 11 deletions custom_components/dirigera_platform/hub_event_listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,49 +76,50 @@ def on_error(self, ws:Any, ws_msg:str):
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}")
logger.warning(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'")
logger.warning(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}")
logger.warning(f"key 'type' not in trigger json : {trigger}")
continue

if trigger["type"] != "controller":
logger.debug(f"Trigger type : {trigger['type']} not controller ignoring...")
continue

if "trigger" not in trigger:
logger.error(f"key 'trigger' not found in trigger json: {trigger}")
logger.warning(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}")
logger.debug(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...")
logger.debug(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"
elif click_pattern == "doublePress":
trigger_type = "double_click"
else:
logger.error(f"click_pattern : {click_pattern} not in list of types...ignoring")
logger.debug(f"click_pattern : {click_pattern} not in list of types...ignoring")
continue

device_id_for_registry = device_id
Expand Down Expand Up @@ -152,14 +153,21 @@ def parse_scene_update(self, msg):

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:
logger.debug(f"rcvd message : {ws_msg}")
msg = json.loads(ws_msg)
if "type" not in msg or msg['type'] != "deviceStateChanged":
if "type" not in msg:
logger.debug(f"'type' not found in incoming message, discarding : {msg}")
return

if msg['type'] == "sceneUpdated":
logger.debug(f"Found sceneUpdated message... ")
return self.parse_scene_update(msg)

if msg['type'] != "deviceStateChanged":
logger.debug(f"discarding non state message: {msg}")
return

Expand Down
2 changes: 1 addition & 1 deletion custom_components/dirigera_platform/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
dirigera==1.1.9
dirigera==1.1.9
4 changes: 2 additions & 2 deletions custom_components/dirigera_platform/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +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...")
logger.debug(f"Found {len(scenes)} scenes...")
entities: list[IkeaScene] = [IkeaScene(hub, s) for s in scenes]
logger.error("Found %d scenes", len(entities))
logger.debug("Found %d scenes", len(entities))
async_add_entities(entities)


Expand Down
23 changes: 14 additions & 9 deletions custom_components/dirigera_platform/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,26 @@ async def async_setup_entry(
# battery % attribute which we shall use to identify
controller_devices = []

# Precuationary delete all empty scenes
await hass.async_add_executor_job(hub.delete_empty_scenes)

for controller_device in hub_controllers:
controller : ikea_controller = ikea_controller(hass, hub, controller_device)

# 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")

# Commenting right now
#scene_name=f"dirigera_platform_empty_scene_{controller.unique_id}"
#logger.error(f"Creating empty scene {scene_name} for controller {controller.unique_id}...")
#await hass.async_add_executor_job(hub.create_empty_scene,scene_name, controller.unique_id)

clicks_supported = controller_device.capabilities.can_send
clicks_supported = [ x for x in clicks_supported if x.endswith("Press") ]

if len(clicks_supported) == 0:
logger.debug(f"Ignoring controller for scene creation : {controller_device.id} as no press event supported : {controller_device.capabilities.can_send}")
else:
#hub.create_empty_scene(controller_id=controller_device.id,clicks_supported=clicks_supported)
await hass.async_add_executor_job(hub.create_empty_scene,controller_device.id, clicks_supported)

if controller_device.attributes.battery_percentage :
controller_devices.append(controller)

env_sensors = []
for env_device in env_devices:
# For each device setup up multiple entities
Expand Down Expand Up @@ -293,7 +298,7 @@ def __init__(self,hass:core.HomeAssistant, hub:Hub, json_data:Controller):
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}")
logger.debug(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)

Expand Down

0 comments on commit 96db9af

Please sign in to comment.