From 6ef80f843831e26b0b25f450cb8faf31d0b291cd Mon Sep 17 00:00:00 2001 From: Wolfgang Hottgenroth Date: Mon, 16 Feb 2026 17:07:23 +0100 Subject: [PATCH] z2m working again --- attic/z2m/models/gs361ah04/gs361ah04.go | 15 --- attic/z2m/models/wsdcgq01lm/wsdcgq01lm.go | 11 --- attic/z2m/models/wsdcgq11lm/wsdcgq11lm.go | 10 -- attic/z2m/z2m.go | 107 ---------------------- src/udi/config-test-2.json | 10 ++ src/udi/dispatcher/dispatcher.go | 6 +- src/udi/handlers/z2m/z2m.go | 80 ++++++++++++++++ 7 files changed, 93 insertions(+), 146 deletions(-) delete mode 100644 attic/z2m/models/gs361ah04/gs361ah04.go delete mode 100644 attic/z2m/models/wsdcgq01lm/wsdcgq01lm.go delete mode 100644 attic/z2m/models/wsdcgq11lm/wsdcgq11lm.go delete mode 100644 attic/z2m/z2m.go create mode 100644 src/udi/handlers/z2m/z2m.go diff --git a/attic/z2m/models/gs361ah04/gs361ah04.go b/attic/z2m/models/gs361ah04/gs361ah04.go deleted file mode 100644 index f23a0f7..0000000 --- a/attic/z2m/models/gs361ah04/gs361ah04.go +++ /dev/null @@ -1,15 +0,0 @@ -package gs361ah04 - -type Observation struct { - LinkQuality uint8 `unit:"" json:"linkquality"` - Battery uint8 `unit:"%" json:"battery"` - AwayMode string `unit:"" json:"away_mode"` - ChildLock string `unit:"" json:"child_lock"` - CurrentHeatingSetpoint float32 `unit:"°C" json:"current_heating_setpoint"` - LocalTemperature float32 `unit:"°C" json:"local_temperature"` - Preset string `unit:"" json:"preset"` - SystemMode string `unit:"" json:"system_mode"` - ValveDetection string `unit:"" json:"valve_detection"` - WindowDetection string `unit:"" json:"window_detection"` -} - diff --git a/attic/z2m/models/wsdcgq01lm/wsdcgq01lm.go b/attic/z2m/models/wsdcgq01lm/wsdcgq01lm.go deleted file mode 100644 index 29b3e02..0000000 --- a/attic/z2m/models/wsdcgq01lm/wsdcgq01lm.go +++ /dev/null @@ -1,11 +0,0 @@ -package wsdcgq01lm - -type Observation struct { - LinkQuality uint8 `unit:"" json:"linkquality"` - Battery uint8 `unit:"%" json:"battery"` - Humidity float32 `unit:"%H" json:"humidity"` - Pressure float32 `unit:"mbar" json:"pressure"` - Temperature float32 `unit:"°C" json:"temperature"` - Voltage uint16 `unit:"mV" json:"voltage"` -} - diff --git a/attic/z2m/models/wsdcgq11lm/wsdcgq11lm.go b/attic/z2m/models/wsdcgq11lm/wsdcgq11lm.go deleted file mode 100644 index 1cc0a67..0000000 --- a/attic/z2m/models/wsdcgq11lm/wsdcgq11lm.go +++ /dev/null @@ -1,10 +0,0 @@ -package wsdcgq11lm - -type Observation struct { - LinkQuality uint8 `unit:"" json:"linkquality"` - Battery uint8 `unit:"%" json:"battery"` - Humidity float32 `unit:"%H" json:"humidity"` - Temperature float32 `unit:"°C" json:"temperature"` - Voltage uint16 `unit:"mV" json:"voltage"` -} - diff --git a/attic/z2m/z2m.go b/attic/z2m/z2m.go deleted file mode 100644 index 4dbe862..0000000 --- a/attic/z2m/z2m.go +++ /dev/null @@ -1,107 +0,0 @@ -package z2m - -import ( - "encoding/json" - "fmt" - "log" - "reflect" - "strings" - "time" - "udi/config" - "udi/database" - "udi/handlers/handler" - "udi/handlers/z2m/models/gs361ah04" - "udi/handlers/z2m/models/wsdcgq01lm" - "udi/handlers/z2m/models/wsdcgq11lm" -) - -type Z2MHandler struct { - handler.CommonHandler - dbh *database.DatabaseHandle -} - -func parse(T any, payload string, variables *map[string]database.VariableType) error { - observationType := reflect.TypeOf(T) - observation := reflect.New(observationType).Interface() - - err := json.Unmarshal([]byte(payload), observation) - if err != nil { - return fmt.Errorf("Unable to parse payload into Observation struct: %v, %s", err, payload) - } - - observationValue := reflect.ValueOf(observation).Elem() - - for i := 0; i < observationType.NumField(); i++ { - field := observationType.Field(i) - name := field.Name - unit := field.Tag.Get("unit") - value := observationValue.Field(i).Interface() - - (*variables)[name] = database.VariableType{ - Label: name, - Variable: "y", - Unit: unit, - Value: value, - } - } - - return nil -} - -func New(id string, config config.HandlerConfigT) handler.Handler { - t := &Z2MHandler{} - t.Id = id - t.dbh = database.NewDatabaseHandle() - log.Printf("Handler Z2M %d initialized", id) - return t -} - -func (self *Z2MHandler) Handle(message handler.MessageT) { - log.Printf("Handler Z2M %d processing %s -> %s", self.Id, message.Topic, message.Payload) - - var measurement database.Measurement - measurement.Time = time.Now() - - subTopics := strings.Split(message.Topic, "/") - deviceId := subTopics[1] - log.Printf("DeviceId: %s", deviceId) - device, err1 := self.dbh.GetDeviceByLabel(deviceId) - if err1 != nil { - self.Lost("Error when loading device", err1, message) - return - } - log.Printf("Device: %s", device) - - measurement.Application = device.Application.Label - measurement.Device = device.Attributes["Label"].(string) - - var T any - switch device.DeviceType.ModelIdentifier { - case "WSDCGQ11LM": - T = wsdcgq11lm.Observation{} - case "WSDCGQ01LM": - T = wsdcgq01lm.Observation{} - case "GS361A-H04": - T = gs361ah04.Observation{} - default: - self.Lost(fmt.Sprintf("No parser found for %s", device.DeviceType.ModelIdentifier), nil, message) - return - } - - measurement.Values = make(map[string]database.VariableType) - measurement.Attributes = make(map[string]interface{}) - err3 := parse(T, - message.Payload, - &(measurement.Values)) - if err3 != nil { - self.Lost("Model parser failed", err3, message) - return - } - - measurement.Attributes["Status"] = "ok" - measurement.Attributes["DeviceId"] = deviceId - measurement.Attributes["DeviceModel"] = device.DeviceType.ModelIdentifier - log.Printf("Prepared measurement item: %s", measurement) - self.dbh.StoreMeasurement(&measurement) - self.S() -} diff --git a/src/udi/config-test-2.json b/src/udi/config-test-2.json index 32d348d..b7827e7 100644 --- a/src/udi/config-test-2.json +++ b/src/udi/config-test-2.json @@ -100,6 +100,16 @@ } } }, + { + "topics": [ "zigbee2mqtt/+" ], + "handler": "Z2M", + "id": "Z2M", + "config": { + "databaseConnStr": "", + "attributes": { + } + } + }, { "topics": [ "shellyplusht/+/status/temperature:0" ], "handler": "SVEJ", diff --git a/src/udi/dispatcher/dispatcher.go b/src/udi/dispatcher/dispatcher.go index 7e45e58..9188044 100644 --- a/src/udi/dispatcher/dispatcher.go +++ b/src/udi/dispatcher/dispatcher.go @@ -20,7 +20,7 @@ import ( "udi/handlers/sver" // "udi/handlers/ttn" - // "udi/handlers/z2m" + "udi/handlers/z2m" "udi/mqtt" ) @@ -54,8 +54,8 @@ func InitDispatcher() { factory = locative.New case "PREP": factory = prepared.New - // case "Z2M": - // factory = z2m.New + case "Z2M": + factory = z2m.New case "Car": factory = car.New default: diff --git a/src/udi/handlers/z2m/z2m.go b/src/udi/handlers/z2m/z2m.go new file mode 100644 index 0000000..bd4194f --- /dev/null +++ b/src/udi/handlers/z2m/z2m.go @@ -0,0 +1,80 @@ +package z2m + +import ( + "encoding/json" + "fmt" + "log" + "strings" + "time" + "udi/config" + "udi/database" + "udi/handlers/handler" +) + +type Z2MHandler struct { + handler.CommonHandler + dbh *database.DatabaseHandle +} + +func New(id string, config config.HandlerConfigT) handler.Handler { + t := &Z2MHandler{} + t.Id = id + t.dbh = database.NewDatabaseHandle() + log.Printf("Handler Z2M %d initialized", id) + return t +} + +func (self *Z2MHandler) Handle(message handler.MessageT) { + log.Printf("Handler Z2M %d processing %s -> %s", self.Id, message.Topic, message.Payload) + + var measurement database.Measurement + measurement.Time = time.Now() + + subTopics := strings.Split(message.Topic, "/") + deviceId := subTopics[1] + log.Printf("DeviceId: %s", deviceId) + + measurement.Device = deviceId + + // Parse JSON direkt in eine map + var jsonData map[string]interface{} + err := json.Unmarshal([]byte(message.Payload), &jsonData) + if err != nil { + self.Lost("Failed to parse JSON payload", err, message) + return + } + + measurement.Attributes = make(map[string]interface{}) + measurement.Values = make(map[string]database.VariableType) + + // Extract device info for application naming + if deviceData, ok := jsonData["device"]; ok { + if deviceMap, ok := deviceData.(map[string]any); ok { + manufacturerId, hasManufacturer := deviceMap["manufacturerID"] + model, hasModel := deviceMap["model"] + + if !hasManufacturer || !hasModel { + self.Lost("Missing manufacturerID or model in device data", fmt.Errorf("manufacturerID: %v, model: %v", hasManufacturer, hasModel), handler.MessageT{}) + return + } + + measurement.Application = "z2m_" + fmt.Sprintf("%v", manufacturerId) + "_" + model.(string) + } + delete(jsonData, "device") + } + + // Konvertiere die restlichen Elemente in VariableType-Map + for key, value := range jsonData { + measurement.Values[key] = database.VariableType{ + Label: key, + Variable: "", + Unit: "", + Value: value, + } + } + + measurement.Attributes["Status"] = "ok" + log.Printf("Prepared measurement item: %s", measurement) + self.dbh.StoreMeasurement(&measurement) + self.S() +}