diff --git a/connection.go b/connection.go index 2ca3477..9eb2258 100644 --- a/connection.go +++ b/connection.go @@ -101,9 +101,11 @@ func (conn *Connection) WriteAndWaitForReport(msg interfaces.Encodable, t time.D go func() { defer close(returnChan) for msg := range pkg.returnChan { - if f, ok := msg.Data.(*serialapi.FuncApplicationCommandHandler); ok { - returnChan <- f.Report - return + if msg != nil { + if f, ok := msg.Data.(*serialapi.FuncApplicationCommandHandler); ok { + returnChan <- f.Report + return + } } logrus.Errorf("WriteAndWaitForReport: Received wrong type: %t", msg) @@ -210,7 +212,7 @@ func (conn *Connection) Reader() error { logrus.Errorf("%s - %d", err.Error(), l) } - if l == 1 { + if l == 1 && msg != nil { for index, c := range conn.inFlight { conn.RLock() if c.uuid == conn.lastCommand { @@ -253,7 +255,7 @@ func (conn *Connection) Reader() error { conn.Lock() for index, c := range conn.inFlight { if !c.Match(incomming) { - logrus.Info("Check match %#v", c) + logrus.Infof("Check match %#v", c) continue } diff --git a/database/commandclasses.go b/database/commandclasses.go index 302be97..a87aff3 100644 --- a/database/commandclasses.go +++ b/database/commandclasses.go @@ -1,5 +1,7 @@ package database +import "fmt" + // GetMandatoryCommandClasses generates a list of mandatory commandclasses func GetMandatoryCommandClasses(generic, specific byte) []*CommandClass { for k, v := range definitions { @@ -9,3 +11,13 @@ func GetMandatoryCommandClasses(generic, specific byte) []*CommandClass { } return nil } + +// GetDescription returns a generic description of the device +func GetDescription(generic, specific byte) string { + for k, v := range descriptions { + if k.Generic == generic && k.Specific == specific { + return v + } + } + return fmt.Sprintf("Unknown (generic=0x%X specific=0x%X)", generic, specific) +} diff --git a/database/mandatory.go b/database/mandatory.go index ae0e4d6..9d703a8 100644 --- a/database/mandatory.go +++ b/database/mandatory.go @@ -1012,3 +1012,146 @@ var definitions = map[Definition][]*CommandClass{ }, }, } + +var descriptions = map[Definition]string{ + Definition{ + Generic: protocol.GENERIC_TYPE_DISPLAY, + Specific: protocol.SPECIFIC_TYPE_SIMPLE_DISPLAY, + }: "Display - Simple", + Definition{ + Generic: protocol.GENERIC_TYPE_ENTRY_CONTROL, + Specific: protocol.SPECIFIC_TYPE_SECURE_BARRIER_ADDON, + }: "Motorized Barrier - Add-ON", + Definition{ + Generic: protocol.GENERIC_TYPE_ENTRY_CONTROL, + Specific: protocol.SPECIFIC_TYPE_SECURE_BARRIER_CLOSE_ONLY, + }: "Motorized Barrier - Close Only", + Definition{ + Generic: protocol.GENERIC_TYPE_ENTRY_CONTROL, + Specific: protocol.SPECIFIC_TYPE_SECURE_BARRIER_OPEN_ONLY, + }: "Motorized Barrier - Open Only", + Definition{ + Generic: protocol.GENERIC_TYPE_ENTRY_CONTROL, + Specific: protocol.SPECIFIC_TYPE_SECURE_DOOR, + }: "Motorized Barrier - GDO", + Definition{ + Generic: protocol.GENERIC_TYPE_ENTRY_CONTROL, + Specific: protocol.SPECIFIC_TYPE_SECURE_GATE, + }: "Motorized Barrier - Gate", + Definition{ + Generic: protocol.GENERIC_TYPE_ENTRY_CONTROL, + Specific: protocol.SPECIFIC_TYPE_SECURE_KEYPAD, + }: "Entry Control Keypad", + Definition{ + Generic: protocol.GENERIC_TYPE_ENTRY_CONTROL, + Specific: protocol.SPECIFIC_TYPE_SECURE_KEYPAD_DOOR_LOCK, + }: "Door Lock - Keypad", + Definition{ + Generic: protocol.GENERIC_TYPE_ENTRY_CONTROL, + Specific: protocol.SPECIFIC_TYPE_SECURE_LOCKBOX, + }: "Lockbox", + Definition{ + Generic: protocol.GENERIC_TYPE_GENERIC_CONTROLLER, + Specific: protocol.SPECIFIC_TYPE_PORTABLE_REMOTE_CONTROLLER, + }: "Remote Controller - Multi Purpose", + Definition{ + Generic: protocol.GENERIC_TYPE_GENERIC_CONTROLLER, + Specific: protocol.SPECIFIC_TYPE_REMOTE_CONTROL_AV, + }: "Remote Control - AV", + Definition{ + Generic: protocol.GENERIC_TYPE_GENERIC_CONTROLLER, + Specific: protocol.SPECIFIC_TYPE_REMOTE_CONTROL_SIMPLE, + }: "Remote Control - Simple", + Definition{ + Generic: protocol.GENERIC_TYPE_METER, + Specific: protocol.SPECIFIC_TYPE_SIMPLE_METER, + }: "Sub Energy Meter", + Definition{ + Generic: protocol.GENERIC_TYPE_METER, + Specific: protocol.SPECIFIC_TYPE_WHOLE_HOME_METER_SIMPLE, + }: "Whole Home Meter - Simple", + Definition{ + Generic: protocol.GENERIC_TYPE_REPEATER_SLAVE, + Specific: protocol.SPECIFIC_TYPE_REPEATER_SLAVE, + }: "Repeater", + Definition{ + Generic: protocol.GENERIC_TYPE_SENSOR_MULTILEVEL, + Specific: protocol.SPECIFIC_TYPE_ROUTING_MULTILEVEL_SENSOR, + }: "Sensor - Multilevel", + Definition{ + Generic: protocol.GENERIC_TYPE_SENSOR_NOTIFICATION, + Specific: protocol.SPECIFIC_TYPE_NOTIFICATION_SENSOR, + }: "Sensor - Notification", + Definition{ + Generic: protocol.GENERIC_TYPE_STATIC_CONTROLLER, + Specific: protocol.SPECIFIC_TYPE_GATEWAY, + }: "Gateway", + Definition{ + Generic: protocol.GENERIC_TYPE_STATIC_CONTROLLER, + Specific: protocol.SPECIFIC_TYPE_PC_CONTROLLER, + }: "Central Controller", + Definition{ + Generic: protocol.GENERIC_TYPE_STATIC_CONTROLLER, + Specific: protocol.SPECIFIC_TYPE_SET_TOP_BOX, + }: "Set Top Box", + Definition{ + Generic: protocol.GENERIC_TYPE_STATIC_CONTROLLER, + Specific: protocol.SPECIFIC_TYPE_SUB_SYSTEM_CONTROLLER, + }: "Sub System Controller", + Definition{ + Generic: protocol.GENERIC_TYPE_STATIC_CONTROLLER, + Specific: protocol.SPECIFIC_TYPE_TV, + }: "TV", + Definition{ + Generic: protocol.GENERIC_TYPE_SWITCH_BINARY, + Specific: protocol.SPECIFIC_TYPE_IRRIGATION_CONTROLLER, + }: "Irrigation Control", + Definition{ + Generic: protocol.GENERIC_TYPE_SWITCH_BINARY, + Specific: protocol.SPECIFIC_TYPE_POWER_STRIP, + }: "Power Strip", + Definition{ + Generic: protocol.GENERIC_TYPE_SWITCH_BINARY, + Specific: protocol.SPECIFIC_TYPE_POWER_SWITCH_BINARY, + }: "On/Off Power Switch", + Definition{ + Generic: protocol.GENERIC_TYPE_SWITCH_BINARY, + Specific: protocol.SPECIFIC_TYPE_SIREN, + }: "Siren", + Definition{ + Generic: protocol.GENERIC_TYPE_SWITCH_BINARY, + Specific: protocol.SPECIFIC_TYPE_VALVE_OPEN_CLOSE, + }: "Valve - open/close", + Definition{ + Generic: protocol.GENERIC_TYPE_SWITCH_MULTILEVEL, + Specific: protocol.SPECIFIC_TYPE_CLASS_A_MOTOR_CONTROL, + }: "Window Covering - No Position/Endpoint", + Definition{ + Generic: protocol.GENERIC_TYPE_SWITCH_MULTILEVEL, + Specific: protocol.SPECIFIC_TYPE_CLASS_B_MOTOR_CONTROL, + }: "Window Covering - Endpoint Aware", + Definition{ + Generic: protocol.GENERIC_TYPE_SWITCH_MULTILEVEL, + Specific: protocol.SPECIFIC_TYPE_CLASS_C_MOTOR_CONTROL, + }: "Window Covering - Position/Endpoint Aware", + Definition{ + Generic: protocol.GENERIC_TYPE_SWITCH_MULTILEVEL, + Specific: protocol.SPECIFIC_TYPE_FAN_SWITCH, + }: "Fan Switch", + Definition{ + Generic: protocol.GENERIC_TYPE_SWITCH_MULTILEVEL, + Specific: protocol.SPECIFIC_TYPE_POWER_SWITCH_MULTILEVEL, + }: "Light Dimmer Switch", + Definition{ + Generic: protocol.GENERIC_TYPE_THERMOSTAT, + Specific: protocol.SPECIFIC_TYPE_SETBACK_THERMOSTAT, + }: "Thermostat - Setback", + Definition{ + Generic: protocol.GENERIC_TYPE_THERMOSTAT, + Specific: protocol.SPECIFIC_TYPE_THERMOSTAT_GENERAL_V2, + }: "Thermostat - HVAC", + Definition{ + Generic: protocol.GENERIC_TYPE_WALL_CONTROLLER, + Specific: protocol.SPECIFIC_TYPE_BASIC_WALL_CONTROLLER, + }: "Wall Controller", +} diff --git a/generators/commandclasses/commandclasses.go b/generators/commandclasses/commandclasses.go index cbd84fb..d42f04e 100644 --- a/generators/commandclasses/commandclasses.go +++ b/generators/commandclasses/commandclasses.go @@ -14,6 +14,7 @@ import ( type templates struct { CommandClasses map[string][]string + Descriptions map[string]string Package string } @@ -30,6 +31,7 @@ func main() { data := &templates{ CommandClasses: make(map[string][]string), + Descriptions: make(map[string]string), Package: packageName, } //data.CommandClasses[database.Definition{Generic: 0x01, Specific: 0x02}] = []string{ @@ -51,6 +53,7 @@ func main() { if text == "" { for _, def := range defs { data.CommandClasses[def[0]+"|"+def[1]] = classes + data.Descriptions[def[0]+"|"+def[1]] = def[2] } defs = [][]string{} classes = []string{} @@ -66,6 +69,7 @@ func main() { defs = append(defs, []string{ strings.TrimSpace(tmp[len(tmp)-2]), strings.TrimSpace(tmp[len(tmp)-1]), + strings.TrimSpace(strings.TrimLeft(strings.Join(tmp[:len(tmp)-2], "-"), "#")), }) } @@ -247,4 +251,14 @@ var definitions = map[Definition][]*CommandClass{ {{- end}} } +var descriptions = map[Definition]string{ +{{- range $key, $value := .Descriptions }} + Definition{ + Generic: protocol.{{ getGeneric $key }}, + Specific: protocol.{{ getSpecific $key }}, + }: "{{ $value }}", + +{{- end}} +} + ` diff --git a/nodes/node.go b/nodes/node.go index eace09a..0c49e48 100644 --- a/nodes/node.go +++ b/nodes/node.go @@ -150,6 +150,7 @@ func (n *Node) Identify() { for { if n.ProtocolInfo() == nil { + logrus.Infof("Identify %d - Step 1 (RequestProtocolInfo)", n.Id) resp, err := n.RequestProtocolInfo() if err != nil { logrus.Errorf("Node ident: Failed RequestProtocolInfo: %s", err.Error()) @@ -168,9 +169,11 @@ func (n *Node) Identify() { // set basic commandClasses classes := database.GetMandatoryCommandClasses(n.ProtocolInfo().Generic, n.ProtocolInfo().Specific) + desc := database.GetDescription(n.ProtocolInfo().Generic, n.ProtocolInfo().Specific) n.Lock() n.CommandClasses = classes + n.Description = desc n.Unlock() //<-self.Connection.SendRaw([]byte{serialapi.GetNodeProtocolInfo, byte(index + 1)}) // Request node information @@ -179,6 +182,7 @@ func (n *Node) Identify() { // All manufacturer specific information such as vendor, vendors product ID and product type if n.ManufacurerSpecific == nil { <-n.isAwake() + logrus.Infof("Identify %d - Step 2 (RequestManufacturerSpecific)", n.Id) resp, err := n.RequestManufacturerSpecific() if err != nil { logrus.Errorf("Node ident: Failed ManufacurerSpecific: %s", err.Error()) @@ -238,9 +242,10 @@ func (n *Node) Identify() { // Request node endpoints n.RLock() - if n.Endpoints == nil { + if n.Endpoints == nil && n.HasCommand(commands.MultiInstance) { n.RUnlock() <-n.isAwake() + logrus.Infof("Identify %d - Step 3 (RequestEndpoints)", n.Id) err := n.RequestEndpoints() if err != nil { <-time.After(time.Second * 10) @@ -260,6 +265,7 @@ func (n *Node) Identify() { if !n.statesOk { n.RUnlock() <-n.isAwake() + logrus.Infof("Identify %d - Step 4 (RequestStates)", n.Id) err := n.RequestStates() if err != nil { <-time.After(time.Second * 10)