Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 51 additions & 15 deletions custom_components/dirigera_platform/device_trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@

from homeassistant.const import CONF_TYPE, CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, ATTR_ENTITY_ID
from homeassistant.components.homeassistant.triggers import event as event_trigger
from homeassistant.components.homeassistant.triggers import state as state_trigger
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA

from .const import DOMAIN
from .hub_event_listener import hub_event_listener

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

TRIGGER_TYPES = ["single_click", "long_press","double_click"]
CONTROLLER_TRIGGER_TYPES = ["single_click", "long_press", "double_click"]
LIGHT_TRIGGER_TYPES = ["turned_on", "turned_off"]
TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend({vol.Required(CONF_TYPE): cv.string, vol.Required(ATTR_ENTITY_ID): cv.string})

async def async_get_triggers(hass: HomeAssistant, device_id: str) -> list[dict[str, Any]]:
Expand Down Expand Up @@ -53,37 +55,57 @@ async def async_get_triggers(hass: HomeAssistant, device_id: str) -> list[dict[s

logger.debug(f"Found controller to tag events to entity : {entity_name}")

entity_domain = entity_name.split('.', 1)[0]
if entity_domain == "light":
for trigger_type in LIGHT_TRIGGER_TYPES:
triggers.append(
{
CONF_DEVICE_ID: device_id,
CONF_DOMAIN: DOMAIN,
CONF_PLATFORM: "device",
CONF_TYPE: trigger_type,
ATTR_ENTITY_ID: entity_name,
}
)
break

# Now we have an ikea_controller
# Check if entity_id has _X suffix (like "xxx_1") - this pattern must match hub_event_listener.py
# If it matches, we ALWAYS use buttonX_ prefix to be consistent with event firing
pattern = r'(([0-9]|[a-z]|-)*)_([0-9])+'
match = re.match(pattern, entity_id)

use_prefix : bool = False
use_prefix: bool = False
if match:
# Device ID has _X suffix - hub_event_listener will add buttonX_ prefix
logger.debug(f"Entity ID {entity_id} matches multi-button pattern, will use prefix")
use_prefix = True
elif registry_entity.number_of_buttons > 1:

if not hasattr(registry_entity, "number_of_buttons"):
logger.debug(f"Entity {entity_id} is not a controller and has no button triggers")
break

if registry_entity.number_of_buttons > 1:
# Multiple buttons without _X suffix - also use prefix
logger.debug("More than one button will use prefix")
use_prefix = True

for btn_idx in range(registry_entity.number_of_buttons):
for trigger_type in TRIGGER_TYPES:
for trigger_type in CONTROLLER_TRIGGER_TYPES:
if use_prefix:
trigger_name = f"button{btn_idx+1}_{trigger_type}"
else:
trigger_name = trigger_type

triggers.append(
{
CONF_DEVICE_ID: device_id,
CONF_DOMAIN: DOMAIN,
CONF_PLATFORM: "device",
CONF_TYPE: trigger_name,
ATTR_ENTITY_ID: entity_name
})
{
CONF_DEVICE_ID: device_id,
CONF_DOMAIN: DOMAIN,
CONF_PLATFORM: "device",
CONF_TYPE: trigger_name,
ATTR_ENTITY_ID: entity_name,
}
)

break

Expand All @@ -92,19 +114,33 @@ async def async_get_triggers(hass: HomeAssistant, device_id: str) -> list[dict[s

async def async_attach_trigger(hass, config, action, trigger_info):
logger.debug(f"Got to async_attach_trigger config: {config}, action: {action}, trigger_info: {trigger_info}")


if config[CONF_TYPE] in LIGHT_TRIGGER_TYPES:
# Attach a HA state trigger for light on/off events
state_config = state_trigger.TRIGGER_SCHEMA(
{
state_trigger.CONF_PLATFORM: "state",
state_trigger.CONF_ENTITY_ID: config[ATTR_ENTITY_ID],
state_trigger.CONF_TO: "on" if config[CONF_TYPE] == "turned_on" else "off",
}
)
return await state_trigger.async_attach_trigger(
hass, state_config, action, trigger_info, platform_type="device"
)

# Controller triggers (button presses) use the custom event bus path
event_config = event_trigger.TRIGGER_SCHEMA(
{
event_trigger.CONF_PLATFORM: "event",
event_trigger.CONF_EVENT_TYPE: f"{DOMAIN}_event",
event_trigger.CONF_EVENT_DATA: {
CONF_DEVICE_ID: config[CONF_DEVICE_ID],
CONF_TYPE: config[CONF_TYPE],
ATTR_ENTITY_ID: config[ATTR_ENTITY_ID]
ATTR_ENTITY_ID: config[ATTR_ENTITY_ID],
},
}
)

return await event_trigger.async_attach_trigger(
hass, event_config, action, trigger_info, platform_type="device"
)
44 changes: 44 additions & 0 deletions custom_components/dirigera_platform/hub_event_listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,15 @@ async def sync_all_device_areas(self):
def on_error(self, ws:Any, ws_msg:str):
logger.debug(f"on_error hub event listener {ws_msg}")

def _fire_dirigera_event(self, device_id: str, entity_id: str, trigger_type: str):
event_data = {
"type": trigger_type,
"device_id": device_id,
ATTR_ENTITY_ID: entity_id,
}
self._hass.bus.fire(event_type="dirigera_platform_event", event_data=event_data)
logger.debug(f"dirigera_platform_event fired: {event_data}")

def parse_scene_update(self, msg):
global controller_trigger_last_time_map
# Verify that this is controller initiated
Expand Down Expand Up @@ -310,10 +319,14 @@ def _apply_scene_actions(self, msg):
continue

entity = registry_value.entity
old_is_on = None
if hasattr(entity._json_data.attributes, "is_on"):
old_is_on = entity._json_data.attributes.is_on

# Only process light-relevant attributes from scene actions
light_attrs = ["isOn", "lightLevel", "colorTemperature", "colorHue", "colorSaturation"]
updated = False
scene_on_off_event = None

for key in attributes:
if key not in light_attrs:
Expand All @@ -323,6 +336,8 @@ def _apply_scene_actions(self, msg):
logger.debug(f"Scene action: setting {key_attr} to {attributes[key]} on {device_id}")
setattr(entity._json_data.attributes, key_attr, attributes[key])
updated = True
if key == "isOn" and old_is_on is not None and old_is_on != attributes[key]:
scene_on_off_event = "turned_on" if attributes[key] else "turned_off"
except Exception as ex:
logger.warning(f"Scene action: failed to set {key} on {device_id}: {ex}")

Expand All @@ -339,6 +354,11 @@ def _apply_scene_actions(self, msg):
try:
entity.schedule_update_ha_state()
logger.debug(f"Scene action: scheduled HA state update for {device_id}")
if scene_on_off_event is not None:
try:
self._fire_dirigera_event(device_id, entity.registry_entry.entity_id, scene_on_off_event)
except Exception:
pass
except Exception as ex:
logger.warning(f"Scene action: failed to schedule update for {device_id}: {ex}")

Expand Down Expand Up @@ -505,6 +525,13 @@ def on_message(self, ws:Any, ws_msg:str):
registry_value = hub_event_listener.get_registry_entry(id)
entity = registry_value.entity

old_is_on = None
if device_type == "light":
try:
old_is_on = entity._json_data.attributes.is_on
except Exception:
old_is_on = None

reachability_changed = False
if "isReachable" in info:
try:
Expand Down Expand Up @@ -637,6 +664,23 @@ def on_message(self, ws:Any, ws_msg:str):
logger.debug(f"Cascading to cascade entity : {registry_value.cascade_entity.unique_id}")
registry_value.cascade_entity.schedule_update_ha_state(False)

# Fire platform event for light on/off state changes to support device triggers
if device_type == "light" and has_attributes and "attributes" in info and "isOn" in info["attributes"]:
new_is_on = info["attributes"]["isOn"]
if old_is_on is not None and new_is_on != old_is_on:
trigger_type = "turned_on" if new_is_on else "turned_off"
try:
entity_id = entity.registry_entry.entity_id
except Exception:
entity_id = None
self._fire_dirigera_event(id, entity_id or "", trigger_type)
if registry_value.cascade_entity is not None:
try:
cascade_entity_id = registry_value.cascade_entity.registry_entry.entity_id
except Exception:
cascade_entity_id = None
self._fire_dirigera_event(registry_value.cascade_entity.unique_id, cascade_entity_id or "", trigger_type)


except Exception as ex:
# Temp solution to not log entries
Expand Down
2 changes: 1 addition & 1 deletion custom_components/dirigera_platform/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"issue_tracker": "https://github.com/nrbrt/dirigera_platform/issues",
"loggers": ["custom_components.dirigera_platform"],
"requirements": ["dirigera==1.2.6"],
"version": "0.0.1"
"version": "0.2.5"
}
Loading