From b0a81699514db30b35f71c115e8e5c11bac9aec6 Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Sat, 24 Jan 2026 19:02:45 +0000 Subject: [PATCH] Add support for defining the MQTT topic prefix that Zigbee2MQTT uses. --- README.md | 4 +++- bridge.go | 17 ++++++++--------- cmd/hapz2m/main.go | 14 ++++++++++++-- hapz2m.conf | 3 +++ 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index dceac06..9c953ea 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,9 @@ The MQTT broker that Zigbee2MQTT connects to is specified using the following: - Server - Username - Password +- TopicPrefix + +TopicPrefix is an optional value, and will default to `zigbee2mqtt/` if undefined. This should mirror your Zigbee2MQTT `base_topic` value with a trailing slash. Additionally, these options control networking aspects for the bridge: @@ -77,4 +80,3 @@ PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . - diff --git a/bridge.go b/bridge.go index f79f14a..ae48af4 100644 --- a/bridge.go +++ b/bridge.go @@ -33,8 +33,6 @@ var ( ) const ( - MQTT_TOPIC_PREFIX = "zigbee2mqtt/" - // Store name for persisting zigbee2mqtt state Z2M_STATE_STORE = "z2m_state" @@ -53,9 +51,10 @@ const BRIDGE_DEVMODE = false type Bridge struct { // MQTT broker and credentials - Server string - Username string - Password string + Server string + Username string + Password string + TopicPrefix string // address and interfaces to bind to ListenAddr string @@ -252,7 +251,7 @@ func (br *Bridge) ConnectMQTT() error { opts.SetOnConnectHandler(func(c mqtt.Client) { log.Printf("connected to MQTT broker") - tok := c.Subscribe(MQTT_TOPIC_PREFIX+"#", 0, br.handleMqttMessage) + tok := c.Subscribe(br.TopicPrefix+"#", 0, br.handleMqttMessage) if tok.Wait() && tok.Error() != nil { log.Fatal(tok.Error()) } @@ -385,8 +384,8 @@ func (br *Bridge) handleMqttMessage(_ mqtt.Client, msg mqtt.Message) { topic, payload, recvTime := msg.Topic(), msg.Payload(), time.Now() // check for topic prefix and remove it - l := len(MQTT_TOPIC_PREFIX) - if len(topic) <= l || topic[:l] != MQTT_TOPIC_PREFIX { + l := len(br.TopicPrefix) + if len(topic) <= l || topic[:l] != br.TopicPrefix { return } topic = topic[l:] @@ -643,7 +642,7 @@ func cmpFloat64Numeric(f, n any) bool { // Publish to the MQTT broker for the specific device func (br *Bridge) PublishState(dev *Device, payload map[string]any) error { - topic := MQTT_TOPIC_PREFIX + dev.FriendlyName + "/set" + topic := br.TopicPrefix + dev.FriendlyName + "/set" jsonPayload, err := json.Marshal(payload) if err != nil { return err diff --git a/cmd/hapz2m/main.go b/cmd/hapz2m/main.go index da5e754..a628970 100644 --- a/cmd/hapz2m/main.go +++ b/cmd/hapz2m/main.go @@ -2,6 +2,7 @@ package main import ( "hapz2m" + "strings" "context" "encoding/json" @@ -40,7 +41,7 @@ type config struct { Pin string - Server, Username, Password string + Server, Username, Password, TopicPrefix string } func parseConfig(fname string) (cfg *config, err error) { @@ -52,7 +53,9 @@ func parseConfig(fname string) (cfg *config, err error) { // remove line comments, json.Unmarshal can't parse them cfgStr = CONFIG_COMMENTS_RE.ReplaceAllLiteral(cfgStr, []byte{}) - cfg = &config{} + cfg = &config{ + TopicPrefix: "zigbee2mqtt/", // use default Z2M prefix if not defined in the configuration + } if err = json.Unmarshal(cfgStr, cfg); err != nil { return } @@ -103,6 +106,7 @@ func main() { br.Server = cfg.Server br.Username = cfg.Username br.Password = cfg.Password + br.TopicPrefix = cfg.TopicPrefix br.DebugMode = *debugMode br.QuietMode = *quietMode @@ -123,6 +127,12 @@ func main() { br.ListenAddr = cfg.ListenAddr } + // Validate that TopicPrefix is valid (must end in a /) + if cfg.TopicPrefix != "" && !strings.HasSuffix(cfg.TopicPrefix, "/") { + log.Fatalf("invalid TopicPrefix: must end with a /") + } + br.TopicPrefix = cfg.TopicPrefix + br.Interfaces = cfg.Interfaces log.Println(versionStr) diff --git a/hapz2m.conf b/hapz2m.conf index ba8c4b6..c62c5a7 100644 --- a/hapz2m.conf +++ b/hapz2m.conf @@ -11,4 +11,7 @@ "Server": "tcp://localhost:1883", "Username": "hapz2m", "Password": "my_s3cr3t_PasSw0rd" + + // Topic prefix of the Z2M instance (optional) + // "TopicPrefix": "zigbee2mqtt/" }