How to gain Root to Futurehome Hub & Activate Z-Wave JS UI with Futurehome App as a Fallback

Hi,
Thought I would share this little hack I made.
(apologies for the messy formating, this was copied from my project notes file)

Here is a guide I have written on how to gain access to the Linux system on your Futurehome hub (tested on v1). It includes scripts that will allow you to access the onboard Z-Wave chip, enabling you to include all devices into a Z-Wave JS UI instance without the need to exclude and re-include everything.

As a bonus, there is also a service that monitors the Z-Wave JS server. If it goes down, the hub will automatically return control of the Z-Wave chip to Futurehome until the server comes back online.

Additionally, a web endpoint is created, providing manual control and status of this modification.

If this is useful to anyone, here is the code. Be sure to review it carefully and ask if you have any uncertainties.

The process consists of two parts:

  1. Rooting the device
  2. Logging into the newly created SSH shell and copying/pasting the second part of the script, which will configure everything for you automatically.

ROOT ACCESS

## ROOT FUTUREHOME Hub (v1)
# this will provide SSH access
# be sure to run line by line and find the right /dev/*mount* and insert sshkey

# Define your public SSH key (ssh-keygen)
$SSH_KEY = ""

# Connect to rpi CM1 module: open device and connect micro usb to linux machine and then power
sudo apt update
sudo apt install usbutils git libusb-1.0-0-dev
git clone --depth=1 https://github.com/raspberrypi/usbboot
cd usbboot
make
sudo ./rpiboot

# Mount filesystem, find mount using 'lsblk'
sudo mount /dev/sda2 /mnt/rpi

# Enable SSH
sudo ln -s /lib/systemd/system/ssh.service /mnt/rpi/etc/systemd/system/multi-user.target.wants/ssh.service
sudo sed -i '/^[[:space:]]*exit 0/i iptables -I INPUT -p tcp --dport 22 -j ACCEPT' /mnt/rpi/etc/rc.local

# Insert SSH key
sudo mkdir -p /mnt/rpi/root/.ssh
echo $SSH_KEY | sudo tee -a /mnt/rpi/root/.ssh/authorized_keys
sudo chmod 700 /mnt/rpi/root/.ssh
sudo chmod 600 /mnt/rpi/root/.ssh/authorized_keys
sudo chown -R 0:0 /mnt/rpi/root/.ssh

sudo umount /mnt/rpi
  • disconnect USB and reboot
  • CONNECT to cube.local:22 using SSH and certificate defined erlier, login as ‘root’ (no password needed)

CONFIGURE / INSTALL SERVICES

#### PASTE >>>


# CONFIG
mkdir /etc/ser2net-server
tee /etc/ser2net-server/config.sh <<EOF
AUTOSTART=yes
FALLBACK=yes
PORT=8091
HOST=
EOF


# INSTALL ser2net
wget -O /tmp/ser2net_2.9.1-1_armhf.deb \
  http://legacy.raspbian.org/raspbian/pool/main/s/ser2net/ser2net_2.9.1-1_armhf.deb
dpkg -i /tmp/ser2net_2.9.1-1_armhf.deb
echo "3333:raw:0:/dev/SER2NET:115200 8DATABITS NONE 1STOPBIT" >> /etc/ser2net.conf
service ser2net restart
sed -i '/^[[:space:]]*exit 0/i iptables -I INPUT -p tcp --dport 3333 -j ACCEPT' /etc/rc.local


## INSTALL ser2net-fallback script
cat <<'EOF' >/usr/local/bin/ser2net-fallback
#!/bin/bash

while true; do
  source /etc/ser2net-server/config.sh
  
  if [ -z "$HOST" ] || [ -z "$PORT" ]; then
    sleep 60
    continue
  fi
  
  if [ "$FALLBACK" != "yes" ]; then
    break
  fi
  
  HOST_UP=$(curl -s --head "http://$HOST:$PORT" > /dev/null && echo "true" || echo "false")
  SER2NET=$(ls -l /dev/SER2NET &>/dev/null && echo 'true' || echo 'false')
  FUTUREHOME=$(ls -l /dev/futurehome/Z-Wave &>/dev/null && echo 'true' || echo 'false')

  if [ "$HOST_UP" = "true" ] && [ "$SER2NET" = "false" ]; then
    /usr/local/bin/ser2net-control start
  
  elif [ "$HOST_UP" = "false" ] && [ "$FUTUREHOME" = "false" ]; then
    /usr/local/bin/ser2net-control pause
  fi
  
  sleep 60
done
EOF
chmod +x /usr/local/bin/ser2net-fallback


## INSTALL ser2net-control script
cat <<'EOF' >/usr/local/bin/ser2net-control
#!/bin/bash
source /etc/ser2net-server/config.sh

if [ "$FALLBACK" = "yes" ] && [ "$1" = "start" ]; then
  if ! pgrep -f "ser2net-fallback" >/dev/null; then
    /usr/local/bin/ser2net-fallback &
  fi
fi

case "$1" in
  boot)
    if [ "$AUTOSTART" = "yes" ]; then
      /usr/local/bin/ser2net-control start
    else
      /usr/local/bin/ser2net-control stop
    fi
    ;;
  
  status)
    echo "FUTUREHOME GATEWAY"
    echo "==== STATUS ===="
    
    if [ -n "$HOST" ] && [ -n "$PORT" ]; then
      if pgrep -f "ser2net-fallback" >/dev/null; then
        echo "FALLBACK: running"
        #STATUS_SER="paused"
        #STATUS_FIMP="active (fallback)"
      else
        echo "FALLBACK: stopped"
      fi
      
      curl -s --head "http://$HOST:$PORT" >/dev/null && \
        echo "FALLBACK TEST: success" || echo "FALLBACK TEST: host unreachable"
    else
      echo "FALLBACK: missing HOST/PORT"
    fi
    #TODO: paused, fallback ...
    echo "SER2NET: $(ls -l /dev/SER2NET &>/dev/null && echo 'active' || echo 'deactivated')"
    echo "FUTUREHOME: $(ls -l /dev/futurehome/Z-Wave &>/dev/null && echo 'active' || echo 'deactivated')"

 
    echo "==== CONFIG ===="
    cat /etc/ser2net-server/config.sh
    ;;
    
  start)
    rm -f /dev/futurehome/Z-Wave &>/dev/null || true
    fuser -k /dev/ttyS0 &>/dev/null || true

    ln -s /dev/ttyS0 /dev/SER2NET &>/dev/null || true
    systemctl restart ser2net &>/dev/null || true
    echo -e "Active: SER2NET\n"
    ;;
    
  stop)
    killall "ser2net-fallback" &>/dev/null || true
    /usr/local/bin/ser2net-control pause
    ;;
    
  pause)
    rm -f /dev/SER2NET &>/dev/null || true
    ln -s /dev/ttyS0 /dev/futurehome/Z-Wave &>/dev/null || true
    killall zipgateway &>/dev/null || true
    echo -e "Active: Futurehome\n"
    ;;
    
  *)
    echo "Usage: $0 {boot|start|stop|status}"
    exit 1
    ;;
esac
EOF
chmod +x /usr/local/bin/ser2net-control


## INSTALL Web control HTTP server
cat <<'EOF' >/usr/local/bin/ser2net-web.py
#!/usr/bin/env python3
import http.server
import socketserver
import subprocess
import shlex
from urllib.parse import urlparse, parse_qs

PORT = 8888

subprocess.check_call(["/usr/local/bin/ser2net-control", "boot"])

class MyHandler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        url_data = urlparse(self.path.lower())
        command = url_data.path.lstrip('/')
        
        # Config updates
        fields=parse_qs(url_data.query)
        if fields:
          config_file = "/etc/ser2net-server/config.sh"
          with open(config_file, "r") as file:
            lines = file.readlines()
          with open(config_file, "w") as file:
            for line in lines:
              key, sep, _ = line.partition("=")
              if key.strip().lower() in ['host', 'port', 'fallback', 'autostart']:
                  value = fields.get(key.lower(), [None])[0]
              if value is not None:
                  value = 'yes' if value in ['yes', 'true', '1'] else ('no' if value in ['no', 'false', '0'] else value)
                  file.write("{key}={value}\n".format(key=key, value=shlex.quote(value)))
                  continue
              file.write(line)

        # Command execution
        if command in ['start', 'stop', 'status']:
            response = subprocess.check_output(["/usr/local/bin/ser2net-control", command], universal_newlines=True)
            
            self.send_response(200)
            self.end_headers()
            self.wfile.write(response.encode('utf-8'))
        else:
            self.send_error(404, "Not Found")

def main():
    httpd = socketserver.TCPServer(("", PORT), MyHandler)
    print("Serving HTTP on port {}".format(PORT))
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        pass
    finally:
        httpd.server_close()

if __name__ == "__main__":
    main()
EOF
chmod +x /usr/local/bin/ser2net-web.py
sed -i '/^[[:space:]]*exit 0/i iptables -I INPUT -p tcp --dport 8888 -j ACCEPT' /etc/rc.local


## CONFIGURE Web control service (autostart)
cat <<'EOF' >/lib/systemd/system/ser2net_web.service
[Unit]
Description=HTTP server to control ser2net/zwave-js / futurehome functions
After=network.target

[Service]
ExecStart=/usr/bin/python3 /usr/local/bin/ser2net-web.py
Restart=always

[Install]
WantedBy=multi-user.target
EOF

ln -sf /lib/systemd/system/ser2net_web.service /etc/systemd/system/multi-user.target.wants/ser2net_web.service
systemctl daemon-reload
systemctl enable ser2net_web.service
systemctl start ser2net_web.service


## EXTRA
# Expose Z/IP ports (for silicon Labs access)
sed -i '/^[[:space:]]*exit 0/i ip6tables -t nat -A PREROUTING -p udp --dport 42242 -j DNAT --to-destination [fd00:aaaa::03]:4123' /etc/rc.local
sed -i '/^[[:space:]]*exit 0/i ip6tables -I INPUT -p udp --dport 42242 -j ACCEPT' /etc/rc.local

#### << PASTE

CONFIGURE Z-Wave JS UI → ser2net

OPTION 1 - using tcp

  • Set ‘serial port’ to
    tcp://cube.local:3333

OPTION 2 - using virtual device

  • Create virtual device on docker host
    sudo apk add --no-cache socat
    sudo socat -d -d pty,link=/dev/ttyFIMP,raw,echo=0,user=$(whoami),group=dialout,mode=660 TCP:cube.local:3333

  • Add expose device in docker compose file
    devices:

    • /dev/ttyFIMP:/dev/ttyzwave
  • In Z-Wave JS UI set ‘serial port’ to
    /dev/ttyzwave

DONE

You should now have your Zwave adpater and devices located in your Futurehome Hub visible in Z-Wave JS UI

REMOTECONTROL Futurehome ser2net behaviour

parameters passed to url are persistent.

1 Like

here you might find the S0 key
xxd -p /var/spool/futurehome/migration/zwave-s0.key | tr -d '\n'

As the new guy in home assistant, it would be help if you can make it detail instruction?

As you wrote: 1/ we need to connect the futurehome hub to home assistant with usb. then how we can root access? use SSH to type above command?

Futurehome charges the monthly abonnement now. This is useful if we can use the hub as Z-wave control and control from HA. Thanks

Hi!
Thanks a lot for sharing this – really appreciate you putting it out there.

I’ve written a guide based on your post, aimed at users with some Linux experience. It goes through the process in a bit more structured way and includes a few practical notes from my own setup. I’ve credited you in the guide, of course.

Hope it’s okay that I’ve built on what you shared – let me know if anything should be adjusted.

Here’s the guide: GitHub - alekvie/SER2NET-guide-FH

Thanks again for the original work – it was a big help!

Hi!
Wonderfull work there. Eager to get going, but i got a v2.
Has anyone done this for the Futurehome Smarthub v2? As there are no micro-usb contact on the board, but there is a custom pinout on the chassis?

1 Like

I Hope they find a solution !!

Dette er krise ass.

Hmm, for me the Futurehome HUB has been very good. Using ZwaveJS will make it possible to use the Hub alongside with HA to add z-wave devices (instead of buying a dongle). It would be really nice if we could also add Zigbee devices to the FH Hub through HA.

The reason I maybe want it like that, is the day when the house is for sale, it would be a lot easier to hand over the Futurehome system to new owners, instead of Home Assistant (sorry :stuck_out_tongue_closed_eyes:)

Why Futurehome don’t see the opportunity to open up the Hub for advanced users for a small fee, where they keep the customers in their ecosystem is out of my understanding.

There is hope. Offering 5000 usd to whoever can hack it: https://youtu.be/KNuZ3BjT7IU?si=MCWBAP9Gx15u-vE3

1 Like

sorry for the late reply, when installing the hack use another computer (not HA)

If v2 also use a RPI CM (compute module) then it should be possible yes, I can not say as I do not own one but post a picture of the circuit board.

That’s great! Feel free to use as you like I am just happy my tinkering could be of use to others.
Regarding the Extraction of the Keys using Silicon Labs, this step is no needed as Zwave JS UI does not require that to connect to the devices (the zwave chip on the hub has these stores and that is used in the communication). however you can use the silicon labs tools to remotely migrate the devices to a new controller (making the process a lot quicker then exclude/include each and every device physically/manually)