Add support for defining the MQTT topic prefix that Zigbee2MQTT uses.

This commit is contained in:
2026-01-24 19:02:45 +00:00
parent bc4c53b5be
commit b0a8169951
4 changed files with 26 additions and 12 deletions

View File

@@ -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 <https://www.gnu.org/licenses/>.

View File

@@ -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

View File

@@ -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)

View File

@@ -11,4 +11,7 @@
"Server": "tcp://localhost:1883",
"Username": "hapz2m",
"Password": "my_s3cr3t_PasSw0rd"
// Topic prefix of the Z2M instance (optional)
// "TopicPrefix": "zigbee2mqtt/"
}