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:
- Rooting the device
- 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.
-
switch to Z-Wave JS UI
http://cube.local:8888/start -
switch to futurehome app:
http://cube.local:8888/stop
http://cube.local:8888/stop?autostart=false -
configure auto fallback to futurehome if Z-Wave JS is down
http://cube.local:8888/start?host=192.168.1.100&port=8091&fallback=true -
status info
http://cube.local:8888/status -
enable/disable autostart
http://cube.local:8888/?autostart=true
http://cube.local:8888/?autostart=false