From 88a0976174eb96867af4c968fc72716f87e54094 Mon Sep 17 00:00:00 2001 From: Mike Crute Date: Sat, 24 Sep 2016 16:46:59 -0700 Subject: Split messages again --- inform/messages.go | 162 -------------------------------------------------- inform/rx_messages.go | 93 +++++++++++++++++++++++++++++ inform/server.go | 92 ++++++++++++++++++++++++++++ inform/tx_messages.go | 69 +++++++++++++++++++++ 4 files changed, 254 insertions(+), 162 deletions(-) delete mode 100644 inform/messages.go create mode 100644 inform/rx_messages.go create mode 100644 inform/server.go create mode 100644 inform/tx_messages.go diff --git a/inform/messages.go b/inform/messages.go deleted file mode 100644 index 3dd8f6d..0000000 --- a/inform/messages.go +++ /dev/null @@ -1,162 +0,0 @@ -package inform - -import ( - "encoding/json" - "strconv" - "time" -) - -type AdminMetadata struct { - Id string `json:"_id"` - Language string `json:"lang"` - Username string `json:"name"` - Password string `json:"x_password"` -} - -type CommandMessage struct { - Metadata *AdminMetadata `json:"_admin,omitempty"` - Id string `json:"_id,omitempty"` - Type string `json:"_type"` - Command string `json:"cmd"` - DateTime string `json:"datetime"` - DeviceId string `json:"device_id,omitempty"` - MacAddress string `json:"mac,omitempty"` - Model string `json:"model,omitempty"` - OffVoltage int `json:"off_volt,omitempty"` - Port int `json:"port"` - SensorId string `json:"sId,omitempty"` - ServerTime string `json:"server_time_in_utc"` - Time int64 `json:"time"` - Timer int `json:"timer"` - Value int `json:"val"` - Voltage int `json:"volt,omitempty"` -} - -func NewOutputCommand(port, val, timer int) *CommandMessage { - return &CommandMessage{ - Type: "cmd", - Command: "mfi-output", - DateTime: time.Now().Format(time.RFC3339), - Port: port, - ServerTime: unixMicroPSTString(), - Time: unixMicroPST(), - Timer: timer, - Value: val, - } -} - -type NoopMessage struct { - Type string `json:"_type"` - Interval int `json:"interval"` - ServerTimeUTC string `json:"server_time_in_utc"` -} - -func unixMicroPST() int64 { - l, _ := time.LoadLocation("America/Los_Angeles") - tnano := time.Now().In(l).UnixNano() - return tnano / int64(time.Millisecond) -} - -func unixMicroPSTString() string { - return strconv.FormatInt(unixMicroPST(), 10) -} - -func unixMicroUTCString() string { - tnano := time.Now().UTC().UnixNano() - t := tnano / int64(time.Millisecond) - return strconv.FormatInt(t, 10) -} - -func NewNoop(interval int) *NoopMessage { - return &NoopMessage{ - Type: "noop", - Interval: interval, - ServerTimeUTC: unixMicroUTCString(), - } -} - -type DeviceMessage struct { - IsDefault bool `json:"default"` - IP string `json:"ip"` - MacAddr string `json:"mac"` - ModelNumber string `json:"model"` - ModelName string `json:"model_display"` - Serial string `json:"serial"` - FirmwareVersion string `json:"version"` - Outputs []*OutputInfo -} - -func (m *DeviceMessage) UnmarshalJSON(data []byte) error { - type Alias DeviceMessage - aux := &struct { - Alarm []struct { - Entries []struct { - Tag string `json:"tag"` - Type string `json:"type"` - Val interface{} `json:"val"` - } `json:"entries"` - Sensor string `json:"sId"` - } `json:"alarm"` - *Alias - }{ - Alias: (*Alias)(m), - } - - if err := json.Unmarshal(data, &aux); err != nil { - return err - } - - m.Outputs = make([]*OutputInfo, len(aux.Alarm)) - - for i, a := range aux.Alarm { - o := &OutputInfo{ - Id: a.Sensor, - Port: i + 1, - Dimmer: m.ModelNumber == "IWD1U", - } - m.Outputs[i] = o - - for _, e := range a.Entries { - switch t := e.Val; e.Tag { - case "output": - o.OutputState = t.(float64) == 1 - case "pf": - o.PowerFactor = t.(float64) - case "energy_sum": - o.EnergySum = t.(float64) - case "v_rms": - o.VoltageRMS = t.(float64) - case "i_rms": - o.CurrentRMS = t.(float64) - case "active_pwr": - o.Watts = t.(float64) - case "thismonth": - o.ThisMonth = t.(float64) - case "lastmonth": - o.LastMonth = t.(float64) - case "dimmer_level": - o.DimmerLevel = int(t.(float64)) - case "dimmer_lock_setting": - o.DimmerLockSetting = int(t.(float64)) - } - } - } - - return nil -} - -type OutputInfo struct { - Id string - Port int - OutputState bool - EnergySum float64 - VoltageRMS float64 - PowerFactor float64 - CurrentRMS float64 - Watts float64 - ThisMonth float64 - LastMonth float64 - Dimmer bool - DimmerLevel int - DimmerLockSetting int -} diff --git a/inform/rx_messages.go b/inform/rx_messages.go new file mode 100644 index 0000000..68fd393 --- /dev/null +++ b/inform/rx_messages.go @@ -0,0 +1,93 @@ +package inform + +// Messages we receive from devices + +import ( + "encoding/json" +) + +type DeviceMessage struct { + IsDefault bool `json:"default"` + IP string `json:"ip"` + MacAddr string `json:"mac"` + ModelNumber string `json:"model"` + ModelName string `json:"model_display"` + Serial string `json:"serial"` + FirmwareVersion string `json:"version"` + Outputs []*OutputInfo +} + +type OutputInfo struct { + Id string + Port int + OutputState bool + EnergySum float64 + VoltageRMS float64 + PowerFactor float64 + CurrentRMS float64 + Watts float64 + ThisMonth float64 + LastMonth float64 + Dimmer bool + DimmerLevel int + DimmerLockSetting int +} + +func (m *DeviceMessage) UnmarshalJSON(data []byte) error { + type Alias DeviceMessage + aux := &struct { + Alarm []struct { + Entries []struct { + Tag string `json:"tag"` + Type string `json:"type"` + Val interface{} `json:"val"` + } `json:"entries"` + Sensor string `json:"sId"` + } `json:"alarm"` + *Alias + }{ + Alias: (*Alias)(m), + } + + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + m.Outputs = make([]*OutputInfo, len(aux.Alarm)) + + for i, a := range aux.Alarm { + o := &OutputInfo{ + Id: a.Sensor, + Port: i + 1, + Dimmer: m.ModelNumber == "IWD1U", + } + m.Outputs[i] = o + + for _, e := range a.Entries { + switch t := e.Val; e.Tag { + case "output": + o.OutputState = t.(float64) == 1 + case "pf": + o.PowerFactor = t.(float64) + case "energy_sum": + o.EnergySum = t.(float64) + case "v_rms": + o.VoltageRMS = t.(float64) + case "i_rms": + o.CurrentRMS = t.(float64) + case "active_pwr": + o.Watts = t.(float64) + case "thismonth": + o.ThisMonth = t.(float64) + case "lastmonth": + o.LastMonth = t.(float64) + case "dimmer_level": + o.DimmerLevel = int(t.(float64)) + case "dimmer_lock_setting": + o.DimmerLockSetting = int(t.(float64)) + } + } + } + + return nil +} diff --git a/inform/server.go b/inform/server.go new file mode 100644 index 0000000..7e33a86 --- /dev/null +++ b/inform/server.go @@ -0,0 +1,92 @@ +package inform + +import ( + "fmt" + "log" + "net/http" + "time" +) + +type StateTree struct { + states map[string]map[int]int +} + +func NewStateTree() *StateTree { + return &StateTree{make(map[string]map[int]int)} +} + +func (t *StateTree) ensureNode(device string, port int) { + _, ok := t.states[device] + if !ok { + t.states[device] = make(map[int]int) + } + + _, ok = t.states[device][port] + if !ok { + t.states[device][port] = 0 + } +} + +func (t *StateTree) GetState(device string, port int) int { + t.ensureNode(device, port) + return t.states[device][port] +} + +func (t *StateTree) SetState(device string, port, value int) { + t.ensureNode(device, port) + t.states[device][port] = value +} + +func Log(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handler.ServeHTTP(w, r) + t := time.Now().Format("02/Jan/2006 15:04:05") + log.Printf("%s - - [%s] \"%s %s %s\"", r.RemoteAddr, t, r.Method, r.URL, r.Proto) + // Addr - - [D/M/Y H:M:S] "Method RequestURI Proto" Code Size + // 127.0.0.1 - - [24/Sep/2016 14:30:35] "GET / HTTP/1.1" 200 - + }) +} + +type InformHandler struct { + Codec *Codec + StateTree *StateTree +} + +func (h *InformHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "405 Method Not Allowed", http.StatusMethodNotAllowed) + return + } + + if r.URL != nil && r.URL.Path != "/inform" { + http.Error(w, "404 Not Found", http.StatusNotFound) + return + } + + msg, err := h.Codec.Unmarshal(r.Body) + if err != nil { + http.Error(w, "Bad Request", http.StatusBadRequest) + return + } + + pl := NewInformWrapper() + copy(pl.MacAddr, msg.MacAddr) + pl.SetEncrypted(true) + + // TODO: compare current state to tree and update + + res, err := h.Codec.Marshal(pl) + if err != nil { + http.Error(w, "Server Error", 500) + return + } + + fmt.Fprintf(w, "%s", res) +} + +func NewServer(handler *InformHandler) *http.Server { + return &http.Server{ + Addr: ":6080", + Handler: Log(handler), + } +} diff --git a/inform/tx_messages.go b/inform/tx_messages.go new file mode 100644 index 0000000..0021ef3 --- /dev/null +++ b/inform/tx_messages.go @@ -0,0 +1,69 @@ +package inform + +// Messages we send to devices + +import ( + "strconv" + "time" +) + +type CommandMessage struct { + Id string `json:"_id,omitempty"` + Type string `json:"_type"` + Command string `json:"cmd"` + DateTime string `json:"datetime"` + DeviceId string `json:"device_id,omitempty"` + MacAddress string `json:"mac,omitempty"` + Model string `json:"model,omitempty"` + OffVoltage int `json:"off_volt,omitempty"` + Port int `json:"port"` + SensorId string `json:"sId,omitempty"` + ServerTime string `json:"server_time_in_utc"` + Time int64 `json:"time"` + Timer int `json:"timer"` + Value int `json:"val"` + Voltage int `json:"volt,omitempty"` +} + +func NewOutputCommand(port, val, timer int) *CommandMessage { + return &CommandMessage{ + Type: "cmd", + Command: "mfi-output", + DateTime: time.Now().Format(time.RFC3339), + Port: port, + ServerTime: unixMicroPSTString(), + Time: unixMicroPST(), + Timer: timer, + Value: val, + } +} + +type NoopMessage struct { + Type string `json:"_type"` + Interval int `json:"interval"` + ServerTimeUTC string `json:"server_time_in_utc"` +} + +func unixMicroPST() int64 { + l, _ := time.LoadLocation("America/Los_Angeles") + tnano := time.Now().In(l).UnixNano() + return tnano / int64(time.Millisecond) +} + +func unixMicroPSTString() string { + return strconv.FormatInt(unixMicroPST(), 10) +} + +func unixMicroUTCString() string { + tnano := time.Now().UTC().UnixNano() + t := tnano / int64(time.Millisecond) + return strconv.FormatInt(t, 10) +} + +func NewNoop(interval int) *NoopMessage { + return &NoopMessage{ + Type: "noop", + Interval: interval, + ServerTimeUTC: unixMicroUTCString(), + } +} -- cgit v1.2.3