aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mcrute@gmail.com>2016-09-24 16:41:32 -0700
committerMike Crute <mcrute@gmail.com>2016-09-24 16:41:32 -0700
commit3ccc02f8f1c08fc82f581e9b3eb4315b42614c8e (patch)
tree548856f40bc0cce12bc964500a193b665530ccf2
parent80aac9a2da274b4f539e5b6cafbfd06b1a75c9d9 (diff)
downloadgo-inform-3ccc02f8f1c08fc82f581e9b3eb4315b42614c8e.tar.bz2
go-inform-3ccc02f8f1c08fc82f581e9b3eb4315b42614c8e.tar.xz
go-inform-3ccc02f8f1c08fc82f581e9b3eb4315b42614c8e.zip
Cleanup message unpacking
-rw-r--r--inform/codec.go2
-rw-r--r--inform/inform.go44
-rw-r--r--inform/messages.go162
-rw-r--r--inform/rx_messages.go99
-rw-r--r--inform/tx_messages.go28
5 files changed, 164 insertions, 171 deletions
diff --git a/inform/codec.go b/inform/codec.go
index e74c3d7..e5c9296 100644
--- a/inform/codec.go
+++ b/inform/codec.go
@@ -8,7 +8,7 @@ import (
8) 8)
9 9
10type Codec struct { 10type Codec struct {
11 // KeyBag contains a mapping of comma-separated MAC addresses to their AES 11 // KeyBag contains a mapping of colon-separated MAC addresses to their AES
12 // keys 12 // keys
13 KeyBag map[string]string 13 KeyBag map[string]string
14} 14}
diff --git a/inform/inform.go b/inform/inform.go
index b159084..5c3ca1c 100644
--- a/inform/inform.go
+++ b/inform/inform.go
@@ -3,13 +3,11 @@ package inform
3import ( 3import (
4 "bytes" 4 "bytes"
5 "encoding/json" 5 "encoding/json"
6 "errors"
7 "fmt" 6 "fmt"
8 "github.com/mitchellh/mapstructure"
9) 7)
10 8
11const ( 9const (
12 PROTOCOL_MAGIC int32 = 1414414933 // TNBU 10 PROTOCOL_MAGIC int32 = 1414414933 // UBNT
13 INFORM_VERSION int32 = 0 11 INFORM_VERSION int32 = 0
14 DATA_VERSION int32 = 1 12 DATA_VERSION int32 = 1
15 13
@@ -91,43 +89,3 @@ func (i *InformWrapper) SetCompressed(c bool) {
91 i.Flags &= COMPRESSED_FLAG 89 i.Flags &= COMPRESSED_FLAG
92 } 90 }
93} 91}
94
95// Decode payload to a map and try to determine the inform type
96func (i *InformWrapper) decodePayload() (map[string]interface{}, string, error) {
97 var m map[string]interface{}
98
99 if err := json.Unmarshal(i.Payload, &m); err != nil {
100 return nil, "", err
101 }
102
103 t, ok := m["_type"]
104 if !ok {
105 return nil, "", errors.New("Message contains no type")
106 }
107
108 st, ok := t.(string)
109 if !ok {
110 return nil, "", errors.New("Message type is not a string")
111 }
112
113 return m, st, nil
114}
115
116// Decode payload JSON as a inform message
117func (i *InformWrapper) JsonMessage() (interface{}, error) {
118 msg, t, err := i.decodePayload()
119 if err != nil {
120 return nil, err
121 }
122
123 switch t {
124 case "noop":
125 var o NoopMessage
126 if err := mapstructure.Decode(msg, &o); err != nil {
127 return nil, err
128 }
129 return o, nil
130 }
131
132 return nil, errors.New(fmt.Sprintf("Message type %s is invalid", t))
133}
diff --git a/inform/messages.go b/inform/messages.go
new file mode 100644
index 0000000..3dd8f6d
--- /dev/null
+++ b/inform/messages.go
@@ -0,0 +1,162 @@
1package inform
2
3import (
4 "encoding/json"
5 "strconv"
6 "time"
7)
8
9type AdminMetadata struct {
10 Id string `json:"_id"`
11 Language string `json:"lang"`
12 Username string `json:"name"`
13 Password string `json:"x_password"`
14}
15
16type CommandMessage struct {
17 Metadata *AdminMetadata `json:"_admin,omitempty"`
18 Id string `json:"_id,omitempty"`
19 Type string `json:"_type"`
20 Command string `json:"cmd"`
21 DateTime string `json:"datetime"`
22 DeviceId string `json:"device_id,omitempty"`
23 MacAddress string `json:"mac,omitempty"`
24 Model string `json:"model,omitempty"`
25 OffVoltage int `json:"off_volt,omitempty"`
26 Port int `json:"port"`
27 SensorId string `json:"sId,omitempty"`
28 ServerTime string `json:"server_time_in_utc"`
29 Time int64 `json:"time"`
30 Timer int `json:"timer"`
31 Value int `json:"val"`
32 Voltage int `json:"volt,omitempty"`
33}
34
35func NewOutputCommand(port, val, timer int) *CommandMessage {
36 return &CommandMessage{
37 Type: "cmd",
38 Command: "mfi-output",
39 DateTime: time.Now().Format(time.RFC3339),
40 Port: port,
41 ServerTime: unixMicroPSTString(),
42 Time: unixMicroPST(),
43 Timer: timer,
44 Value: val,
45 }
46}
47
48type NoopMessage struct {
49 Type string `json:"_type"`
50 Interval int `json:"interval"`
51 ServerTimeUTC string `json:"server_time_in_utc"`
52}
53
54func unixMicroPST() int64 {
55 l, _ := time.LoadLocation("America/Los_Angeles")
56 tnano := time.Now().In(l).UnixNano()
57 return tnano / int64(time.Millisecond)
58}
59
60func unixMicroPSTString() string {
61 return strconv.FormatInt(unixMicroPST(), 10)
62}
63
64func unixMicroUTCString() string {
65 tnano := time.Now().UTC().UnixNano()
66 t := tnano / int64(time.Millisecond)
67 return strconv.FormatInt(t, 10)
68}
69
70func NewNoop(interval int) *NoopMessage {
71 return &NoopMessage{
72 Type: "noop",
73 Interval: interval,
74 ServerTimeUTC: unixMicroUTCString(),
75 }
76}
77
78type DeviceMessage struct {
79 IsDefault bool `json:"default"`
80 IP string `json:"ip"`
81 MacAddr string `json:"mac"`
82 ModelNumber string `json:"model"`
83 ModelName string `json:"model_display"`
84 Serial string `json:"serial"`
85 FirmwareVersion string `json:"version"`
86 Outputs []*OutputInfo
87}
88
89func (m *DeviceMessage) UnmarshalJSON(data []byte) error {
90 type Alias DeviceMessage
91 aux := &struct {
92 Alarm []struct {
93 Entries []struct {
94 Tag string `json:"tag"`
95 Type string `json:"type"`
96 Val interface{} `json:"val"`
97 } `json:"entries"`
98 Sensor string `json:"sId"`
99 } `json:"alarm"`
100 *Alias
101 }{
102 Alias: (*Alias)(m),
103 }
104
105 if err := json.Unmarshal(data, &aux); err != nil {
106 return err
107 }
108
109 m.Outputs = make([]*OutputInfo, len(aux.Alarm))
110
111 for i, a := range aux.Alarm {
112 o := &OutputInfo{
113 Id: a.Sensor,
114 Port: i + 1,
115 Dimmer: m.ModelNumber == "IWD1U",
116 }
117 m.Outputs[i] = o
118
119 for _, e := range a.Entries {
120 switch t := e.Val; e.Tag {
121 case "output":
122 o.OutputState = t.(float64) == 1
123 case "pf":
124 o.PowerFactor = t.(float64)
125 case "energy_sum":
126 o.EnergySum = t.(float64)
127 case "v_rms":
128 o.VoltageRMS = t.(float64)
129 case "i_rms":
130 o.CurrentRMS = t.(float64)
131 case "active_pwr":
132 o.Watts = t.(float64)
133 case "thismonth":
134 o.ThisMonth = t.(float64)
135 case "lastmonth":
136 o.LastMonth = t.(float64)
137 case "dimmer_level":
138 o.DimmerLevel = int(t.(float64))
139 case "dimmer_lock_setting":
140 o.DimmerLockSetting = int(t.(float64))
141 }
142 }
143 }
144
145 return nil
146}
147
148type OutputInfo struct {
149 Id string
150 Port int
151 OutputState bool
152 EnergySum float64
153 VoltageRMS float64
154 PowerFactor float64
155 CurrentRMS float64
156 Watts float64
157 ThisMonth float64
158 LastMonth float64
159 Dimmer bool
160 DimmerLevel int
161 DimmerLockSetting int
162}
diff --git a/inform/rx_messages.go b/inform/rx_messages.go
deleted file mode 100644
index 3f80fcd..0000000
--- a/inform/rx_messages.go
+++ /dev/null
@@ -1,99 +0,0 @@
1package inform
2
3// TODO: Convert string time to time.Time
4// Response packet
5type NoopMessage struct {
6 Type string `json:"_type"`
7 Interval int `json:"interval"`
8 ServerTimeUTC string `json:"server_time_in_utc"`
9}
10
11type AlarmEntry struct {
12 Tag string `json:"tag"`
13 Type string `json:"string"`
14 Value string `json:"val"` // float or int observed
15}
16
17type AlarmMessage struct {
18 Entries []*AlarmEntry `json:"entries"`
19 Index string `json:"index"`
20 Id string `json:"sId"`
21 Time int `json:"time"`
22}
23
24type InterfaceMessage struct {
25 IP string `json:"ip"`
26 MacAddress string `json:"mac"`
27 Name string `json:"name"`
28 Type string `json:"type"`
29 ReceivedBytes int `json:"rx_bytes"`
30 ReceivedDropped int `json:"rx_dropped"`
31 ReceivedErrors int `json:"rx_errors"`
32 ReceivedPackets int `json:"rx_packets"`
33 TransmittedBytes int `json:"tx_bytes"`
34 TransmittedDropped int `json:"tx_dropped"`
35 TransmittedErrors int `json:"tx_errors"`
36 TransmittedPackets int `json:"tx_packets"`
37}
38
39type RadioMessage struct {
40 Gain int `json:"builtin_ant_gain"`
41 BuiltinAntenna bool `json:"builtin_antenna"`
42 MaxTransmitPower int `json:"max_txpower"`
43 Name string `json:"name"`
44 RadioProfile string `json:"radio"`
45 // "scan_table": []
46}
47
48type AccessPointMessage struct {
49 BasicSSID string `json:"bssid"`
50 ExtendedSSID string `json:"essid"`
51 ClientConnectionQuality int `json:"ccq"`
52 Channel int `json:"channel"`
53 Id string `json:"id"`
54 Name string `json:"name"`
55 StationNumber string `json:"num_sta"` // int?
56 RadioProfile string `json:"radio"`
57 Usage string `json:"usage"`
58 ReceivedBytes int `json:"rx_bytes"`
59 ReceivedDropped int `json:"rx_dropped"`
60 ReceivedErrors int `json:"rx_errors"`
61 ReceivedPackets int `json:"rx_packets"`
62 ReceivedCrypts int `json:"rx_crypts"`
63 ReceivedFragments int `json:"rx_frags"`
64 ReceivedNetworkIDs int `json:"rx_nwids"`
65 TransmittedBytes int `json:"tx_bytes"`
66 TransmittedDropped int `json:"tx_dropped"`
67 TransmittedErrors int `json:"tx_errors"`
68 TransmittedPackets int `json:"tx_packets"`
69 TransmitPower int `json:"tx_power"`
70 TransmitRetries int `json:"tx_retries"`
71}
72
73// TODO: Convert time to time.Time
74type IncomingMessage struct {
75 Alarms []*AlarmMessage `json:"alarm"`
76 ConfigVersion string `json:"cfgversion"`
77 Default bool `json:"default"`
78 GuestToken string `json:"guest_token"`
79 Hostname string `json:"hostname"`
80 InformURL string `json:"inform_url"`
81 IP string `json:"ip"`
82 Isolated bool `json:"isolated"`
83 LocalVersion string `json:"localversion"`
84 Locating bool `json:"locating"`
85 MacAddress string `json:"mac"`
86 IsMfi string `json:"mfi"` // boolean as string
87 Model string `json:"model"`
88 ModelDisplay string `json:"model_display"`
89 PortVersion string `json:"portversion"`
90 Version string `json:"version"`
91 Serial string `json:"serial"`
92 Time int `json:"time"`
93 Trackable string `json:"trackable"` // boolean as string
94 Uplink string `json:"uplink"`
95 Uptime int `json:"uptime"`
96 Interfaces []*InterfaceMessage `json:"if_table"`
97 Radios []*RadioMessage `json:"radio_table"`
98 AccessPoints []*AccessPointMessage `json:"vap_table"`
99}
diff --git a/inform/tx_messages.go b/inform/tx_messages.go
deleted file mode 100644
index 5efddd6..0000000
--- a/inform/tx_messages.go
+++ /dev/null
@@ -1,28 +0,0 @@
1package inform
2
3type AdminMetadata struct {
4 Id string `json:"_id"`
5 Language string `json:"lang"`
6 Username string `json:"name"`
7 Password string `json:"x_password"`
8}
9
10// TODO: Convert string time to time.Time
11type CommandMessage struct {
12 Metadata *AdminMetadata `json:"_admin"`
13 Id string `json:"_id"`
14 Type string `json:"_type"` // cmd
15 Command string `json:"cmd"` // mfi-output
16 DateTime string `json:"datetime"` // 2016-07-28T01:17:55Z
17 DeviceId string `json:"device_id"`
18 MacAddress string `json:"mac"`
19 Model string `json:"model"`
20 OffVoltage int `json:"off_volt"`
21 Port int `json:"port"`
22 MessageId string `json:"sId"` // ??
23 ServerTime string `json:"server_time_in_utc"`
24 Time string `json:"time"`
25 Timer int `json:"timer"`
26 Value int `json:"val"`
27 Voltage int `json:"volt"`
28}