Ikea Styrbar Force OTA if on V1.0.024

Ha I’m equally frightened! Guess I’ll be making an issue on git then when I get time off at work :joy:

I had a look in Zigbee2MQTT and its the same behavior in there too :frowning:

Issue created:

How did you login? as previously said I’m running Home Assistant OS from VirtualBox. When my machine is up and running I have access to Home Assistant CLI, trying Login then both root and my username but nothing is found. Wont let me through.
Using terminal from HA in browser only limits me to what I can already see from samba-share

I typed login at that command prompt.
Then I typed find / -name "zigpy"
I don’t fully understand the architecture and didn’t want to muck about in there :rofl:

What I ended up doing was to download deCONZ as I have a ConBee II Zigbee stick.
Then I downloaded the firmware from Ikea - http://fw.ota.homesmart.ikea.net/global/GW1.0/01.19.046/bin/10078031-zingo_kt_styrbar_remote-2.4.5.ota.ota.signed
Then I used the deCONZ to update the remote. It doesn’t check for version but took 2-3hrs to update :rofl: I probably had a bad setting on my side.


This process successfully updated the remote to the new version. See how you go with the recommended method on github, failing that - this could be a good option as it was pretty straightforward :crossed_fingers::grin:

The only small annoyance now is that the battery % is now reported as 0-255 in the new firmware where in the old it was 0-100. Perhaps I can download the latest quirk from github as it mentions being updated for 2.4.5 in the comments. Maybe homeassistant is yet to integrate this version.

To work around the battery % now bring reported as 0-255, I patched the latest quirk from github. The current version doesn’t patch this issue. And there is a comment regarding the battery and version meta issues so this will do for now :wink:

I then followed these instructions to install the custom quirk.

And the quirk itself inside fourbtnremote.py:

"""Device handler for IKEA of Sweden TRADFRI remote control."""
from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
from zigpy.zcl.clusters.general import (
    Basic,
    Identify,
    LevelControl,
    OnOff,
    Ota,
    PollControl,
    PowerConfiguration,
)
from zigpy.zcl.clusters.lightlink import LightLink

from zhaquirks.const import (
    CLUSTER_ID,
    COMMAND,
    COMMAND_HOLD,
    COMMAND_MOVE,
    COMMAND_MOVE_ON_OFF,
    COMMAND_OFF,
    COMMAND_ON,
    COMMAND_PRESS,
    COMMAND_STOP,
    COMMAND_STOP_ON_OFF,
    DEVICE_TYPE,
    DIM_DOWN,
    DIM_UP,
    ENDPOINT_ID,
    ENDPOINTS,
    INPUT_CLUSTERS,
    LEFT,
    LONG_PRESS,
    LONG_RELEASE,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PARAMS,
    PROFILE_ID,
    RIGHT,
    SHORT_PRESS,
    TURN_OFF,
    TURN_ON,
)
from zhaquirks.ikea import (
    IKEA,
    IKEA_CLUSTER_ID,
    WWAH_CLUSTER_ID,
    PowerConfiguration2AAACluster,
    ScenesCluster,
)

from zhaquirks import DoublingPowerConfigurationCluster

class PowerConfiguration2AAAClusterSTYRBAR_2_4_5(DoublingPowerConfigurationCluster):
    """Updating Power attributes 2 AAA."""
    """And for STYRBAR V2.4.5 as it scales battery voltage between 0-255."""

    BATTERY_SIZES = 0x0031
    BATTERY_QUANTITY = 0x0033
    BATTERY_RATED_VOLTAGE = 0x0034
    
    BATTERY_PERCENTAGE_REMAINING  = 0x0021

    _CONSTANT_ATTRIBUTES = {
        BATTERY_SIZES: 4,
        BATTERY_QUANTITY: 2,
        BATTERY_RATED_VOLTAGE: 15,
    }

    def _update_attribute(self, attrid, value):
        if attrid == self.BATTERY_PERCENTAGE_REMAINING:
            value = (value / 255) * 100
        super()._update_attribute(attrid, value)


class IkeaTradfriRemoteV1(CustomDevice):
    """Custom device representing IKEA of Sweden TRADFRI remote control V1.0.024."""

    signature = {
        # <SimpleDescriptor endpoint=1 profile=260 device_type=820
        # device_version=1
        # input_clusters=[0, 1, 3, 32, 4096, 64599]
        # output_clusters=[3, 6, 8, 25, 4096]>
        MODELS_INFO: [(IKEA, "Remote Control N2")],
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.NON_COLOR_CONTROLLER,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    PowerConfiguration.cluster_id,
                    Identify.cluster_id,
                    PollControl.cluster_id,
                    LightLink.cluster_id,
                    WWAH_CLUSTER_ID,
                ],
                OUTPUT_CLUSTERS: [
                    Identify.cluster_id,
                    OnOff.cluster_id,
                    LevelControl.cluster_id,
                    Ota.cluster_id,
                    LightLink.cluster_id,
                ],
            }
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.NON_COLOR_CONTROLLER,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    PowerConfiguration2AAACluster,
                    Identify.cluster_id,
                    PollControl.cluster_id,
                    LightLink.cluster_id,
                    WWAH_CLUSTER_ID,
                ],
                OUTPUT_CLUSTERS: [
                    Identify.cluster_id,
                    ScenesCluster,
                    OnOff.cluster_id,
                    LevelControl.cluster_id,
                    Ota.cluster_id,
                    LightLink.cluster_id,
                ],
            }
        }
    }

    device_automation_triggers = {
        (SHORT_PRESS, TURN_ON): {COMMAND: COMMAND_ON, CLUSTER_ID: 6, ENDPOINT_ID: 1},
        (LONG_PRESS, DIM_UP): {
            COMMAND: COMMAND_MOVE_ON_OFF,
            CLUSTER_ID: 8,
            ENDPOINT_ID: 1,
            PARAMS: {"move_mode": 0},
        },
        (LONG_RELEASE, DIM_UP): {
            COMMAND: COMMAND_STOP_ON_OFF,
            CLUSTER_ID: 8,
            ENDPOINT_ID: 1,
        },
        (SHORT_PRESS, TURN_OFF): {COMMAND: COMMAND_OFF, CLUSTER_ID: 6, ENDPOINT_ID: 1},
        (LONG_PRESS, DIM_DOWN): {
            COMMAND: COMMAND_MOVE,
            CLUSTER_ID: 8,
            ENDPOINT_ID: 1,
            PARAMS: {"move_mode": 1},
        },
        (LONG_RELEASE, DIM_DOWN): {
            COMMAND: COMMAND_STOP,
            CLUSTER_ID: 8,
            ENDPOINT_ID: 1,
        },
        (SHORT_PRESS, LEFT): {
            COMMAND: COMMAND_PRESS,
            CLUSTER_ID: 5,
            ENDPOINT_ID: 1,
            PARAMS: {
                "param1": 257,
                "param2": 13,
                "param3": 0,
            },
        },
        (LONG_PRESS, LEFT): {
            COMMAND: COMMAND_HOLD,
            CLUSTER_ID: 5,
            ENDPOINT_ID: 1,
            PARAMS: {
                "param1": 3329,
                "param2": 0,
            },
        },
        (SHORT_PRESS, RIGHT): {
            COMMAND: COMMAND_PRESS,
            CLUSTER_ID: 5,
            ENDPOINT_ID: 1,
            PARAMS: {
                "param1": 256,
                "param2": 13,
                "param3": 0,
            },
        },
        (LONG_PRESS, RIGHT): {
            COMMAND: COMMAND_HOLD,
            CLUSTER_ID: 5,
            ENDPOINT_ID: 1,
            PARAMS: {
                "param1": 3328,
                "param2": 0,
            },
        },
    }


class IkeaTradfriRemoteV2(CustomDevice):
    """Custom device representing IKEA of Sweden TRADFRI remote control Version 2.4.5."""

    signature = {
        # <SimpleDescriptor endpoint=1 profile=260 device_type=820
        # device_version=1
        # input_clusters=[0, 1, 3, 32, 4096, 64599, 64636]
        # output_clusters=[3, 5, 6, 8, 25, 4096]>
        MODELS_INFO: [(IKEA, "Remote Control N2")],
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.NON_COLOR_CONTROLLER,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    PowerConfiguration.cluster_id,
                    Identify.cluster_id,
                    PollControl.cluster_id,
                    LightLink.cluster_id,
                    WWAH_CLUSTER_ID,
                    IKEA_CLUSTER_ID,
                ],
                OUTPUT_CLUSTERS: [
                    Identify.cluster_id,
                    ScenesCluster.cluster_id,
                    OnOff.cluster_id,
                    LevelControl.cluster_id,
                    Ota.cluster_id,
                    LightLink.cluster_id,
                ],
            }
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.NON_COLOR_CONTROLLER,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    PowerConfiguration2AAAClusterSTYRBAR_2_4_5,
                    Identify.cluster_id,
                    PollControl.cluster_id,
                    LightLink.cluster_id,
                    WWAH_CLUSTER_ID,
                    IKEA_CLUSTER_ID,
                ],
                OUTPUT_CLUSTERS: [
                    Identify.cluster_id,
                    ScenesCluster,
                    OnOff.cluster_id,
                    LevelControl.cluster_id,
                    Ota.cluster_id,
                    LightLink.cluster_id,
                ],
            }
        }
    }

    device_automation_triggers = IkeaTradfriRemoteV1.device_automation_triggers.copy()

Don’t know if its right, but it seems to work and is good enough for now :joy:

1 Like

Ty for taking your time :smiley:
I managed to SSH into machine this morning aswell as find the appropriate init.py and modify the lines that check version. Had to leave for work before I could try the update so tonight if all goes well… :stuck_out_tongue:

Such a hassle this update turned out to be…!
I aint buying a conbee stick just for an OTA of stupid ikea devices :smiley:

Update underway yay :slight_smile: I’ll do a proper write up tonight how I did it for those that are in the same pickle

Could you share details on where you edited the init.py file? Am using HA Supervised and can’t locate the file.

Also having difficulty in getting ZHA to manually update the firmware (despite having downloaded the modded firmware).

Sure I can share all the details later today please be patient, got some things to finish up on the family front before I can have a sit at my PC :slight_smile: I’m trying to write a small guide, not finished yet but should have the basics written down

No worries! By stroke of luck, I managed to trigger the manual update.

But will be good to know nevertheless!

Hope you find it helpful. I was primarly writing the guide for myself but in a way as if I might share it :stuck_out_tongue: Ain’t the best guide-writer

1 Like

I’ve installed the quirk. Now see “Quirk: fourbtnremote.IkeaTradfriRemoteV2” in the Device info. But the battery level is still showing as 255%.

The only step I couldn’t follow was “you need deleting the __pycache__ folder in your temporary quirk fold” because I had no pycache folder in any temporary folder. I had one in /config/custom_zha_quirks/, but even after I deleted it and restarted HA, the battery was still shown as 255%.

Do I maybe need to re-add the Remote Control N2 device to my setup?

Do I maybe need to re-add the Remote Control N2 device to my setup?

OK. I see I do. :face_with_hand_over_mouth:

1 Like

I get this error after following your tutorial.

2023-03-11 12:32:15.891 ERROR (MainThread) [homeassistant.core] Error executing service: <ServiceCall zha.issue_zigbee_cluster_command (c:01GV83MDR64TFPXRSRK7YWWE7V): ieee=0c:43:14:ff:fe:a6:3a:4c, endpoint_id=1, cluster_id=25, cluster_type=out, command=0, command_type=client, params=payload_type=QueryJitter, query_jitter=100>
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/core.py", line 1826, in catch_exceptions
    await coro_or_task
  File "/usr/src/homeassistant/homeassistant/core.py", line 1845, in _execute_service
    await cast(Callable[[ServiceCall], Awaitable[None]], handler.job.target)(
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 762, in admin_handler
    await result
  File "/usr/src/homeassistant/homeassistant/components/zha/api.py", line 1336, in issue_zigbee_cluster_command
    await zha_device.issue_cluster_command(
  File "/usr/src/homeassistant/homeassistant/components/zha/core/device.py", line 727, in issue_cluster_command
    response = await getattr(cluster, commands[command].name)(
  File "/usr/local/lib/python3.10/site-packages/zigpy/zcl/__init__.py", line 358, in reply
    return await self._endpoint.reply(
  File "/usr/local/lib/python3.10/site-packages/zigpy/endpoint.py", line 256, in reply
    return await self.device.reply(
  File "/usr/local/lib/python3.10/site-packages/zigpy/device.py", line 384, in reply
    return await self.request(
  File "/usr/local/lib/python3.10/site-packages/zigpy/device.py", line 297, in request
    await self._application.request(
  File "/usr/local/lib/python3.10/site-packages/zigpy/application.py", line 692, in request
    await self.send_packet(
  File "/usr/local/lib/python3.10/site-packages/zigpy_znp/zigbee/application.py", line 1162, in send_packet
    raise DeliveryError(
zigpy.exceptions.DeliveryError: Request failed after 5 attempts: 

I am also not filling those up. Do you know the values to put there?

Hello,
Did you press any button to wake the device before you issued the command from HA?

I did not fill any other fields only adjusted query jitter as you did

I think it worked now, thanks

So went trough the whole update process and still on 1.0.024. Saw the update in the log till 100%. Must be doing something wrong.

2023-03-11 17:41:35.343 DEBUG (MainThread) [zigpy.zcl] [0xABBB:1:0x0019] OTA upgrade_end handler for 'IKEA of Sweden Remote Control N2': status=Status.SUCCESS, manufacturer_id=4476, image_type=4555, file_version=581

2023-03-11 17:41:35.344 DEBUG (MainThread) [zigpy.zcl] [0xABBB:1:0x0019] Sending reply header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.CLUSTER_COMMAND: 1>, is_manufacturer_specific=False, direction=<Direction.Client_to_Server: 1>, disable_default_response=1, reserved=0, *is_cluster=True, *is_general=False, *is_reply=True), tsn=1, command_id=7, *direction=<Direction.Client_to_Server: 1>, *is_reply=True)

2023-03-11 17:41:35.344 DEBUG (MainThread) [zigpy.zcl] [0xABBB:1:0x0019] Sending reply: upgrade_end_response(manufacturer_code=4476, image_type=4555, file_version=581, current_time=0, upgrade_time=0)

file_version=581 is this right?

1 Like

I had to re-pair my device after update ':slight_smile: try that

Also follow this post afterwards to fix the battery showing as above 100%

Do we still need to apply this manual quirk workaround as of 2023.4 ?