Somfy Connexoon/Tahoma jailbreak

hello,
it works perfectly for me!
But with the new version of home assistant 2022.2.2, does it still work?

Awesome discovery. Thanks for your work on this!

Has anyone tried this with Cozytouch?

I got ssh access into the box and was able to add it to home assistant but it only displays some presets (which don’t work). The sensors and the climate entity are missing (for the radiator). I assume it’s because only some of the integration has been reworked to function locally?

I get this for the device

And I see this under entities

In the normal integration i see this (example of one heater)

The JSON it returns via the local API is probably just slightly different from the regular one. At least that’s how it was with my connexoon. You can check by visiting:

http://192.168.x.x/enduser-mobile-web/1/enduserAPI/setup/devices

It does need your local token in the headers:

X-Auth-Token: <token>

Just FYI this is what my output looks like:

[
  {
    "deviceURL": "internal://1234-1234-1234/pod/0",
    "available": true,
    "synced": true,
    "type": 1,
    "states": [
      {
        "type": 3,
        "name": "core:CountryCodeState",
        "value": "NL"
      },
      {
        "type": 1,
        "name": "internal:LightingLedPodModeState",
        "value": 1
      },
      {
        "type": 3,
        "name": "core:NameState",
        "value": "Box"
      },
      {
        "type": 3,
        "name": "core:ConnectivityState",
        "value": "online"
      },
      {
        "type": 3,
        "name": "core:LocalIPv4AddressState",
        "value": "N/A"
      }
    ],
    "label": "Box",
    "subsystemId": 0,
    "attributes": [],
    "enabled": true,
    "controllableName": "internal:PodMiniComponent",
    "definition": {
      "states": [
        {
          "name": "core:ConnectivityState"
        },
        {
          "name": "core:CountryCodeState"
        },
        {
          "name": "core:LocalIPv4AddressState"
        },
        {
          "name": "core:NameState"
        },
        {
          "name": "internal:LastActionConfigButtonState"
        },
        {
          "name": "internal:LightingLedPodModeState"
        }
      ],
      "widgetName": "Pod",
      "type": "ACTUATOR",
      "commands": [
        {
          "commandName": "activateCalendar",
          "nparams": 0
        },
        {
          "commandName": "deactivateCalendar",
          "nparams": 0
        },
        {
          "commandName": "getName",
          "nparams": 0
        },
        {
          "commandName": "refreshPodMode",
          "nparams": 0
        },
        {
          "commandName": "refreshUpdateStatus",
          "nparams": 0
        },
        {
          "nparams": 1,
          "commandName": "setCalendar",
          "paramsSig": "p1"
        },
        {
          "nparams": 1,
          "commandName": "setCountryCode",
          "paramsSig": "p1"
        },
        {
          "nparams": 1,
          "commandName": "setLightingLedPodMode",
          "paramsSig": "p1"
        },
        {
          "commandName": "setPodLedOff",
          "nparams": 0
        },
        {
          "commandName": "setPodLedOn",
          "nparams": 0
        },
        {
          "commandName": "update",
          "nparams": 0
        }
      ],
      "uiClass": "Pod"
    }
  },
  {
    "deviceURL": "io://1234-1234-1234/5928357",
    "available": true,
    "synced": true,
    "type": 1,
    "states": [
      {
        "type": 3,
        "name": "core:StatusState",
        "value": "available"
      },
      {
        "type": 3,
        "name": "core:DiscreteRSSILevelState",
        "value": "good"
      },
      {
        "type": 1,
        "name": "core:RSSILevelState",
        "value": 86
      },
      {
        "type": 1,
        "name": "core:DeploymentState",
        "value": 0
      },
      {
        "type": 11,
        "name": "core:ManufacturerSettingsState",
        "value": {
          "current_position": 0
        }
      },
      {
        "type": 3,
        "name": "core:OpenClosedState",
        "value": "closed"
      },
      {
        "type": 1,
        "name": "core:TargetClosureState",
        "value": 0
      },
      {
        "type": 6,
        "name": "core:MovingState",
        "value": false
      },
      {
        "type": 3,
        "name": "core:NameState",
        "value": "Zonnescherm"
      },
      {
        "type": 1,
        "name": "core:Memorized1PositionState",
        "value": 105
      }
    ],
    "label": "Zonnescherm",
    "subsystemId": 0,
    "attributes": [
      {
        "type": 3,
        "name": "core:Manufacturer",
        "value": "Somfy"
      },
      {
        "type": 3,
        "name": "core:FirmwareRevision",
        "value": "5071665X10\u0003"
      }
    ],
    "enabled": true,
    "controllableName": "io:HorizontalAwningIOComponent",
    "definition": {
      "states": [
        {
          "name": "core:AdditionalStatusState"
        },
        {
          "name": "core:DeploymentState"
        },
        {
          "name": "core:DiscreteRSSILevelState"
        },
        {
          "name": "core:ManufacturerSettingsState"
        },
        {
          "name": "core:Memorized1PositionState"
        },
        {
          "name": "core:MovingState"
        },
        {
          "name": "core:NameState"
        },
        {
          "name": "core:OpenClosedState"
        },
        {
          "name": "core:RSSILevelState"
        },
        {
          "name": "core:SecuredPositionState"
        },
        {
          "name": "core:StatusState"
        },
        {
          "name": "core:TargetClosureState"
        }
      ],
      "widgetName": "PositionableHorizontalAwning",
      "type": "ACTUATOR",
      "commands": [
        {
          "nparams": 1,
          "commandName": "advancedRefresh",
          "paramsSig": "p1"
        },
        {
          "commandName": "close",
          "nparams": 0
        },
        {
          "nparams": 1,
          "commandName": "delayedStopIdentify",
          "paramsSig": "p1"
        },
        {
          "commandName": "deploy",
          "nparams": 0
        },
        {
          "commandName": "down",
          "nparams": 0
        },
        {
          "commandName": "getName",
          "nparams": 0
        },
        {
          "commandName": "identify",
          "nparams": 0
        },
        {
          "commandName": "keepOneWayControllersAndDeleteNode",
          "nparams": 0
        },
        {
          "commandName": "my",
          "nparams": 0
        },
        {
          "commandName": "open",
          "nparams": 0
        },
        {
          "nparams": 1,
          "commandName": "pairOneWayController",
          "paramsSig": "p1,*p2"
        },
        {
          "commandName": "refreshMemorized1Position",
          "nparams": 0
        },
        {
          "nparams": 2,
          "commandName": "runManufacturerSettingsCommand",
          "paramsSig": "p1,p2"
        },
        {
          "commandName": "sendIOKey",
          "nparams": 0
        },
        {
          "nparams": 1,
          "commandName": "setClosure",
          "paramsSig": "p1"
        },
        {
          "nparams": 1,
          "commandName": "setConfigState",
          "paramsSig": "p1"
        },
        {
          "nparams": 1,
          "commandName": "setDeployment",
          "paramsSig": "p1"
        },
        {
          "nparams": 1,
          "commandName": "setMemorized1Position",
          "paramsSig": "p1"
        },
        {
          "nparams": 1,
          "commandName": "setName",
          "paramsSig": "p1"
        },
        {
          "nparams": 1,
          "commandName": "setPosition",
          "paramsSig": "p1"
        },
        {
          "nparams": 1,
          "commandName": "setSecuredPosition",
          "paramsSig": "p1"
        },
        {
          "commandName": "startIdentify",
          "nparams": 0
        },
        {
          "commandName": "stop",
          "nparams": 0
        },
        {
          "commandName": "stopIdentify",
          "nparams": 0
        },
        {
          "commandName": "undeploy",
          "nparams": 0
        },
        {
          "commandName": "unpairAllOneWayControllers",
          "nparams": 0
        },
        {
          "commandName": "unpairAllOneWayControllersAndDeleteNode",
          "nparams": 0
        },
        {
          "nparams": 1,
          "commandName": "unpairOneWayController",
          "paramsSig": "p1,*p2"
        },
        {
          "commandName": "up",
          "nparams": 0
        },
        {
          "nparams": 1,
          "commandName": "wink",
          "paramsSig": "p1"
        }
      ],
      "uiClass": "Awning"
    }
  },
  {
    "deviceURL": "io://1234-1234-1234/16516299",
    "available": true,
    "synced": true,
    "type": 5,
    "states": [],
    "label": "IO (16516299)",
    "subsystemId": 0,
    "attributes": [
      {
        "type": 3,
        "name": "core:Manufacturer",
        "value": "Somfy"
      }
    ],
    "enabled": true,
    "controllableName": "io:StackComponent",
    "definition": {
      "states": [],
      "widgetName": "IOStack",
      "type": "PROTOCOL_GATEWAY",
      "commands": [
        {
          "nparams": 1,
          "commandName": "advancedSomfyDiscover",
          "paramsSig": "p1"
        },
        {
          "nparams": 0,
          "commandName": "discover1WayController",
          "paramsSig": "*p1,*p2"
        },
        {
          "nparams": 1,
          "commandName": "discoverActuators",
          "paramsSig": "p1"
        },
        {
          "nparams": 1,
          "commandName": "discoverSensors",
          "paramsSig": "p1"
        },
        {
          "commandName": "discoverSomfyUnsetActuators",
          "nparams": 0
        },
        {
          "commandName": "joinNetwork",
          "nparams": 0
        },
        {
          "commandName": "resetNetworkSecurity",
          "nparams": 0
        },
        {
          "commandName": "shareNetwork",
          "nparams": 0
        }
      ],
      "uiClass": "ProtocolGateway"
    }
  }
]```

Thanks for the suggestions. I think the issue might be the local api / hub. Based on your output it doesn’t look like my bridge is reporting any radiators (or at least I think that’s what it is saying). The output is certainly shorter than yours!

They do show up in the cozytouch app so I’m unsure what’s going on.

I get the following from the request (http://192.168.x.x/enduser-mobile-web/1/enduserAPI/setup/devices)

[
  {
    "deviceURL": "io://1234-1234-1234/9775556",
    "available": true,
    "synced": true,
    "type": 5,
    "states": [],
    "label": "IO (9775556)",
    "subsystemId": 0,
    "attributes": [
      {
        "type": 3,
        "name": "core:Manufacturer",
        "value": "Somfy"
      }
    ],
    "enabled": true,
    "controllableName": "io:StackComponent",
    "definition": {
      "states": [],
      "widgetName": "IOStack",
      "type": "PROTOCOL_GATEWAY",
      "commands": [
        {
          "nparams": 1,
          "commandName": "discoverActuators",
          "paramsSig": "p1"
        },
        {
          "commandName": "joinNetwork",
          "nparams": 0
        },
        {
          "nparams": 1,
          "commandName": "advancedSomfyDiscover",
          "paramsSig": "p1"
        },
        {
          "commandName": "resetNetworkSecurity",
          "nparams": 0
        },
        {
          "commandName": "shareNetwork",
          "nparams": 0
        },
        {
          "nparams": 0,
          "commandName": "discover1WayController",
          "paramsSig": "*p1,*p2"
        },
        {
          "nparams": 1,
          "commandName": "discoverSensors",
          "paramsSig": "p1"
        },
        {
          "commandName": "discoverSomfyUnsetActuators",
          "nparams": 0
        }
      ],
      "uiClass": "ProtocolGateway"
    }
  },
  {
    "deviceURL": "internal://1234-1234-1234/pod/0",
    "available": true,
    "synced": true,
    "type": 1,
    "states": [
      {
        "type": 1,
        "name": "internal:LightingLedPodModeState",
        "value": 1
      },
      {
        "type": 3,
        "name": "core:CountryCodeState",
        "value": "NZ"
      },
      {
        "type": 3,
        "name": "core:LocalIPv4AddressState",
        "value": "N/A"
      },
      {
        "type": 3,
        "name": "core:NameState",
        "value": "Box"
      },
      {
        "type": 3,
        "name": "core:ConnectivityState",
        "value": "offline"
      }
    ],
    "label": "Box",
    "subsystemId": 0,
    "attributes": [],
    "enabled": true,
    "controllableName": "internal:PodMiniComponent",
    "definition": {
      "states": [
        {
          "name": "core:ConnectivityState"
        },
        {
          "name": "core:LocalIPv4AddressState"
        },
        {
          "name": "core:CountryCodeState"
        },
        {
          "name": "internal:LightingLedPodModeState"
        },
        {
          "name": "core:NameState"
        },
        {
          "name": "internal:LastActionConfigButtonState"
        }
      ],
      "widgetName": "Pod",
      "type": "ACTUATOR",
      "commands": [
        {
          "commandName": "deactivateCalendar",
          "nparams": 0
        },
        {
          "commandName": "refreshPodMode",
          "nparams": 0
        },
        {
          "commandName": "getName",
          "nparams": 0
        },
        {
          "commandName": "setPodLedOff",
          "nparams": 0
        },
        {
          "nparams": 1,
          "commandName": "setCalendar",
          "paramsSig": "p1"
        },
        {
          "commandName": "setPodLedOn",
          "nparams": 0
        },
        {
          "commandName": "activateCalendar",
          "nparams": 0
        },
        {
          "nparams": 1,
          "commandName": "setLightingLedPodMode",
          "paramsSig": "p1"
        },
        {
          "commandName": "update",
          "nparams": 0
        },
        {
          "commandName": "refreshUpdateStatus",
          "nparams": 0
        },
        {
          "nparams": 1,
          "commandName": "setCountryCode",
          "paramsSig": "p1"
        }
      ],
      "uiClass": "Pod"
    }
  }
]

Hi!

So you know, I’m the one who found the Jailbreak method using the USB debug interface on the TaHoma. Just passing by to let you know that my project was only on hold but that I’m still working on it.

I’m amazed by what this community was able to pull off with it! I saw the internal application but was unable to find how to enable it and put it to actual use. Great job!

On my side, out of curiosity, I’m still working on reversing the actual radio protocol. I have some insights and am able to decode some parts of it sniffed directly on the air. The protocol frames are cryptographically signed but not encrypted :slight_smile:

For that, I’m creating a custom OS stack for the TaHoma. So far I have an operational bootloader and u-boot.

I’ll keep updates on Github but will for sure check this thread from time to time.

8 Likes

Fyi, I posted a small update to the blog:

TL;DR, there are some changes and you can use the newly introduced developer mode if you’re willing to generate SSL certificates! Also check out this topic: Overkiz API and Somfy API - #1777 by imick

1 Like

I have a connexoon, wanted to jailbreak but i’m not getting the Atmel bootloader. When i shortcut pin 9 and 3,3v indeed the power LED is off but nothing happens. Tried several times and with different USB cables…

Does anyone have a functioning patched image they can share? I tried the guide on the newest firmware that was released a few weeks ago, but that doesn’t seem to work.

For what is this relevant now since somfy switches to local API on official Image?

As far as I know that’s only true for the Tahoma, not the Connexoon

I tried the jailbreak on the latest firmware just now. It still works but some things that changed. The biggest change is that rootB is now a static partition. This means that it is read-only. You can change this by repacking the ubi-volume.bin and making it a dynamic partition again.

So, in short, after the step Create local folders on my blog follow the steps listed at Rebuild the firmware.

Then, rename ubi-volume_patched.bin to ubi-volume_dynamic.bin.

Now, continue from the step Prepare the virtual nand, but replace every occurence of ubi-volume.bin with ubi-volume_dynamic.bin

The second change is that there is a script that checks if the partition MD5’s match known hashes and force updates them if they don’t. To prevent this you can either block outgoing traffic from your connexoon with a firewall, or cripple the /usr/local/scripts/systemcheck script (location might differ, I’m writing this from memory).

From there, if you create the proper certificates, you should be able to use the local mode on the connexoon just as you can on the tahoma.

1 Like

I’m sorry, I don’t follow. If I execute the steps listed at Rebuild the firmware right after creating the local folders it fails (ubinfo:“error!: UBI is not present in the system”). That’s what I would expect, there’s nothing mounted, there are only empty directories.

Oh, yeah, correct. Like I said, writing from memory ;). It obviously needs to be mounted first.

I want to check if I understand the correct procedure.

Dump the firmware
Create directories
Prepare virtual nand
Mount image
Resize
(Don’t do anything to alter the filesystem)
Rebuild firmware

Use that rebuilt firmware to go through the process again, only this time makes the changes with dropbear and ssh.
Flash

Is that the gist of it?

I just went trough the whole process on the latest firmware.

Here are the steps from Donnys Blog with the latest updates for the new FW from Somfy:

Dumping the firmware

./sam-ba -p usb -b sam9xx5-ek -a lowlevel
./sam-ba -p usb -b sam9xx5-ek -a extram
./sam-ba -p serial -d sam9xx5 -a nandflash:1:8:0xc0902405 -c read:bootstrap.bin:0x000000:0x20000 -c read:ubi-volume.bin:0x20000

Create working folders

mkdir ubi-root
mkdir ubi-rootB
mkdir extract

Prepare the virtual NAND to change the partition from static to dynamic

sudo modprobe nandsim first_id_byte=0xec second_id_byte=0xa1 third_id_byte=0x00 fourth_id_byte=0x15;
sudo flash_erase /dev/mtd0 0 0
sudo nandwrite /dev/mtd0 ubi-volume.bin
sudo modprobe ubi
sudo ubiattach -p /dev/mtd0 -O 2048

Make the partitions dynamic

for i in {0..10}; do name=$(ubinfo -d 0 -n $i | grep Name | awk '{print $2}'); sudo dd if=/dev/ubi0_$i of=extract/$name.bin; done

cd extract
for i in {0..5}; do name=$(ubinfo -d 0 -n $i | grep Name | awk '{print $2}');size=$(ubinfo -d 0 -n $i | grep Size | cut -d '(' -f 2 | cut -d ' ' -f 1); echo -en "[$name]\nmode=ubi\nimage=$name.bin\nvol_id=$i\nvol_size=$size\nvol_type=static\nvol_name=$name\nvol_alignment=1\n\n"; done > config.ini
for i in {6..10}; do name=$(ubinfo -d 0 -n $i | grep Name | awk '{print $2}');size=$(ubinfo -d 0 -n $i | grep Size | cut -d '(' -f 2 | cut -d ' ' -f 1); echo -en "[$name]\nmode=ubi\nimage=$name.bin\nvol_id=$i\nvol_size=$size\nvol_type=dynamic\nvol_name=$name\nvol_alignment=1\n\n"; done >> config.ini

ubinize -o ../ubi-volume_dynamic.bin -p 131072 -m 2048 -O 2048 -s 512 -Q 1056559212 config.ini
cd ..

Clean-up (I don’t know if this needed at this point but it worked for me)

sudo umount /dev/ubi0_*
sudo ubidetach -p /dev/mtd0
sudo rmmod ubifs ubi nandsim

Prepare the virtual NAND with the new dynamic partitions

sudo modprobe nandsim first_id_byte=0xec second_id_byte=0xa1 third_id_byte=0x00 fourth_id_byte=0x15;
sudo flash_erase /dev/mtd0 0 0
sudo nandwrite /dev/mtd0 ubi-volume_dynamic.bin
sudo modprobe ubi
sudo ubiattach -p /dev/mtd0 -O 2048

Resize root partition (need for the latest FW)

sudo ubirsvol /dev/ubi0 -n 7 -S 206

Mount the “root” and “rootB” partition

sudo mount -t ubifs -o rw /dev/ubi0_7 ubi-root
sudo mount -t ubifs -o rw /dev/ubi0_9 ubi-rootB

Enable dropbear

cd ubi-root/etc/rc5.d
sudo ln -s ../init.d/dropbear S06dropbear
cd ../../../
cd ubi-root/etc/rc2.d
sudo ln -s ../init.d/dropbear S06dropbear
cd ../../../

cd ubi-rootB/etc/rc5.d
sudo ln -s ../init.d/dropbear S06dropbear
cd ../../../
cd ubi-rootB/etc/rc2.d
sudo ln -s ../init.d/dropbear S06dropbear
cd ../../../

After this continue from here: No clouds, just sunshine. Disconnecting Somfy Connexoon from the cloud. - Unauthorized Access Blog

Do the authentication changes in “ubi-root”.

2 Likes

@dmaasland

Thanks for the updates on this! Could you please explain the process to generate the SSL certificates and how to integrate them?

Also, if you want to use the new developer mode on your Connexoon you will either have to generate SSL certificates yourself. See the configuration files at /etc/lighttpd.d/ssl.conf and /etc/lighttpd.d/8443/devmodesocket.conf for hints.

Can you contact me on Discord? I would be interested to see how we eventually can incorporate this one into core, together with the official Somfy Developer integration.

3 Likes

Hello!

Just so you know. I finally cracked io-homecontrol authentication mechanisms. You will find details here: iown-homecontrol/LinkLayer.md at 6e2361f7971c8f02d9dc1c05b8510109202163fb · Aldohrs/iown-homecontrol · GitHub

And here is the key that keeps everything together (the key used to encrypt other keys sent on the air):

34c3466ed88f4e8e16aa473949884373

As this key is hardcoded, I have no doubt they can’t change it just like that.

Enjoy replaying/forge your own io-homecontrol frames. I don’t have much details on how to have a complete specification for all devices but I figured out while intercepting frames of my devices that once authentication is sorted out, frame replay should be easy.

5 Likes

Hi @dmaasland,
I have been using your tweaked version of ha-tahoma for quite some time now, but after the Home Assistant update on 11.2022 it does not work anymore. Do you intend to fix this anytime soon? This would be much appreciated! :slight_smile:
Or even better: maybe @imick could integrate this “tweak” into the official ha-tahoma?!

ha-tahoma is in maintenance mode, thus won’t get any new features (from me). We are working on bringing local support to core and if by the time Connexxoon is still not supported, I will add the jailbreaked support as well.

5 Likes