Connect to homeassistant remotely over IPv6

·

3 min read

Most ISP today support IPv6 address.
If you connect the home assistant to the router and the router is configured correctly the homeassitant should acquire IPv6 address.

configuring the router:
Basically for configuring the router there are two options: DHCPv6 configuration or relay configuration.
The preferred option is the DHCPv6. configuring it is different from one router to another so I can't add instructions to it here. But the general guidelines should be to enable the DHCPv6 in your router (stateless or statefull) and set a rule in the firewall to allow through IPv6 packets over port 8123.
If this doesn't work, some routers allow you to set the DHCPv6 to a relay mode. This should forward the DHCPv6 requests from the homeassitant to the ISP's DNS which, hopefully, supports this.

configuring the homeassistant
The home assistant IPv6 configuration is in:
setting->system->networks->"configure network interface".
this is best set to automatic (meaning acquire address dynamically).
The IPV6 address of the homeassitant can be seen over ssh using the command ifconfig.
Do note that this will display several IPv6 addresses, you are looking for the public address, public addresses should starts with the number ‘2’ or ‘3’ (addresses starting with 'f' are local addresses).
To use IPv6 address in a browser use '[' and ']'.
For example: http://[2001:0123:4567:89ab:0123:4567:89ab:cdef]:8123
To use the ipv6 url in the companion app go to:
settings->"companion app"->"servers & Devices" → “add a server”
The companion app server field is using the same the url format as the browser.

Theoretically, an IPv6 address should remain constant, despite of this, ISPs sometimes change the prefix of the address allocated to you. This is similar to changing the IPv4 address. To solve this you can use a dynamic DNS service. The most popular dynamic DNS service with homeassistant is duckDNS, however, it's homeassistant addon is lacking the update for IPv6. To go around this, instead of using the official add on you can use the script attached at the button of this post.
**
Setting duckDNS.py**
The script requires first to setup an account with duckDNS.
To run the python script you can use either appdaemon addon or shell command.
To run the script automatically set an automation to trigger the python script every 1 or two minutes.
Most browsers default configuration is to prefer IPv6 over IPv4, so if your the dynamic DNS has both addresses, the IPv6 address will be the one your browser use.
Don’t forget to modify the DUCKDNS_DOMAIN and DUCKDNS_TOKEN parameters in the script

update duckDNS.py

import subprocess
import requests

DUCKDNS_DOMAIN = "your subdomain" # the subdomain you choos when creating the account
DUCKDNS_TOKEN = "your token" # found in your duckDNS account

def get_ipv6_address():
    try:
        # We'll use 'ifconfig' for getting the ipv6 address
        result = subprocess.run(['ifconfig'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)

        # Search for the IPv6 public address (ipv6 line should start with 'inet6')
        for line in result.stdout.splitlines():
            if 'inet6' in line:
                address = line.split()[1]
                if address.startswith("2") or address.startswith("3"): #public addresses start with'2' or '3'
                    return address

        print("No IPv6 address found.")
        return None
    except Exception as e:
        print(f"Error while retrieving IPv6 address: {e}")
        return None

def update_duckdns_ipv6(ipv6_address):
    if ipv6_address:
        url = f"https://www.duckdns.org/update?domains={DUCKDNS_DOMAIN}&token={DUCKDNS_TOKEN}&ipv6={ipv6_address}"
        try:
            response = requests.get(url)
            if response.status_code == 200:
                print(f"Successfully updated DuckDNS record for {DUCKDNS_DOMAIN} with IPv6 address {ipv6_address}")
            else:
                print(f"Failed to update DuckDNS record. HTTP Status: {response.status_code}")
        except Exception as e:
            print(f"Error while updating DuckDNS: {e}")
    else:
        print("No IPv6 address provided to update.")

def main():
    ipv6_address = get_ipv6_address()
    if ipv6_address:
        update_duckdns_ipv6(ipv6_address)
    else:
        print("Couldn't retrieve IPv6 address. Skipping update.")

if __name__ == "__main__":
    main()