1
1
package main
2
2
3
3
import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "io"
4
7
"log"
5
8
"net"
9
+ "net/http"
10
+ "os"
11
+ "time"
6
12
7
13
"github.com/grandcat/zeroconf"
8
14
"github.com/rs/xid"
9
15
)
10
16
17
+ type Response struct {
18
+ Result string `json:"result"`
19
+ Message string `json:"message,omitempty"`
20
+ Data map [string ]any `json:"data,omitempty"`
21
+ }
22
+
11
23
func publishHomeAssistant () {
12
24
var err error
13
25
26
+ var outboundIP net.IP
27
+
28
+ for {
29
+ outboundIP , err = getOutboundIP ()
30
+ if outboundIP != nil {
31
+ break
32
+ }
33
+ log .Printf ("Failed to get outbound IP, retrying in 5s: %s" , err )
34
+ time .Sleep (5 * time .Second )
35
+ }
36
+
14
37
unique := xid .New ()
15
- hostURL := "http://" + getOutboundIP () .String () + ":8123"
38
+ hostURL := "http://" + outboundIP .String () + ":8123"
16
39
params := []string {
17
40
"location_name=Home Assistant" ,
18
41
"uuid=" ,
@@ -31,21 +54,76 @@ func publishHomeAssistant() {
31
54
}
32
55
}
33
56
34
- // Get preferred outbound ip of this machine
35
- // https://stackoverflow.com/a/37382208
36
- // Can be removed after Supervisor v248 with new lookup
37
- func getOutboundIP () net.IP {
38
- conn , err := net .Dial ("udp" , "8.8.8.8:80" )
57
+ // Get the first IPv4 address from the interface information
58
+ func getFirstIPv4Address (iface map [string ]any ) (net.IP , error ) {
59
+ ipv4Data , ok := iface ["ipv4" ].(map [string ]any )
60
+ if ! ok {
61
+ return nil , fmt .Errorf ("no ipv4 addresses found" )
62
+ }
63
+
64
+ addresses , ok := ipv4Data ["address" ].([]any )
65
+ if ! ok || len (addresses ) == 0 {
66
+ return nil , fmt .Errorf ("empty ipv4 address list" )
67
+ }
68
+
69
+ addr , ok := addresses [0 ].(string )
70
+ if ! ok {
71
+ return nil , fmt .Errorf ("can't parse first ipv4 address" )
72
+ }
73
+
74
+ ip , _ , err := net .ParseCIDR (addr )
75
+
39
76
if err != nil {
40
- log . Fatal ( err )
77
+ return nil , fmt . Errorf ( "invalid IPv4 address: %s" , err )
41
78
}
42
- defer func () {
43
- if err := conn .Close (); err != nil {
44
- log .Printf ("error closing mdns connection: %v" , err )
79
+
80
+ return ip , nil
81
+ }
82
+
83
+ // Get IP address of the default interface from Supervisor
84
+ func getOutboundIP () (net.IP , error ) {
85
+ supervisorHost := getSupervisorHost ()
86
+
87
+ client := & http.Client {}
88
+ req , err := http .NewRequest ("GET" , "http://" + supervisorHost + "/network/interface/default/info" , nil )
89
+
90
+ if err != nil {
91
+ return nil , fmt .Errorf ("can't create request to Supervisor: %s" , err )
92
+ }
93
+
94
+ req .Header .Add ("Authorization" , "Bearer " + os .Getenv ("SUPERVISOR_TOKEN" ))
95
+
96
+ response , err := client .Do (req )
97
+ if err == nil {
98
+ defer response .Body .Close ()
99
+ }
100
+
101
+ if err != nil || response .StatusCode >= 300 {
102
+ var errorMsg string
103
+
104
+ if err == nil {
105
+ if data , err := io .ReadAll (response .Body ); err == nil {
106
+ errorMsg = string (data )
107
+ } else {
108
+ errorMsg = err .Error ()
109
+ }
110
+ } else {
111
+ errorMsg = err .Error ()
45
112
}
46
- }()
47
113
48
- localAddr := conn .LocalAddr ().(* net.UDPAddr )
114
+ return nil , fmt .Errorf ("can't get default interface from Supervisor: %s" , errorMsg )
115
+ }
116
+
117
+ var responseData Response
118
+
119
+ if err := json .NewDecoder (response .Body ).Decode (& responseData ); err != nil {
120
+ return nil , fmt .Errorf ("can't parse default interface data: %s" , err )
121
+ }
122
+
123
+ ipv4Addr , err := getFirstIPv4Address (responseData .Data )
124
+ if err != nil {
125
+ return nil , err
126
+ }
49
127
50
- return localAddr . IP
128
+ return ipv4Addr , nil
51
129
}
0 commit comments