This post covers how I shifted capabilities off my HA controller hardware so it could run HA OS. The solution works but doubled power required to maintain my alarm system during a power outage. I believe these capabilities are core capabilities that should be supported in HA OS. Hopefully documenting things will open the door for HA OS reconsideration. If not maybe they’ll just be helpful to someone, myself include should I have to rebuild things.
Update: I’ve currently have an alternative implantation that moves GPIO monitoring over to an esp and the routing over to an OpenWrt router.
Here’s what the part of my HA implementation related to this post now looks like:
You years I’ve been running the an HA supervised install. The driver for this was to have full access to the controller hardware and have the ability to implement more complex networking. The hardware that was of interest were the GPIO pins on the Odroid N2+ HA controller. I utilized the GPIO pins to interface with wired contact sensors that came with the house. Wired contact sensors are significantly better than the wireless alternatives. With control of the networking, I was able to connected to both my wired internet provider and utilize cellular to provide HA an backup route to the internet. The alternative route stays up should I lose power. Utilizing Nabu Casa, you can still reach your smart home alarm features remotely during a power outage. These features can’t be implemented on HA OS today.
I wanted to migrate over to HA OS because we plan to sell our house at some point. Most people recommend against including your HA smart house implementation in the sale. Not me, I plan on giving the buyer the option to take over all smart house features in as is condition. That said, I’ve been spending time trying to make things in the house as easy as possible to support. While I was fine with my supervised install, I believe it requires more knowledge than using HA OS. I believe HA OS will be easier for someone to takeover.
I still want to have the contact sensor and redundant internet connections while I live in the house. Once we sell the house, I want to make it easy for the new home owner to use or remove features, or completely disable the smart house if they desire. My HA controller is now running HA OS. My companion system is using a Rasberry pi 4 with Rasberry pi OS handling the GPIO integration and networking.
My new implementation carries over the capabilities described in these post:
- How to: GPIO integration of old Alarm system contact sensors
- How to: HA base security system with Cellular backup and 24/7 3rd party monitoring
From the requirements mentioned in that second post above, I no longer utilize a 24/7 3rd monitoring service. I’ve moved over to using frigate for camera processing. Frigate and other applications that are CPU intensive run on a larger docker/NAS server that is not on battery backup. This architecture allows the home alarm part of my smart house to ride through power failures and ISP outages.
Added Hardware
- Raspberry Pi 4
- Aluminion Case
- USB WiFi Adaptor
- USB Network Adaptor
Setting up the networking
I run the Rasberry in headless mode, using the serial console port to access the pi command line interface. The Pi’s Physical network connector is hardwired to the IOT network. The USB network adapter is connected from the pi to the HA controller network interface. I used rasp-config to make sure the the localisation values for locale, timezone and WLAN Country were set appropriately for my location. I use it to change the hostname and enable ssh and the serial port. I made sure all packages on the pi were up to date with:
sudo apt update
sudo apt upgrade
I don’t utilize ipv6 so I disabled it via creating the file /etc/sysctl.d/94-ipv6Disable.conf holding the line:
net.ipv6.conf.all.disable_ipv6 = 1
This can be loaded with “sudo sysctl -p /etc/sysctl.d/94-ipv6Disable.conf” or with a reboot. You can verify it’s being set with: sysctl -a |grep disable_ipv6.
Since I’m not using the integrated WiFi adaptors , I disabled it by adding the following line to the end of the /boot/firmware/config.txt file.
dtoverlay=disable-wifi
A reboot is required for this to take effect. It’s best to do this before you add the WiFi adapter and build the bridge described below. I utilize syslog on all my machines so I install it via:
sudo apt install syslog-ng.
I typically monitor the syslog in real time as I make changes. At this point as I was building the system, syslog was useful to verify when I plug in the wireless adapter that it is recognized as such.
A network bridge is created between the two Network adapters and the WiFi adapter. Implementing a bridge gives the pi, HA and the cell phone direct connect access to the IOT network. That is, they are all on the same unified network segment as the IOT network falling under the same network address block. If the pi were acting only as a routing device, between HA and the IOT network, then HA’s device discovery would fail.
The WiFi adapter acts as an access point for the Cellular phone WiFi connection. The cell phone is also connected to the pi via USB, tethering it’s internet access. The phone I use runs android. You can not tell android what IP CIDR block to assign to the tethered network. Surprisingly when I first utilized a phone for celluar access a few years back, I had a case where the cell phone picked the same network block as the IOT network. This is a bigh problem if it happens. By connecting the cell phone wifi to the IOT network, you prevent the phone from assigning the same address space as the IOT network. A secondary advantage of this is that the phone will send all network traffic over the WiFi network when it’s available. So under normal operation I can ensure all traffic from my network goes out my main firewall. When my ISP internet access is having problems I can shutdown the pi’s access point and the Cell phone will shift to using the Celluar network until wifi access returns.
Steps to build the bridge:
This link provides details on setting up the bridge, including the WiFi hotspot . The commands I used from this document follow.
Create the bridge
sudo nmcli connection add type bridge con-name 'Bridge' ifname bridge0
Connect the on board nic to the bridge
sudo nmcli connection add type ethernet slave-type bridge con-name 'Ethernet0' ifname eth0 master bridge0
Connect the USB NIC adapter the bridge
sudo nmcli connection add type ethernet slave-type bridge con-name 'Ethernet1' ifname eth1 master bridge0
Create and connect the wifi hotspot to the bridge
Change YOUR_SID and YOUR_PASS_PHRASE as desired
sudo nmcli connection add con-name 'Hotspot' ifname wlan0 type wifi slave-type bridge master bridge0 wifi.mode ap wifi.ssid YOUR_SID wifi-sec.key-mgmt wpa-psk wifi-sec.proto rsn wifi-sec.pairwise ccmp wifi-sec.psk YOUR_PASS_PHRASE
I restrict the wifi to 5Ghz because I use zigbee devices and I don’t want to add any more noise to the 2.4Ghz wireless band.
sudo nmcli connection modify Hotspot 802-11-wireless.band a
Bring the bridge up
sudo nmcli connection up Bridge
sudo nmcli connection up Hotspot
With the above configuration and the pi connected inline between HA and your IOT network, HA should work like it was directly connected to the IOT network.
Routing HA’s external traffic through the pi
The next step is to route any external traffic from HA through the pi. To do this the first thing we need to do is enabling routing on the pi. You create the file /etc/sysctl.d/95-ipForward.conf containing this line:
net.ipv4.ip_forward = 1
This can be loaded with “sudo sysctl -p /etc/sysctl.d/ 95-ipForward.conf” or with a reboot.
I recommend a static ipv4 address on the pi Bridge. You can do this with a modification of the following command, the IP information needs to be appropriate for connecting to your IOT network:
sudo nmcli con modify "Bridge" ipv4.method manual ipv4.addresses 192.168.11.150/24 ipv4.gateway 192.168.11.2 ipv4.dns 192.168.11.2
In theory you could restart the bridge with the following command, however sometimes this command doesn’t seem to complete:
sudo nohup nmcli con down "Bridge" && nmcli con up "Bridge"
I’d recommend you do a reboot again and remember you’ll be sshing into the new IP addressed you just assigned.
On the HA system you should set a static IP address under Settings→System→Network→IPV4. The gateway address would be set to the IP address of the PI. I also set the DNS server address to an external DNS server address so that DNS works when you’re on the cellular network. I use the google dns server 8.8.8.8. After making these changes your HA system may not fully function right as we need to add some iptables rules on the PI to handle the traffic forwarding.
On the pi you have multiple options for enabling firewalling capabilities. I still use iptables, so on the pi I installed the iptables tools via:
sudo apt install iptables-persistent
The persistent version gives the system the ability to reload your firewall rules on reboot.
Loading some iptabes rules
The minimal configuration required is to set up maquerade for the cellular network network interfaces usb0 via the following:
sudo iptables -t nat -A POSTROUTING -o usb0 -j MASQUERADE
The above is required because the cell phone is looking to tether a single device and not a full network.
*Add rules to limit flows that can enter the pi for routing
These rules restrict new flows coming in on the usb0 connector (cell phone tethered interface) to only DHCP messages:
sudo iptables -A INPUT -i usb0 -p udp -m udp --dport 68 -j ACCEPT
sudo iptables -A INPUT -i usb0 -p udp -m udp --dport 67 -j ACCEPT
sudo iptables -A INPUT -i usb0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
I want to make sure the only new flows coming into the pi via the bridge are ssh, icmp and HA controller packets:
sudo iptables -A INPUT -s IOT_NET_CIDR -i bridge0 -p tcp -m tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -s IP_OF_HA -i bridge0 -j ACCEPT
sudo iptables -A INPUT -p icmp -j ACCEPT
sudo iptables -A INPUT -i bridge0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
Above IOT_NET_CIRD and IP_OF_HA need to be match your network.
Setting the INPUT policy to DROP, ensures no other devices can utilize the pi as a routing device:
sudo iptables -P INPUT DROP
You’ll want to save the rules so they are applied at reboot
sudo iptables-save > /etc/iptables/rules.v4
Set metric values for routes
Verify what your current connection are via:
nmcli device
From that output, find the connection name for usb0. It’s normally ‘Wired connection 1’, but it could be something else. Set a metric value via:
sudo nmcli connection modify 'Wired connection 1' ipv4.route-metric 150
Set the bridge metric to a lower value so it’s used as the normal default route
sudo nmcli connection modify 'Bridge' ipv4.route-metric 100
For these to take effect the connections must be restarted:
sudo nmcli connection up 'Wired connection 1'
sudo nmcli connection up 'Bridge'
The usb0 connection will come back almost instantly, however the bridge is slow to go down and come back up.
You can now verify the routes have these newly assigned metrics via:
route -n
I have other networks available off my IOT firewall. One is a VPN that gives me external access to my HA system. As a result I have to add additional routes to make sure traffic can make it between the HA controller and other lan segments. I do this by simply editing the file /etc/NetworkManager/system-connections/Bridge.nmconnection and adding in the [ipv4] section a record for each route such as:
route1=192.168.10.0/24,192.168.11.2
route2=10.1.2.0/24,192.168.11.2
Above the CIRD blocks are the other networks that need access to HA and the individual IP address is the address of your router/firewall. Access to HA from these LAN segments will not work until you add specific routes.
Adding a script to control the internet routing.
The following script tracks the state of the internet and adjust the internet path to ensure HA communications can make it out of the house. The script is placed in the file /usr/local/bin/route-ctrl.py. IP_OF_HA, PORT_NUMBER_FOR_HA_GUI, MQTT_USER and MQTT_PASSSWORD need to be set correctly. This script assumes your running a MQTT server on HA.
#! /bin/python3
# Program looks to see if it can communicate with the internet, if it can't it takes
# wifi access point down, if it can access interent and wifi was previously taken
# down it brings it back up
import os
import subprocess
import time
import syslog
import socket
import sys
from datetime import datetime
HOST, PORT = "IP_OF_HA", PORT_NUMBER_FOR_HA_GUI
PASS="MQTT_PASSWORD"
USER="MQTT_USER"
connected = False
def ha_up():
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.close()
return True
except socket.error:
return False
def send_mqtt_msg( msg ):
# only continue if mqtt broker is up
done = 1
while done != 0:
#print("Sending message",msg)
done = os.system( msg )
if done != 0:
time.sleep(5)
def move_to_cell_internet():
# attempt to shutdown Hotspot so with metric change external traffic goes out celluar
# really should verify these next two command work before declaring success
response = os.system("nmcli connection modify 'Wired connection 1' ipv4.route-metric 50")
response = os.system("nmcli connection up 'Wired connection 1'")
response = os.system("nmcli connection down 'Hotspot'")
if response == 0:
os.system("conntrack --flush")
return response
def move_to_isp_internet():
# attempt to bring HotSpot back up
response = os.system("nmcli connection up 'Hotspot'")
if response == 0:
#Hot spot is up so go ahead and change metric so traffic no longer goes out the cell phone tethered connection
# really should verify these next two command work before declaring success
os.system("nmcli connection modify 'Wired connection 1' ipv4.route-metric 150")
os.system("nmcli connection up 'Wired connection 1'")
# wait a little bit and then flush existing iptables flows so things will shift back to IPS connection on eth0
time.sleep(.25)
os.system("conntrack --flush")
return response
# wait for HA to come up before continuing
while not ha_up():
syslog.syslog(syslog.LOG_INFO,"Waiting on HA to come up")
time.sleep(10)
syslog.syslog(syslog.LOG_INFO,"HA is up and running")
internet_test_addr = "8.8.8.8"
full_ping_delay = 5
ping_delay = full_ping_delay
refreshTime = 0
# set so we start WiFi if the internet is up
house_internet_up = True
going_down = False
#Delay a couple seconds for things to come up
time.sleep(2)
syslog.syslog(syslog.LOG_INFO, 'Network Watcher up using target ' + internet_test_addr )
# Let HA know we currently have Hotspot up so using IPS internet
send_mqtt_msg("mosquitto_pub -u USER -P "+ PASS + " -h "+ HOST +" -t home-assistant/wifi -m "+ '{\\"state\\":\\"ON\\"}')
# just loop forever
while True:
# check to see if we can ping out ISP internet to test address
response = os.system("ping -I bridge0 -c 1 " + internet_test_addr + "> /dev/null")
# good ping so internet is up
#print( "Ping Response: ", response )
if response == 0:
#print( 'Good response to ping' )
# if internet was down then bring wifi back up
if not house_internet_up:
#print( 'Good ping ' + internet_test_addr + ' Bringing Hotspot Up')
syslog.syslog(syslog.LOG_INFO, 'Good ping ' + internet_test_addr + ' so enable Hotspot so if traffic flows to cell phone it will still go out ISP internet')
response = move_to_isp_internet()
if response == 0:
# successful traffic shift
house_internet_up = True
# Up date HA sensor indicating we're now on ISP internet access
send_mqtt_msg("mosquitto_pub -u USER -P "+ PASS + " -h "+ HOST +" -t home-assistant/wifi -m "+ '{\\"state\\":\\"ON\\"}')
refreshTime = 0
syslog.syslog(syslog.LOG_INFO,"Hostsport successfully activated");
going_down = False
ping_delay = full_ping_delay
else:
# failed traffic shift, so we'll try again next time
syslog.syslog(syslog.LOG_INFO,"Hotsport did not activated");
# second ping fails so if internet up then take it down
elif going_down:
if house_internet_up:
#print('Cant ping ' + internet_test_addr + ' Shutting down HA WiFi')
syslog.syslog(syslog.LOG_INFO, 'Cant ping ' + internet_test_addr + ' Shutting down Hotsport so traffic flows out cellular network')
#os.system("rfkill block wlan")
response = move_to_cell_internet()
if response == 0:
syslog.syslog(syslog.LOG_INFO,"Hotsport turned off show phone will send traffic out celluar network");
# Up date HA sensor indicating we're now on cellular backup internet access
send_mqtt_msg("mosquitto_pub -u USER -P "+ PASS + " -h "+ HOST +" -t home-assistant/wifi -m "+ '{\\"state\\":\\"OFF\\"}')
refreshTime = 0
house_internet_up = False
ping_delay = full_ping_delay
else:
syslog.syslog(syslog.LOG_INFO,"Hotsport did not turn off so will try again next time");
# failed first ping of internet test address so set to try second ping
else:
syslog.syslog(syslog.LOG_INFO, "First ping failed, look for second error before routing external traffic out celluar network")
#print "First ping failed, look for second error before taking internet down "
going_down = True
ping_delay = 5
refreshTime = refreshTime + ping_delay
# refress HA sensor state about ever 60 seconds
if refreshTime >= 60:
refreshTime = 0
if house_internet_up:
send_mqtt_msg("mosquitto_pub -u USER -P "+ PASS + " -h "+ HOST +" -t home-assistant/wifi -m "+ '{\\"state\\":\\"ON\\"}')
else:
send_mqtt_msg("mosquitto_pub -u USER -P "+ PASS + " -h "+ HOST +" -t home-assistant/wifi -m "+ '{\\"state\\":\\"OFF\\"}')
time.sleep(ping_delay)
brian@HAbridge:~ $
The following goes in /etc/systemd/system/watchpins_mqtt.service to start the above script on boot.
[Unit]
Description=Watch GPIO pins and report to HA via MQTT
After=network.target
[Service]
Type=simple
#User=
#Group=
#ExecStart=/usr/local/bin/dufs -p 443 -a mybackup:GetYourData@/:rw --allow-delete --allow-search --allow-archive --log-file /var/log/dufs.log --tls-key=/etc/ssl/private/dufs-selfsigned.key --tls-cert=/etc/ssl/certs/dufs-selfsigned.crt /home/mybackup
ExecStart=/usr/local/bin/watchpins_mqtt.bash
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Once this file is in place you need to enable it so it starts on reboot:
systemctl enable watchpins_mqtt
defining the sensor for HA
On HA I have binary_sensors that report the state of the doors. There is probably a different way to do this now, but when I first start using this years ago I had to added the following information in the homeassistant/configuration.yaml to create the mqtt binary_sensor:
mqtt:
binary_sensor:
- name: "wifi_status"
device_class: connectivity
state_topic: "home-assistant/wifi"
qos: 0
value_template: "{{ value_json.state }}"
unique_id: wifi_status_bsensor
I hit the character limit for a post so the GPIO information in next post.
