From a33146fbcb0cbf155f2199f9d74f11b96e992fa2 Mon Sep 17 00:00:00 2001 From: Norbert Date: Tue, 13 Jan 2026 11:33:36 +0100 Subject: [PATCH 1/2] Add support for MYGGSPRAY motion sensors (occupancySensor) IKEA MYGGSPRAY motion sensors (E2494) report as deviceType 'occupancySensor' instead of 'motionSensor'. They also lack the 'is_on' attribute that regular motion sensors have. This PR fixes both issues by: - Including 'occupancySensor' in motion sensor queries - Creating MotionSensorX with optional is_on attribute - Overriding get_motion_sensors() and get_motion_sensor_by_id() Tested with IKEA MYGGSPRAY E2494 sensors on Home Assistant 2026.1.1. --- .../dirigera_platform/dirigera_lib_patch.py | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/custom_components/dirigera_platform/dirigera_lib_patch.py b/custom_components/dirigera_platform/dirigera_lib_patch.py index f849c96..2a514cd 100644 --- a/custom_components/dirigera_platform/dirigera_lib_patch.py +++ b/custom_components/dirigera_platform/dirigera_lib_patch.py @@ -80,6 +80,26 @@ def delete_empty_scenes(self): if scene.name.startswith("dirigera_integration_empty_scene_"): logging.debug(f"Deleting Scene id: {scene.id} name: {scene.name}...") self.delete_scene(scene.id) + + def get_motion_sensors(self) -> List[MotionSensorX]: + """ + Fetches all motion sensors registered in the Hub. + Includes both motionSensor and occupancySensor device types. + IKEA MYGGSPRAY sensors report as occupancySensor instead of motionSensor. + """ + devices = self.get("/devices") + sensors = list(filter(lambda x: x["deviceType"] in ("motionSensor", "occupancySensor"), devices)) + return [dict_to_motion_sensor_x(sensor, self) for sensor in sensors] + + def get_motion_sensor_by_id(self, id_: str) -> MotionSensorX: + """ + Fetches a motion sensor by ID. + Accepts both motionSensor and occupancySensor device types. + """ + motion_sensor = self._get_device_data_by_id(id_) + if motion_sensor["deviceType"] not in ("motionSensor", "occupancySensor"): + raise ValueError("Device is not a MotionSensor or OccupancySensor") + return dict_to_motion_sensor_x(motion_sensor, self) class ControllerAttributesX(Attributes): is_on: Optional[bool] = None @@ -136,4 +156,35 @@ def trigger(self) -> HackScene: self.hub.post(route=f"/scenes/{self.id}/trigger") def undo(self) -> HackScene: - self.hub.post(route=f"/scenes/{self.id}/undo") \ No newline at end of file + self.hub.post(route=f"/scenes/{self.id}/undo") + + +# Motion sensor patch for MYGGSPRAY (occupancySensor) +# MYGGSPRAY sensors don't have is_on attribute, so we make it optional +class MotionSensorAttributesX(Attributes): + battery_percentage: Optional[int] = None + is_on: Optional[bool] = None # Made optional for MYGGSPRAY compatibility + light_level: Optional[float] = None + is_detected: Optional[bool] = False + + +class MotionSensorX(Device): + dirigera_client: AbstractSmartHomeHub + attributes: MotionSensorAttributesX + + def reload(self) -> "MotionSensorX": + data = self.dirigera_client.get(route=f"/devices/{self.id}") + return MotionSensorX(dirigeraClient=self.dirigera_client, **data) + + def set_name(self, name: str) -> None: + if "customName" not in self.capabilities.can_receive: + raise AssertionError("This sensor does not support the set_name function") + data = [{"attributes": {"customName": name}}] + self.dirigera_client.patch(route=f"/devices/{self.id}", data=data) + self.attributes.custom_name = name + + +def dict_to_motion_sensor_x( + data: Dict[str, Any], dirigera_client: AbstractSmartHomeHub +) -> MotionSensorX: + return MotionSensorX(dirigeraClient=dirigera_client, **data) \ No newline at end of file From b7303db869ae79b9f9a66268e9250db555efa9f0 Mon Sep 17 00:00:00 2001 From: Norbert Date: Tue, 13 Jan 2026 19:59:25 +0100 Subject: [PATCH 2/2] Add occupancySensor to WebSocket event listener MYGGSPRAY motion sensors send events as occupancySensor device type. Without this, the hub_event_listener ignores real-time state changes from these sensors. Co-Authored-By: Claude Opus 4.5 --- custom_components/dirigera_platform/hub_event_listener.py | 1 + 1 file changed, 1 insertion(+) diff --git a/custom_components/dirigera_platform/hub_event_listener.py b/custom_components/dirigera_platform/hub_event_listener.py index e5ef40a..be82389 100644 --- a/custom_components/dirigera_platform/hub_event_listener.py +++ b/custom_components/dirigera_platform/hub_event_listener.py @@ -19,6 +19,7 @@ process_events_from = { "motionSensor" : ["isDetected","isOn","batteryPercentage"], + "occupancySensor" : ["isDetected","isOn","batteryPercentage"], "outlet" : [ "isOn", "currentAmps", "currentActivePower",