Ikea Styrbar Force OTA if on V1.0.024

Hello!

This is my sloppely written guide for the time that you want to do OTA manually because for instance in my case I had old firmware on my IKEA Styrbar remote that could not get auto-updated because of how the newer version of firmware reported its version number.

This is written from my setups perspective as I’m not `linux-savvy´ I can’t say that it will work the same for every setup. And in the way I’ve interpreted what I’ve read from various sources so bare with me if I’m off on some terminology.

Also NOTE I’m assuming a certain skill-level when it comes to theese types of edits
Skip the parts you’ve got covered already (for instance SSH access to host)

I’m running Home Assistant OS on Oracle VM.

Step 1: We need access to the Container housing our Home Assistant on our host machine in order to modify a specific file. In order to get this access we need to enable SSH access to our host machine (source1: Debugging the Home Assistant Operating System | Home Assistant Developer Docs)
(source2: operating-system/Documentation/configuration.md at dev · home-assistant/operating-system · GitHub)

  • Format a thumb-drive as FAT32/NTFS and label it CONFIG (case-sensitive)
  • Insert into PC where our VM is running and pass the thumb-drive through to Home Assistant OS. This is just to test that the VM can pick it up. (One of my usb-drives wasn’t detectable)
  • When step 2 is OK unmount drive from VM
  • Download and install PuTTy (https://www.putty.org/)
  • Follow guide to generate a public/private key
    IBM Documentation
  • Using Notepad++ create a new text document and paste in the contents of the generated public key from PuTTy. Change/convert encoding to ANSI from the top menu options and in the bottom right, third from right, Check that you change “Windows (CR LF)” to “Unix (LF)”. Then save the file as authorized_keys (without extension) in the root of your thumb-drive.
  • Save Private key somewhere on your PC, you need this for your SSH Connection later
  • Insert thumb-drive to PC with VM, pass thumb-drive to VM and from the Home Assistant CLI type Os Import. After this step is done port 22222 should be open on your machine so you can access Home Assistant cointainer

Step 2: Use PuTTy to SSH into your machine at the newly exposed port 22222 with the previously generated private key. (How to use SSH Keys with PuTTY - IONOS)
image

Connect with root@VM-IP on port 22222 and enter your private key pass if you created one
image

Step 3: If successfully connected to host machine the following command should be issued first:

docker exec -it homeassistant /bin/bash

Step 3.1 (only once): write the following command to install suplemon for textediting

pip install suplemon

Step 4: Now we need to edit the init.py file of the zigpy package. By writing the following command you should open the correct file in suplemon editor. (Notice, python versions may change, check with command “python –version” and edit path accordingly)

suplemon /usr/local/lib/python3.10/site-packages/zigpy/ota/__init__.py

Step 5: Navigate inside the __init__.py in Suplemon to find the row below

if ver >= self.version:

This needs to be edited to the code below in order to trigger on any OTA-image that isn’t equal to the currently installed one

if ver == self.version:

Save the file with CTRL+S then close it with CTRL+Q

Step 6 (Optional): If you wish to downgrade for some reason

Step 7 (Optional): Enable logging in order to track that the OTA-image is accepted as well as progress by adding the following lines to your configuration.yaml

  logger:
  default: info
  logs:
    homeassistant.components.zha: debug
    zigpy: debug

(source: OTA Device Firmware Updates · zigpy/zigpy Wiki · GitHub)

Step 8 : Enable OTA updates by editing configuration.yaml according to the link below

Restart Home assistant!

Step 9: Manually force OTA on selected devices according to the documentation below found, I Did 3-4 devices at a time without issue. This should pull the latest firmware from the cloud

And that’s about it. Good luck!

Old Post:

1 Like

Just tried to do this myself today and had the same issue. I think I know the issue.

Looking at the log you can see the remote reporting: current_file_version=65572
And the OTA image version is: OTA image version: 581

65572 in HEX is 10024
581 in HEX is 245

Now looking at the IKEA release notes the current remote’s current version is V-2.4.5 and the last version is V-1.0.024.

zigpy uses if ver >= self.version to see if the OTA image is newer before initiating an update. It seems the way the IKEA remote is reporting the version is making zigpy think the update is older :frowning:

The documentation for zigpy (OTA Device Firmware Updates · zigpy/zigpy Wiki · GitHub) mentions editing zigpy/ota/__init__.py to allow downgrade. Maybe we could use a similar suggestion and set if ver != self.version just to get this update applied. I don’t think its a bug in zigpy but rather a problem the way the version has been done for the IKEA remote.

My problem is I can’t find the file to try this idea out :frowning:

Hey
Thanks for looking into this. I saw that aswell and was willing to try it but got to late had to go to bed after my post. Being workdays and all :stuck_out_tongue_winking_eye:

Aye can’t seem to find those files either. I’m run HA in Oracles VM. Do we need to edit through terminal perhaps?

I’m running the Home Assistant Operating System (formerly HassOS) on a VM in Proxmox.

I first had a look using the terminal add-on in Home Assistant itself and couldn’t find anything.

I then went to the terminal on the VM itself, typed login and found multiple references to the file under folders docker/overlay so I’m not brave enough to fiddle around in there :joy:

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