瀏覽代碼

[FEAT] start zigbee2mqtt compat

Adrien Carteron 2 年之前
父節點
當前提交
2e27f44103

+ 3 - 3
cmd/homectrl/main.go

@@ -5,7 +5,7 @@ import (
 	"io/ioutil"
 	"os"
 
-	"homectrl/internal/connectors/mqtt"
+	"homectrl/internal/connectors/zigbee2mqtt"
 	interaction "homectrl/internal/devices"
 )
 
@@ -28,7 +28,7 @@ func main() {
 			interaction.Builder(byteValue)
 		}
 	}()
-	c := mqtt.New(broker, port)
-	c.SubTopic("zigbee2mqtt/bridge/devices")
+	var inst = zigbee2mqtt.Zigbee2mqtt{}
+	inst.New(broker, port)
 
 }

+ 65 - 25
internal/connectors/mqtt/mqtt.go

@@ -10,9 +10,20 @@ import (
 	mqtt_cli "github.com/eclipse/paho.mqtt.golang"
 )
 
-type mqtt struct {
-	client mqtt_cli.Client
-	topics list.List
+type Mqtt interface {
+	New(string, int) Mqtt
+	connect(string, int) mqtt_cli.Client
+	SubscribeTopicSubscribeTopicSubscribeTopic(string)
+	subscribeTopic(string)
+}
+
+// Mqtt defines mqtt broker client and list of suscribed topics
+type MqttInst struct {
+	options *mqtt_cli.ClientOptions
+	client  mqtt_cli.Client
+	topics  list.List
+	broker  string
+	port    int
 }
 
 var messagePubHandler mqtt_cli.MessageHandler = func(client mqtt_cli.Client, msg mqtt_cli.Message) {
@@ -20,16 +31,16 @@ var messagePubHandler mqtt_cli.MessageHandler = func(client mqtt_cli.Client, msg
 	devices.Builder(msg.Payload())
 }
 
-var connectHandler mqtt_cli.OnConnectHandler = func(client mqtt_cli.Client) {
+var ConnectHandler mqtt_cli.OnConnectHandler = func(client mqtt_cli.Client) {
 	fmt.Println("Connected")
 }
 
-var connectLostHandler mqtt_cli.ConnectionLostHandler = func(client mqtt_cli.Client, err error) {
+var ConnectLostHandler mqtt_cli.ConnectionLostHandler = func(client mqtt_cli.Client, err error) {
 	fmt.Printf("Connect lost: %v", err)
 }
 
-func sub(client mqtt_cli.Client, topic string) {
-	token := client.Subscribe(topic, 1, nil)
+func (mq MqttInst) subscribeTopic(topic string) {
+	token := mq.client.Subscribe(topic, 1, nil)
 	token.Wait()
 	fmt.Printf("Subscribed to topic: %s", topic)
 	receiveCount := 0
@@ -43,30 +54,59 @@ func sub(client mqtt_cli.Client, topic string) {
 	}
 }
 
-func connect(broker string, port int) mqtt_cli.Client {
-	opts := mqtt_cli.NewClientOptions()
-	opts.AddBroker(fmt.Sprintf("tcp://%s:%d", broker, port))
-	opts.SetClientID("go_mqtt_client")
-	opts.SetUsername("emqx")
-	opts.SetPassword("public")
-	opts.SetDefaultPublishHandler(messagePubHandler)
-	opts.OnConnect = connectHandler
-	opts.OnConnectionLost = connectLostHandler
-	client := mqtt_cli.NewClient(opts)
-	if token := client.Connect(); token.Wait() && token.Error() != nil {
+func (mq *MqttInst) setDefaultOptions() {
+	mq.options.AddBroker(fmt.Sprintf("tcp://%s:%d", mq.broker, mq.port))
+	mq.options.SetClientID("go_mqtt_client")
+	mq.options.SetUsername("emqx")
+	mq.options.SetPassword("public")
+	mq.options.SetDefaultPublishHandler(messagePubHandler)
+	mq.options.OnConnect = ConnectHandler
+	mq.options.OnConnectionLost = ConnectLostHandler
+}
+
+func (mq *MqttInst) SetDefaultPublishHandler(messagePubHandler mqtt_cli.MessageHandler) {
+	mq.options.SetDefaultPublishHandler(messagePubHandler)
+	mq.options.AddBroker(fmt.Sprintf("tcp://%s:%d", mq.broker, mq.port))
+	mq.options.SetClientID("go_mqtt_client")
+	mq.options.SetUsername("emqx")
+	mq.options.SetPassword("public")
+	mq.options.OnConnect = ConnectHandler
+	mq.options.OnConnectionLost = ConnectLostHandler
+}
+
+func (mq *MqttInst) SetOnConnectHandler(ConnectHandler mqtt_cli.OnConnectHandler) {
+	mq.options.OnConnect = ConnectHandler
+}
+
+func (mq *MqttInst) SetOnConnectionLostHandler(ConnectHandler mqtt_cli.ConnectionLostHandler) {
+	mq.options.OnConnectionLost = ConnectLostHandler
+}
+
+func (mq *MqttInst) Connect() {
+	fmt.Println("mqtt/Connect")
+	fmt.Println("mqtt/Connect " + mq.broker + " ")
+	fmt.Println(mq.port)
+
+	mq.client = mqtt_cli.NewClient(mq.options)
+	if token := mq.client.Connect(); token.Wait() && token.Error() != nil {
 		fmt.Println("Error")
 		panic(token.Error())
 	}
-	return client
 }
 
-func New(broker string, port int) mqtt {
-	m := mqtt{client: connect(broker, port)}
-	return m
+// New creates a link to mqtt broker
+func (mq *MqttInst) New(broker string, port int) {
+	fmt.Println("mqtt/New")
+	mq.options = mqtt_cli.NewClientOptions()
+	//mq.setDefaultOptions()
+	mq.broker = broker
+	mq.port = port
+	//mq.Connect()
 }
 
-func (m mqtt) SubTopic(topic string) {
+// SubTopic subscribe to a topic
+func (mq *MqttInst) SubscribeTopic(topic string) {
 	fmt.Println("subtopic")
-	m.topics.PushBack(topic)
-	sub(m.client, topic)
+	mq.topics.PushBack(topic)
+	mq.subscribeTopic(topic)
 }

+ 163 - 0
internal/connectors/zigbee2mqtt/device.go

@@ -0,0 +1,163 @@
+package zigbee2mqtt
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+type hex int
+
+/* {
+	"ieee_address":"0x00158d00018255df",
+	"type":"Router",
+	"network_address":29159,
+	"supported":true,
+	"disabled": false,
+	"friendly_name":"my_plug",
+	"description":"this plug is in the kitchen",
+	"endpoints":{"1":{"bindings":[],"configured_reportings":[],"clusters":{"input":["genOnOff","genBasic"],"output":[]}}},
+	"definition":{
+		"model":"ZNCZ02LM",
+		"vendor":"Xiaomi",
+		"description":"Mi power plug ZigBee",
+		"options": [...], // see exposes/options below
+		"exposes": [...]  // see exposes/options below
+	},
+	"power_source":"Mains (single phase)",
+	"date_code":"02-28-2017",
+	"model_id":"lumi.plug",
+	"scenes": [{"id": 3, "name": "Chill scene"}],
+	"interviewing":false,
+	"interview_completed":true
+}, */
+
+/*
+	 "exposes": [{
+		"access": 1,
+		"description": "Remaining battery in %",
+		"name": "battery",
+		"property": "battery",
+		"type": "numeric",
+		"unit": "%",
+		"value_max": 100,
+		"value_min": 0
+	}
+*/
+type expose struct {
+	Access      int
+	Description string
+	Name        string
+	Property    string
+	Type        string
+	Unit        string
+	Value_max   int
+	Value_min   int
+}
+
+/*
+	 "target": {
+		"endpoint": 1,
+		"ieee_address": "0x00124b00229884d8",
+		"type": "endpoint"
+	}
+*/
+type target struct {
+	Endpoint     int
+	Ieee_address string
+	Type         string
+}
+
+/*
+	"bindings": [{
+		"cluster": "genPowerCfg",
+		"target": {
+			"endpoint": 1,
+			"ieee_address": "0x00124b00229884d8",
+			"type": "endpoint"
+		}
+	},
+*/
+type binding struct {
+	Cluster string
+	Target  target
+}
+
+/*
+	"clusters": {
+		"input": ["genBasic", "genPowerCfg", "genPollCtrl", "touchlink", "64768"],
+		"output": ["genIdentify", "genGroups", "genScenes", "genOnOff", "genLevelCtrl", "genOta", "lightingColorCtrl", "touchlink"]
+	},
+*/
+type clusters struct {
+	Input  []string
+	Output []string
+}
+
+/*
+"configured_reportings": [{
+	"attribute": "batteryVoltage",
+	"cluster": "genPowerCfg",
+	"maximum_report_interval": 62000,
+	"minimum_report_interval": 3600,
+	"reportable_change": 0
+}],
+*/
+
+type reportings struct {
+	Attribute               string
+	Cluster                 string
+	Maximum_report_interval int
+	Minimum_report_interval json.Number
+	Reportable_change       int
+}
+
+/*
+"scenes": []
+*/
+
+type endpoint struct {
+	Bindings              []binding
+	Configured_reportings []reportings
+	// Scenes: []
+	Scenes []string
+}
+
+type definition struct {
+	Description string
+	Exposes     []expose
+}
+
+type device struct {
+	Date_code           string
+	Disabled            bool
+	Friendly_name       string
+	Ieee_address        string
+	Interview_completed bool
+	Interviewing        bool
+	Manufacturer        string
+	Model_id            string
+	Network_address     int
+	Power_source        string
+	Software_build_id   string
+	Supported           bool
+	Type                string
+	Endpoints           map[string]endpoint
+	Clusters            clusters
+	Definition          definition
+}
+
+type Builder func([]byte)
+
+func Build(data []byte) []device {
+	var arr []device
+	err := json.Unmarshal(data, &arr)
+	fmt.Println("connectors/zigbee2mqtt/device/builder")
+	if err != nil {
+		panic(err)
+	}
+	fmt.Println(arr)
+	for _, s := range arr {
+		fmt.Println(s)
+	}
+	return arr
+}

+ 27 - 0
internal/connectors/zigbee2mqtt/zigbee2mqtt.go

@@ -0,0 +1,27 @@
+package zigbee2mqtt
+
+import (
+	"fmt"
+	"homectrl/internal/connectors/mqtt"
+
+	mqtt_cli "github.com/eclipse/paho.mqtt.golang"
+)
+
+type Zigbee2mqtt struct {
+	devices []device
+}
+
+var messagePubHandler mqtt_cli.MessageHandler = func(client mqtt_cli.Client, msg mqtt_cli.Message) {
+	fmt.Printf("Received message: %s from topic: %s\n", msg.Payload(), msg.Topic())
+	Build(msg.Payload())
+}
+
+func (z2m *Zigbee2mqtt) New(broker string, port int) {
+	fmt.Println("zigbee2mqtt/New")
+	var inst = &mqtt.MqttInst{}
+	inst.New(broker, port)
+	inst.SetDefaultPublishHandler(messagePubHandler)
+	inst.Connect()
+	fmt.Println("zigbee2mqtt/New2")
+	inst.SubscribeTopic("zigbee2mqtt/bridge/devices")
+}

+ 8 - 4
internal/devices/builder.go

@@ -8,13 +8,15 @@ import (
 
 type device struct {
 	Friendly_name string
+	Model_id      string
 }
 
-func to_devices(raw_device string) {
-	s2 := strings.Split(raw_device, "_")
+func toDevices(rawDevice string) {
+	s2 := strings.Split(rawDevice, "_")
 	fmt.Println(s2)
 }
 
+// Builder creates device
 func Builder(data []byte) {
 	var arr []device
 	err := json.Unmarshal(data, &arr)
@@ -22,12 +24,14 @@ func Builder(data []byte) {
 	if err != nil {
 		panic(err)
 	}
+	fmt.Println(arr)
 	for _, s := range arr {
-		fmt.Println(s.Friendly_name)
-		to_devices(s.Friendly_name)
+		fmt.Println(s.Model_id)
+		toDevices(s.Friendly_name)
 	}
 }
 
+// Builder2 creates device
 func Builder2(data string) {
 	var arr []device
 	err := json.Unmarshal([]byte(data), &arr)

+ 1 - 0
internal/devices/interaction.go

@@ -1,5 +1,6 @@
 package interaction
 
+// Interaction is currently not used
 type Interaction interface {
 	Location() string
 	LocationId() int

+ 7 - 2
internal/devices/light.go

@@ -8,22 +8,27 @@ type Light struct {
 	on       bool
 }
 
+// Location
 func (l Light) Location() string {
 	return l.location
 }
 
-func (l Light) Id() int {
+// ID
+func (l Light) ID() int {
 	return l.id
 }
 
+// Type
 func (Light) Type() string {
 	return "Light"
 }
 
+// String
 func (l Light) String() string {
-	return l.Type() + " " + strconv.Itoa(l.Id()) + " " + l.Location()
+	return l.Type() + " " + strconv.Itoa(l.ID()) + " " + l.Location()
 }
 
+// New
 func New(id int, location string) Light {
 	l := Light{id, location, false}
 	return l