Skip to content
Snippets Groups Projects
Commit 58bbf7f0 authored by KERDREUX Jerome's avatar KERDREUX Jerome
Browse files

Initial release

parents
No related branches found
No related tags found
No related merge requests found
package main
import "github.com/google/uuid"
var (
mqttTopic = "zigbee2mqtt"
mqttClientID = "z2m-" + uuid.New().String()
)
package main
import (
"gitlab.imt-atlantique.fr/xaal/code/go/schemas"
"gitlab.imt-atlantique.fr/xaal/code/go/uuid"
"gitlab.imt-atlantique.fr/xaal/code/go/xaal"
)
func setupDevice(dev *xaal.Device, bDevice BridgeDevice) {
dev.VendorID = bDevice.Definition.Vendor
dev.ProductID = bDevice.Definition.Model
dev.HWID = bDevice.IeeeAddress
dev.Version = bDevice.SwBuildID
}
func NewDevices(bDevice BridgeDevice) {
// TODO: Handle errors
baseAddr, _ := uuid.RandomBase(8)
ieeeAddr, _ := hexStringToInteger(bDevice.IeeeAddress)
baseAddr, _ = baseAddr.Add(int64(ieeeAddr))
for i, expose := range bDevice.Definition.Exposes {
addr, _ := baseAddr.Add(int64(i))
var dev *xaal.Device
deviceMap := map[string]func(uuid.UUID) *xaal.Device{
"contact": schemas.NewContact,
"battery": schemas.NewBattery,
"switch": schemas.NewPowerrelay,
"temperature": schemas.NewThermometer,
"humidity": schemas.NewHygrometer,
"power": schemas.NewPowermeter,
}
if createDevice, ok := deviceMap[expose.Name]; ok {
dev = createDevice(addr)
} else if expose.Type == "switch" {
// I don't know why but some devices have a switch type
// but name
dev = schemas.NewPowerrelay(addr)
}
if dev != nil {
setupDevice(dev, bDevice)
dev.Dump()
eng.AddDevice(dev)
}
}
}
This diff is collapsed.
go.mod 0 → 100644
main.go 0 → 100644
package main
import (
"flag"
"fmt"
"gitlab.imt-atlantique.fr/xaal/code/go/xaal"
)
var (
eng *xaal.Engine
)
func main() {
mqttBroker := flag.String("broker", "", "The MQTT broker URL")
flag.Parse()
if *mqttBroker == "" {
fmt.Println("Error: MQTT broker URL is required")
return
}
client := setupMQTT(*mqttBroker, 1883)
// client.Publish("zigbee2mqtt/0x70b3d52b600f89d3/set", 0, false, `{"state": "toggle"}`)
eng = xaal.NewEngine()
eng.Start()
eng.Run()
client.Disconnect(250)
}
utils.go 0 → 100644
package main
import "strconv"
func hexStringToInteger(hexString string) (uint64, error) {
// Drop the "0x" prefix if it exists
if len(hexString) > 2 && hexString[:2] == "0x" {
hexString = hexString[2:]
}
// Convert the hex string to an integer
integerValue, err := strconv.ParseUint(hexString, 16, 64)
if err != nil {
return 0, err
}
return integerValue, nil
}
z2m.go 0 → 100644
package main
import (
"encoding/json"
"fmt"
"log"
"slices"
"sort"
MQTT "github.com/eclipse/paho.mqtt.golang"
)
var (
ignoredTopics = []string{
mqttTopic + "/bridge/groups",
mqttTopic + "/bridge/definitions",
mqttTopic + "/bridge/extensions",
mqttTopic + "/bridge/info",
mqttTopic + "/bridge/state",
mqttTopic + "/bridge/logging",
}
)
// JSON structures for the z2m device from /bridge/devices
type BridgeDevice struct {
Definition struct {
Vendor string `json:"vendor"`
Model string `json:"model"`
Exposes []Expose `json:"exposes"`
} `json:"definition"`
Type string `json:"type"`
IeeeAddress string `json:"ieee_address"`
SwBuildID string `json:"software_build_id"`
}
type Expose struct {
Name string `json:"name"`
Type string `json:"type"`
Features []Feature `json:"features,omitempty"`
Values []string `json:"values,omitempty"`
Unit string `json:"unit,omitempty"`
}
type Feature struct {
Name string `json:"name"`
Type string `json:"type"`
}
func parseDeviceJSON(jsonData []byte) {
var devices []BridgeDevice
err := json.Unmarshal([]byte(jsonData), &devices)
if err != nil {
log.Fatalf("Erreur lors du décodage du JSON : %v", err)
}
for _, device := range devices {
// Extraction des informations
fmt.Println("Vendor:", device.Definition.Vendor)
fmt.Println("Model:", device.Definition.Model)
fmt.Println("Type:", device.Type)
fmt.Println("IEEE Address:", device.IeeeAddress)
// Extracting exposed features
for _, expose := range device.Definition.Exposes {
fmt.Printf("- Name: %s, Type: %s Unit: %s\n", expose.Name, expose.Type, expose.Unit)
if len(expose.Features) > 0 {
for _, feature := range expose.Features {
fmt.Printf(" - Feature Name: %s, Type: %s,\n", feature.Name, feature.Type)
}
}
if len(expose.Values) > 0 {
fmt.Printf(" - Values: %v\n", expose.Values)
}
}
fmt.Printf("------------------\n\n")
// spew.Dump(device)
NewDevices(device)
}
}
func publishHander(client MQTT.Client, msg MQTT.Message) {
if slices.Contains(ignoredTopics, msg.Topic()) {
return
}
fmt.Printf("Received message on topic: %s\n", msg.Topic())
if msg.Topic() == mqttTopic+"/bridge/devices" {
parseDeviceJSON(msg.Payload())
} else {
var data map[string]interface{}
err := json.Unmarshal(msg.Payload(), &data)
if err != nil {
log.Fatalf("Erreur lors du décodage du JSON : %v", err)
}
keys := make([]string, 0, len(data))
for key := range data {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
fmt.Printf("%s => \t%v\n", key, data[key])
}
fmt.Printf("------------------\n\n")
}
}
func setupMQTT(mqttBroker string, port int) MQTT.Client {
// This JS style of creating a client is awfully verbose
opts := MQTT.NewClientOptions().
AddBroker(fmt.Sprintf("tcp://%s:%d", mqttBroker, port)).
SetClientID(mqttClientID).
SetDefaultPublishHandler(publishHander).SetAutoReconnect(true)
client := MQTT.NewClient(opts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
panic(token.Error())
}
if token := client.Subscribe(mqttTopic+"/#", 0, nil); token.Wait() && token.Error() != nil {
panic(token.Error())
}
return client
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment