Ikea lights mode switching
Question
Alright, I'm posting this as a question since I'm not sure if it can be considered a bug. Technically ControllerX does what's asked of it but unfortunately there's an issue with Ikea lights and this behavior.
The issue, I've set up an E2123 as a lightcontroller, this part works fine, light toggle, brightness, color and color_temp works by them self. But.. Ikea lights switches modes depending on what you ask of it. So if I click color_up/down the state attribute color_mode switches to xy and the light is now colored. While in this state, if I now click color_temp_up/down I get an error from ControllerX (see below logs). If I then set color_temp in Hass, color_temp_up/down now functions as it should again.
The reason as far as I can see is due to ControllerX wanting to read the attribute color_temp:
2025-08-22 14:02:28.982802 ERROR Kitchen_Table_light: ValueError: Value for color_temp attribute could not be retrieved from light.kitchen_ceiling_table.
However while in color_mode: xy, the color_temp value is null (makes sense since we're not in that mode).
If I set any color_temp in Hass, color_temp now has a value and everything works from ControllerX again.
The reason it allows shifting from color_temp to color is since Z2M by default translates color_temp to color so an updated attribute for xy_color is always available.
Here's all the attributes for the light in Hass: Color mode: xy:
min_color_temp_kelvin: 2202
max_color_temp_kelvin: 4000
min_mireds: 250
max_mireds: 454
effect_list:
- blink
- breathe
- okay
- channel_change
- finish_effect
- stop_effect
- colorloop
- stop_colorloop
supported_color_modes:
- color_temp
- xy
effect: null
color_mode: xy
brightness: 128
color_temp_kelvin: null
color_temp: null
hs_color:
- 242.588
- 100
rgb_color:
- 11
- 0
- 255
xy_color:
- 0.136
- 0.04
icon: phu:ikea-ps-2014-open
friendly_name: Kitchen Ceiling Table
supported_features: 44
And color_mode: color_temp:
min_color_temp_kelvin: 2202
max_color_temp_kelvin: 4000
min_mireds: 250
max_mireds: 454
effect_list:
- blink
- breathe
- okay
- channel_change
- finish_effect
- stop_effect
- colorloop
- stop_colorloop
supported_color_modes:
- color_temp
- xy
effect: null
color_mode: color_temp
brightness: 128
color_temp_kelvin: 3003
color_temp: 333
hs_color:
- 27.82
- 56.814
rgb_color:
- 255
- 177
- 110
xy_color:
- 0.496
- 0.383
icon: phu:ikea-ps-2014-open
friendly_name: Kitchen Ceiling Table
supported_features: 44
The question? :) Could this be worked around in any way? I noticed for instance, on a different Ikea light, that the action: set_half_color_temp would switch it over to color_temp mode. But I can't really use that here since this controller is rather, let's call it "special" in how it sends commands and how it doesn't send release.. So I'm running out of actually usable buttons.
Additional information
- Devices involved:
- Model: Controller: E2123
- Light: LED2109G6 (https://www.zigbee2mqtt.io/devices/LED2109G6.html#ikea-led2109g6)
- Integration: z2m
- AppDaemon version: 0.17.8
- ControllerX version: v5.0.0
- HACS version 2.0.5
- Home Assistant Core version: 2025.8.3
AppDaemon app configuration
Kitchen_Table_light:
module: controllerx
class: E2123LightController
integration:
name: z2m
listen_to: mqtt
topic_prefix: sesvmalevz2m
controller: kitchen/controller/ceiling_table_light
light: light.kitchen_ceiling_table
Logs
AppDaemon logs:
Functioning commands for color_temp:
----------------------------------------------------------------
2025-08-22 14:02:07.303868 INFO Kitchen_Table_light: 🎮 Button event triggered: `dots_2_short_release`
2025-08-22 14:02:07.315063 INFO Kitchen_Table_light: 🏃 Running `Predefined (click_colortemp_up)` now
2025-08-22 14:02:07.344484 INFO Kitchen_Table_light:
🤖 Service: light.turn_on
- entity_id: light.kitchen_ceiling_table
- color_temp: 451
- transition: 0.30
----------------------------------------------------------------
Functioning commands for color:
----------------------------------------------------------------
2025-08-22 14:02:16.466250 INFO Kitchen_Table_light: 🎮 Button event triggered: `track_next`
2025-08-22 14:02:16.479937 INFO Kitchen_Table_light: 🏃 Running `Predefined (click_color_up)` now
2025-08-22 14:02:16.504429 INFO Kitchen_Table_light:
🤖 Service: light.turn_on
- entity_id: light.kitchen_ceiling_table
- xy_color: [0.217, 0.077]
- transition: 0.30
----------------------------------------------------------------
Now, after color, back to color_temp:
----------------------------------------------------------------
2025-08-22 14:02:28.886561 INFO Kitchen_Table_light: 🎮 Button event triggered: `dots_1_short_release`
2025-08-22 14:02:28.899822 INFO Kitchen_Table_light: 🏃 Running `Predefined (click_colortemp_down)` now
2025-08-22 14:02:28.980412 ERROR Kitchen_Table_light: ===== event_callback() in Kitchen_Table_light ===========================
2025-08-22 14:02:28.981071 ERROR Kitchen_Table_light: EventCallbackFail: Scheduled callback failed for app 'Kitchen_Table_light'
2025-08-22 14:02:28.981489 ERROR Kitchen_Table_light: args: ('MQTT_MESSAGE', {'topic': 'sesvmalevz2m/kitchen/controller/ceiling_table_light', 'wildcard': '#', 'payload': '{"action":"dots_1_short_release","battery":83,"identify":null,"last_seen":"2025-08-22T14:02:28+02:00","linkquality":109,"update":{"installed_version":16777269,"latest_version":16777269,"state":"idle"},"update_available":false,"voltage":2500}'}, {'topic': 'sesvmalevz2m/kitchen/controller/ceiling_table_light', '__thread_id': 'MainThread'})
2025-08-22 14:02:28.982802 ERROR Kitchen_Table_light: ValueError: Value for `color_temp` attribute could not be retrieved from `light.kitchen_ceiling_table`. Check the FAQ to know more about this error: https://xaviml.github.io/controllerx/faq
2025-08-22 14:02:28.985672 ERROR Kitchen_Table_light: File "/usr/lib/python3.12/site-packages/appdaemon/threads.py", line 986, in safe_callback
2025-08-22 14:02:28.986159 ERROR Kitchen_Table_light: await funcref()
2025-08-22 14:02:28.986591 ERROR Kitchen_Table_light: File "/config/apps/controllerx/controllerx/cx_core/integration/z2m.py", line 75, in event_callback
2025-08-22 14:02:28.987011 ERROR Kitchen_Table_light: await self.controller.handle_action(payload[action_key], extra=payload)
2025-08-22 14:02:28.987594 ERROR Kitchen_Table_light: File "/config/apps/controllerx/controllerx/cx_core/controller.py", line 392, in handle_action
2025-08-22 14:02:28.987986 ERROR Kitchen_Table_light: await self.call_action(action_key, extra=extra)
2025-08-22 14:02:28.988430 ERROR Kitchen_Table_light: File "/config/apps/controllerx/controllerx/cx_core/controller.py", line 466, in call_action
2025-08-22 14:02:28.988809 ERROR Kitchen_Table_light: await self.action_timer_callback({"action_key": action_key, "extra": extra})
2025-08-22 14:02:28.989324 ERROR Kitchen_Table_light: File "/config/apps/controllerx/controllerx/cx_core/controller.py", line 504, in action_timer_callback
2025-08-22 14:02:28.989666 ERROR Kitchen_Table_light: await task
2025-08-22 14:02:28.990253 ERROR Kitchen_Table_light: File "/config/apps/controllerx/controllerx/cx_core/controller.py", line 520, in call_action_types
2025-08-22 14:02:28.990636 ERROR Kitchen_Table_light: await action_type.run(extra=extra)
2025-08-22 14:02:28.991195 ERROR Kitchen_Table_light: File "/config/apps/controllerx/controllerx/cx_core/action_type/predefined_action_type.py", line 118, in run
2025-08-22 14:02:28.991572 ERROR Kitchen_Table_light: await action(*positional, **action_args)
2025-08-22 14:02:28.992055 ERROR Kitchen_Table_light: File "/config/apps/controllerx/controllerx/cx_core/controller.py", line 52, in _action_impl
2025-08-22 14:02:28.992648 ERROR Kitchen_Table_light: await method(controller, *args, **kwargs)
2025-08-22 14:02:28.993085 ERROR Kitchen_Table_light: File "/config/apps/controllerx/controllerx/cx_core/type/light_controller.py", line 831, in click
2025-08-22 14:02:28.993626 ERROR Kitchen_Table_light: self.value_attribute = await self.get_value_attribute(attribute)
2025-08-22 14:02:28.994004 ERROR Kitchen_Table_light: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-08-22 14:02:28.994387 ERROR Kitchen_Table_light: File "/config/apps/controllerx/controllerx/cx_core/type/light_controller.py", line 753, in get_value_attribute
2025-08-22 14:02:28.994876 ERROR Kitchen_Table_light: raise ValueError(
2025-08-22 14:02:28.995298 ERROR Kitchen_Table_light: ===========================================================================
----------------------------------------------------------------
Additional Context
The class E2123LightController doesn't exist currently so I added it myself into ikea.py I'm not done yet though, but here's the code: ''' class E2123LightController(LightController):
def get_z2m_actions_mapping(self) -> DefaultActionsMapping:
return {
"toggle": Light.TOGGLE,
"volume_up": Light.CLICK_BRIGHTNESS_UP,
"volume_down": Light.CLICK_BRIGHTNESS_DOWN,
"track_next": Light.CLICK_COLOR_UP,
"track_previous": Light.CLICK_COLOR_DOWN,
"dots_1_short_release": Light.CLICK_COLOR_TEMP_DOWN,
"dots_1_long_release": Light.RELEASE,
"dots_2_short_release": Light.CLICK_COLOR_TEMP_UP,
"dots_2_long_release": Light.RELEASE,
}
'''
Well, I kind of solved it.. However I doubt that it will be sustainable since I'm unsure how this affects other suppliers devices. But it works in my environment... so far My thinking was that if color_temp is supported but cannot be read from the state, let's just set a default middle value on it instead of throwing an error. Then changes to color_temp can originate from that value.
So I changed this in light_controller.py:
async def get_value_attribute(self, attribute: str) -> Number:
if self.smooth_power_on_check:
return 0
if attribute == LightController.ATTRIBUTE_XY_COLOR:
return 0
elif (
attribute == LightController.ATTRIBUTE_BRIGHTNESS
or attribute == LightController.ATTRIBUTE_WHITE_VALUE
or attribute == LightController.ATTRIBUTE_COLOR_TEMP
):
value = await self.get_entity_state(attribute=attribute)
if value is None:
raise ValueError(
f"Value for `{attribute}` attribute could not be retrieved "
f"from `{self.entity.main}`. "
"Check the FAQ to know more about this error: "
"https://xaviml.github.io/controllerx/faq"
)
else:
try:
return float(value)
except ValueError:
raise ValueError(
f"Attribute `{attribute}` with `{value}` as a value "
"could not be converted to float"
)
else:
raise ValueError(f"Attribute `{attribute}` not expected")
To this:
async def get_value_attribute(self, attribute: str) -> Number:
if self.smooth_power_on_check:
return 0
if attribute == LightController.ATTRIBUTE_XY_COLOR:
return 0
elif (
attribute == LightController.ATTRIBUTE_BRIGHTNESS
or attribute == LightController.ATTRIBUTE_WHITE_VALUE
or attribute == LightController.ATTRIBUTE_COLOR_TEMP
):
value = await self.get_entity_state(attribute=attribute)
if value is None:
return float(250)
else:
try:
return float(value)
except ValueError:
raise ValueError(
f"Attribute `{attribute}` with `{value}` as a value "
"could not be converted to float"
)
else:
raise ValueError(f"Attribute `{attribute}` not expected")
And, hey presto.
2025-08-22 19:27:44.758487 INFO Kitchen_Table_light: 🎮 Button event triggered: `track_next`
2025-08-22 19:27:44.762885 INFO Kitchen_Table_light: 🏃 Running `Predefined (click_color_up)` now
2025-08-22 19:27:44.775247 INFO Kitchen_Table_light:
🤖 Service: light.turn_on
- entity_id: light.kitchen_ceiling_table
- xy_color: [0.667, 0.284]
- transition: 0.30
2025-08-22 19:27:49.535949 INFO Kitchen_Table_light: 🎮 Button event triggered: `dots_2_short_release`
2025-08-22 19:27:49.543677 INFO Kitchen_Table_light: 🏃 Running `Predefined (click_colortemp_up)` now
2025-08-22 19:27:49.562988 INFO Kitchen_Table_light:
🤖 Service: light.turn_on
- entity_id: light.kitchen_ceiling_table
- color_temp: 284
- transition: 0.30
It works...
Although this is probably not the best way of doing it, I hope it shows what I'm after here anyway :)