From 443ba52ab3f49a28e8874ebcb5476e36c3defb4c Mon Sep 17 00:00:00 2001 From: xengineering Date: Thu, 26 Mar 2026 20:57:22 +0100 Subject: Add `Retain` attribute to MQTTMessage This allows the sending part of the code to decide about the retain flag. The MQTT go routine will set it accordingly. --- cache.go | 1 + mqtt.go | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cache.go b/cache.go index 4e0eddc..18ce0c9 100644 --- a/cache.go +++ b/cache.go @@ -43,6 +43,7 @@ func (c *Cache) Update(states States) { c.Tx <- MQTTMessage{ Topic: topic, Payload: payload, + Retain: true, } } c.States[id] = state diff --git a/mqtt.go b/mqtt.go index fd1cba1..acd0108 100644 --- a/mqtt.go +++ b/mqtt.go @@ -27,6 +27,7 @@ var ( type MQTTMessage struct { Topic string Payload []byte + Retain bool } type Route struct { @@ -60,6 +61,7 @@ func MQTTRun(config MQTTConfig, tx chan MQTTMessage, routes ...Route) { message := MQTTMessage{ Topic: strings.TrimPrefix(msg.Topic(), config.TopicPrefix + "/"), Payload: msg.Payload(), + Retain: msg.Retained(), } route.Destination <- message }) @@ -88,7 +90,7 @@ func MQTTRun(config MQTTConfig, tx chan MQTTMessage, routes ...Route) { for message := range tx { topic := fmt.Sprintf("%s/%s", config.TopicPrefix, message.Topic) - client.Publish(topic, QoS1, RETAINED, message.Payload) + client.Publish(topic, QoS1, message.Retain, message.Payload) } } -- cgit v1.3 From 0ec5e0034891075ca0c70c6d29e20442c8ddb46e Mon Sep 17 00:00:00 2001 From: xengineering Date: Thu, 26 Mar 2026 21:02:20 +0100 Subject: Add Shelly / TP-Link device discovery This announces Shelly and TP-Link devices with an empty MQTT message. This makes it possible that a client can display the available devices. --- README.md | 28 ++++++++++++++++++++++++++-- main.go | 4 ++-- shelly.go | 6 +++++- tplink.go | 6 +++++- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index cb3be93..e0555a0 100644 --- a/README.md +++ b/README.md @@ -118,9 +118,21 @@ For all terms not explained here see the [MQTT version 3.1.1 documentation][9]. - `open`: contact is open - `closed`: contact is closed +### `/cover/` + +- description: Discovery of Shelly 2PM Gen3 covers +- direction: Sia server to client +- Quality of Service: QoS 1 (at least once) +- retained: yes +- receives will message: no +- topic parameters: + - `id`: ID of the Shelly 2PM Gen3 cover +- payloads: + - `exists`: a device with the given ID exists + ### `/cover//movement` -- description: Allows control of Shelly 2PM Gen3 covers +- description: Control of Shelly 2PM Gen3 covers - direction: client to Sia server - Quality of Service: QoS 2 (exactly once) - retained: no @@ -132,9 +144,21 @@ For all terms not explained here see the [MQTT version 3.1.1 documentation][9]. - `retract`: cover decreases the covering surface - `stop`: cover stops current motion if given +### `/plug/` + +- description: Discovery of TP-Link HS100 Wi-Fi plugs +- direction: Sia server to client +- Quality of Service: QoS 1 (at least once) +- retained: yes +- receives will message: no +- topic parameters: + - `id`: ID of the TP-Link HS100 Wi-Fi plug +- payloads: + - `exists`: a device with the given ID exists + ### `/plug//action` -- description: Implements control of tp-link HS100 Wi-Fi plugs +- description: Control of TP-Link HS100 Wi-Fi plugs - direction: client to Sia server - Quality of Service: QoS 2 (exactly once) - retained: no diff --git a/main.go b/main.go index 3194359..ef59574 100644 --- a/main.go +++ b/main.go @@ -24,8 +24,8 @@ func main() { go MQTTRun(config.MQTT, tx, coverMovement, plugAction) go HomematicRun(config.Homematic, tx) - go ShellyRun(config.Shelly, coverMovement) - go TPLinkRun(config.TPLink, plugAction) + go ShellyRun(config.Shelly, tx, coverMovement) + go TPLinkRun(config.TPLink, tx, plugAction) Await(syscall.SIGTERM, syscall.SIGINT) } diff --git a/shelly.go b/shelly.go index 508b393..2fa2fa6 100644 --- a/shelly.go +++ b/shelly.go @@ -9,7 +9,11 @@ import ( "github.com/gorilla/websocket" ) -func ShellyRun(config ShellyConfigs, route Route) { +func ShellyRun(config ShellyConfigs, tx chan MQTTMessage, route Route) { + for _, shelly := range config { + tx <- MQTTMessage{fmt.Sprintf("cover/%s", shelly.ID), []byte("exists"), true} + } + for message := range route.Destination { ip, command, err := parseMessage(config, message) if err != nil { diff --git a/tplink.go b/tplink.go index 11d8ab1..a256e71 100644 --- a/tplink.go +++ b/tplink.go @@ -16,7 +16,11 @@ const ( TPLink_HS100_OFF = false ) -func TPLinkRun(config TPLinkConfigs, route Route) { +func TPLinkRun(config TPLinkConfigs, tx chan MQTTMessage, route Route) { + for _, tplink := range config { + tx <- MQTTMessage{fmt.Sprintf("plug/%s", tplink.ID), []byte("exists"), true} + } + for message := range route.Destination { ip, action, err := tplinkParseMessage(config, message) if err != nil { -- cgit v1.3