diff options
author | Mike Crute <mike@crute.us> | 2018-06-06 04:01:25 +0000 |
---|---|---|
committer | Mike Crute <mike@crute.us> | 2018-06-06 04:01:25 +0000 |
commit | 451f69f66be6e0a9d9c9eba96974d8452ca5d134 (patch) | |
tree | fc344df8d1b2c69a41fdb83743824d4ee4df7108 | |
parent | 365bc847b69551a0107950bed2f881ff9c779677 (diff) | |
download | mfi_homekit-451f69f66be6e0a9d9c9eba96974d8452ca5d134.tar.bz2 mfi_homekit-451f69f66be6e0a9d9c9eba96974d8452ca5d134.tar.xz mfi_homekit-451f69f66be6e0a9d9c9eba96974d8452ca5d134.zip |
Enable switch toggling
-rw-r--r-- | main.go | 109 |
1 files changed, 76 insertions, 33 deletions
@@ -10,18 +10,20 @@ import ( | |||
10 | "golang.org/x/crypto/ssh" | 10 | "golang.org/x/crypto/ssh" |
11 | "io/ioutil" | 11 | "io/ioutil" |
12 | "net" | 12 | "net" |
13 | "sort" | 13 | "time" |
14 | ) | 14 | ) |
15 | 15 | ||
16 | const CMD_PATH = "/var/etc/persistent/power_control.sh" | 16 | const CMD_PATH = "/var/etc/persistent/power_control.sh" |
17 | 17 | ||
18 | type Device struct { | 18 | type Device struct { |
19 | Root *Device | 19 | Root *Device |
20 | Username string | 20 | Name string |
21 | Password string | 21 | Username string |
22 | Port int | 22 | Password string |
23 | Host string | 23 | Port int |
24 | Devices []string | 24 | Host string |
25 | Devices []string | ||
26 | sshClient *ssh.Client | ||
25 | } | 27 | } |
26 | 28 | ||
27 | func (d *Device) GetUsername() string { | 29 | func (d *Device) GetUsername() string { |
@@ -54,11 +56,12 @@ func (d *Device) GetPort() int { | |||
54 | } | 56 | } |
55 | } | 57 | } |
56 | 58 | ||
57 | func (d *Device) newSession() (*ssh.Client, *ssh.Session, error) { | 59 | func (d *Device) Connect() error { |
58 | cfg := &ssh.ClientConfig{ | 60 | cfg := &ssh.ClientConfig{ |
59 | User: d.GetUsername(), | 61 | User: d.GetUsername(), |
60 | Auth: []ssh.AuthMethod{ssh.Password(d.GetPassword())}, | 62 | Auth: []ssh.AuthMethod{ssh.Password(d.GetPassword())}, |
61 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), | 63 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), |
64 | Timeout: 0, | ||
62 | } | 65 | } |
63 | 66 | ||
64 | // These devices use really old crufty versions of SSH | 67 | // These devices use really old crufty versions of SSH |
@@ -67,32 +70,49 @@ func (d *Device) newSession() (*ssh.Client, *ssh.Session, error) { | |||
67 | 70 | ||
68 | conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", d.Host, d.GetPort()), cfg) | 71 | conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", d.Host, d.GetPort()), cfg) |
69 | if err != nil { | 72 | if err != nil { |
70 | return nil, nil, err | 73 | return err |
71 | } | 74 | } |
72 | 75 | ||
73 | s, err := conn.NewSession() | 76 | d.sshClient = conn |
74 | if err != nil { | 77 | return nil |
75 | conn.Close() | 78 | } |
76 | return nil, nil, err | 79 | |
80 | func (d *Device) Disconnect() { | ||
81 | d.sshClient.Close() | ||
82 | } | ||
83 | |||
84 | func (d *Device) newSession() (*ssh.Session, error) { | ||
85 | for i := 3; i > 0; i-- { | ||
86 | if s, err := d.sshClient.NewSession(); err == nil { | ||
87 | return s, nil | ||
88 | } else if err != nil && i == 0 { | ||
89 | fmt.Printf("Session error, failing: %s\n", err) | ||
90 | return nil, err | ||
91 | } | ||
92 | |||
93 | fmt.Println("Session error, reconnecting") | ||
94 | d.Connect() | ||
77 | } | 95 | } |
78 | 96 | ||
79 | return conn, s, nil | 97 | return nil, errors.New("Unable to connect via SSH") |
80 | } | 98 | } |
81 | 99 | ||
82 | func (d *Device) runCommand(cmd string) ([]DeviceOutput, error) { | 100 | func (d *Device) runCommand(cmd string) ([]DeviceOutput, error) { |
83 | conn, sess, err := d.newSession() | 101 | sess, err := d.newSession() |
84 | if err != nil { | 102 | if err != nil { |
103 | fmt.Printf("Session error: %s\n", err) | ||
85 | return nil, err | 104 | return nil, err |
86 | } | 105 | } |
87 | defer conn.Close() | ||
88 | defer sess.Close() | 106 | defer sess.Close() |
89 | 107 | ||
90 | out, err := sess.Output(cmd) | 108 | out, err := sess.Output(cmd) |
91 | if err != nil { | 109 | if err != nil { |
110 | fmt.Printf("Command error: %s\n", err) | ||
92 | return nil, err | 111 | return nil, err |
93 | } | 112 | } |
94 | 113 | ||
95 | if out == nil { | 114 | if out == nil { |
115 | fmt.Printf("Nil return\n") | ||
96 | return nil, nil | 116 | return nil, nil |
97 | } else { | 117 | } else { |
98 | var r []DeviceOutput | 118 | var r []DeviceOutput |
@@ -169,20 +189,20 @@ type DeviceOutput struct { | |||
169 | PowerFactor float64 `json:"power_factor"` | 189 | PowerFactor float64 `json:"power_factor"` |
170 | } | 190 | } |
171 | 191 | ||
172 | func LoadAppConfig(filename string) (map[string]*Device, error) { | 192 | func LoadAppConfig(filename string) ([]*Device, error) { |
173 | raw, err := ioutil.ReadFile(filename) | 193 | raw, err := ioutil.ReadFile(filename) |
174 | if err != nil { | 194 | if err != nil { |
175 | return nil, err | 195 | return nil, err |
176 | } | 196 | } |
177 | 197 | ||
178 | var cfg map[string]*Device | 198 | var cfg []*Device |
179 | err = json.Unmarshal(raw, &cfg) | 199 | err = json.Unmarshal(raw, &cfg) |
180 | if err != nil { | 200 | if err != nil { |
181 | return nil, err | 201 | return nil, err |
182 | } | 202 | } |
183 | 203 | ||
184 | if def, ok := cfg["DEFAULT"]; ok { | 204 | if def := cfg[0]; def.Name == "DEFAULT" { |
185 | delete(cfg, "DEFAULT") | 205 | cfg = cfg[1:] |
186 | 206 | ||
187 | for _, v := range cfg { | 207 | for _, v := range cfg { |
188 | v.Root = def | 208 | v.Root = def |
@@ -205,32 +225,53 @@ func (o *Output) Toggle(on bool) error { | |||
205 | return err | 225 | return err |
206 | } | 226 | } |
207 | 227 | ||
228 | func GatherReports(devs []*Device) { | ||
229 | t := time.NewTicker(10 * time.Second) | ||
230 | defer t.Stop() | ||
231 | |||
232 | for { | ||
233 | <-t.C | ||
234 | for _, d := range devs { | ||
235 | d.GetReport() | ||
236 | } | ||
237 | } | ||
238 | } | ||
239 | |||
240 | func StopBlinking(devs []*Device) { | ||
241 | t := time.NewTicker(10 * time.Second) | ||
242 | defer t.Stop() | ||
243 | |||
244 | for { | ||
245 | <-t.C | ||
246 | for _, d := range devs { | ||
247 | d.DisableBlink() | ||
248 | } | ||
249 | } | ||
250 | } | ||
251 | |||
208 | func main() { | 252 | func main() { |
209 | cfg, err := LoadAppConfig("config.json") | 253 | devs, err := LoadAppConfig("config.json") |
210 | if err != nil { | 254 | if err != nil { |
211 | fmt.Println(err) | 255 | fmt.Println(err) |
212 | return | 256 | return |
213 | } | 257 | } |
214 | 258 | ||
215 | devs := make([]string, 0, len(cfg)) | ||
216 | for k, _ := range cfg { | ||
217 | devs = append(devs, k) | ||
218 | } | ||
219 | sort.Strings(devs) | ||
220 | |||
221 | reg := []*accessory.Accessory{} | 259 | reg := []*accessory.Accessory{} |
222 | accs := make(map[*characteristic.Characteristic]*Output, 10) | 260 | accs := make(map[*characteristic.Characteristic]*Output, 10) |
223 | 261 | ||
224 | for _, k := range devs { | 262 | for _, k := range devs { |
225 | v := cfg[k] | 263 | fmt.Printf("Connecting to %s\n", k.Name) |
226 | fmt.Println("Connecting to ", v) | 264 | |
265 | if err = k.Connect(); err != nil { | ||
266 | panic(err) | ||
267 | } | ||
227 | 268 | ||
228 | report, err := v.GetReport() | 269 | report, err := k.GetReport() |
229 | if err != nil { | 270 | if err != nil { |
230 | panic(err) | 271 | panic(err) |
231 | } | 272 | } |
232 | 273 | ||
233 | for i, d := range v.Devices { | 274 | for i, d := range k.Devices { |
234 | // Unnamed outputs are unused | 275 | // Unnamed outputs are unused |
235 | if d == "" { | 276 | if d == "" { |
236 | continue | 277 | continue |
@@ -244,7 +285,7 @@ func main() { | |||
244 | output.Toggle(on) | 285 | output.Toggle(on) |
245 | }) | 286 | }) |
246 | accs[sw.Switch.On.Characteristic] = &Output{ | 287 | accs[sw.Switch.On.Characteristic] = &Output{ |
247 | Device: v, | 288 | Device: k, |
248 | Name: d, | 289 | Name: d, |
249 | } | 290 | } |
250 | sw.Switch.On.UpdateValue(report[i].Engaged) | 291 | sw.Switch.On.UpdateValue(report[i].Engaged) |
@@ -254,7 +295,7 @@ func main() { | |||
254 | 295 | ||
255 | br := accessory.NewBridge(accessory.Info{Name: "mFi Bridge"}) | 296 | br := accessory.NewBridge(accessory.Info{Name: "mFi Bridge"}) |
256 | 297 | ||
257 | t, err := hc.NewIPTransport(hc.Config{Pin: "00102003"}, br.Accessory, reg...) | 298 | t, err := hc.NewIPTransport(hc.Config{Pin: "00102003", Port: "6969"}, br.Accessory, reg...) |
258 | if err != nil { | 299 | if err != nil { |
259 | fmt.Println(err) | 300 | fmt.Println(err) |
260 | } | 301 | } |
@@ -263,5 +304,7 @@ func main() { | |||
263 | <-t.Stop() | 304 | <-t.Stop() |
264 | }) | 305 | }) |
265 | 306 | ||
307 | // Gathering reports also keeps the SSH connections alive | ||
308 | go GatherReports(devs) | ||
266 | t.Start() | 309 | t.Start() |
267 | } | 310 | } |