summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2018-06-06 04:01:25 +0000
committerMike Crute <mike@crute.us>2018-06-06 04:01:25 +0000
commit451f69f66be6e0a9d9c9eba96974d8452ca5d134 (patch)
treefc344df8d1b2c69a41fdb83743824d4ee4df7108
parent365bc847b69551a0107950bed2f881ff9c779677 (diff)
downloadmfi_homekit-451f69f66be6e0a9d9c9eba96974d8452ca5d134.tar.bz2
mfi_homekit-451f69f66be6e0a9d9c9eba96974d8452ca5d134.tar.xz
mfi_homekit-451f69f66be6e0a9d9c9eba96974d8452ca5d134.zip
Enable switch toggling
-rw-r--r--main.go109
1 files changed, 76 insertions, 33 deletions
diff --git a/main.go b/main.go
index d2d1fd7..5a12a93 100644
--- a/main.go
+++ b/main.go
@@ -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
16const CMD_PATH = "/var/etc/persistent/power_control.sh" 16const CMD_PATH = "/var/etc/persistent/power_control.sh"
17 17
18type Device struct { 18type 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
27func (d *Device) GetUsername() string { 29func (d *Device) GetUsername() string {
@@ -54,11 +56,12 @@ func (d *Device) GetPort() int {
54 } 56 }
55} 57}
56 58
57func (d *Device) newSession() (*ssh.Client, *ssh.Session, error) { 59func (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
80func (d *Device) Disconnect() {
81 d.sshClient.Close()
82}
83
84func (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
82func (d *Device) runCommand(cmd string) ([]DeviceOutput, error) { 100func (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
172func LoadAppConfig(filename string) (map[string]*Device, error) { 192func 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
228func 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
240func 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
208func main() { 252func 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}