Skip to content
Snippets Groups Projects
Commit 00da01ec authored by Jonas Leder's avatar Jonas Leder
Browse files

Merge branch 'develop/mqtt-pair-api' into 'master'

Implement API Endpoint for MQTT pair

See merge request jonasled/firehouse-smokedetection!1
parents 8eafb373 1e6be0d6
Branches
No related tags found
1 merge request!1Implement API Endpoint for MQTT pair
Pipeline #58497 passed
......@@ -15,9 +15,24 @@
- arm64
stages:
- test
- build
- deploy
check-if-api-docs-updated:
stage: build
needs: []
image: golang:1.24.1
before_script:
- go install github.com/swaggo/swag/cmd/swag@latest
script:
- $GOPATH/bin/swag init
- |
if ! git diff --quiet; then
echo "The API docs needs to be updated first"
exit 1
fi
build-application-binary:
stage: build
needs: []
......
package cache
import "jonasled.dev/jonasled/firehouse-smokedetection/types"
var BridgeInfo types.BridgeInfo
......@@ -49,6 +49,123 @@ const docTemplate = `{
}
}
}
},
"/api/v1/mqtt/pair": {
"get": {
"description": "Returns the current pairing status",
"produces": [
"application/json"
],
"tags": [
"pairing"
],
"summary": "Pairing status",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/types.PairingStatusResponse"
}
}
}
},
"post": {
"description": "Enable MQTT pairing",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"pairing"
],
"summary": "Enable pairing",
"parameters": [
{
"description": "Pairing time",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/types.EnablePairing"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/types.GeneralResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/types.GeneralResponse"
}
}
}
},
"delete": {
"description": "Disable MQTT pairing",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"pairing"
],
"summary": "Disable pairing",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/types.GeneralResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/types.GeneralResponse"
}
}
}
}
}
},
"definitions": {
"types.EnablePairing": {
"type": "object",
"properties": {
"time": {
"type": "integer"
}
}
},
"types.GeneralResponse": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"success": {
"type": "boolean"
}
}
},
"types.PairingStatusResponse": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"end": {
"type": "string"
}
}
}
}
}`
......
......@@ -40,6 +40,123 @@
}
}
}
},
"/api/v1/mqtt/pair": {
"get": {
"description": "Returns the current pairing status",
"produces": [
"application/json"
],
"tags": [
"pairing"
],
"summary": "Pairing status",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/types.PairingStatusResponse"
}
}
}
},
"post": {
"description": "Enable MQTT pairing",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"pairing"
],
"summary": "Enable pairing",
"parameters": [
{
"description": "Pairing time",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/types.EnablePairing"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/types.GeneralResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/types.GeneralResponse"
}
}
}
},
"delete": {
"description": "Disable MQTT pairing",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"pairing"
],
"summary": "Disable pairing",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/types.GeneralResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/types.GeneralResponse"
}
}
}
}
}
},
"definitions": {
"types.EnablePairing": {
"type": "object",
"properties": {
"time": {
"type": "integer"
}
}
},
"types.GeneralResponse": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"success": {
"type": "boolean"
}
}
},
"types.PairingStatusResponse": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"end": {
"type": "string"
}
}
}
}
}
\ No newline at end of file
definitions:
types.EnablePairing:
properties:
time:
type: integer
type: object
types.GeneralResponse:
properties:
message:
type: string
success:
type: boolean
type: object
types.PairingStatusResponse:
properties:
enabled:
type: boolean
end:
type: string
type: object
info:
contact:
name: Jonas Leder
......@@ -26,4 +46,60 @@ paths:
summary: Liveness Probe
tags:
- health
/api/v1/mqtt/pair:
delete:
consumes:
- application/json
description: Disable MQTT pairing
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/types.GeneralResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/types.GeneralResponse'
summary: Disable pairing
tags:
- pairing
get:
description: Returns the current pairing status
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/types.PairingStatusResponse'
summary: Pairing status
tags:
- pairing
post:
consumes:
- application/json
description: Enable MQTT pairing
parameters:
- description: Pairing time
in: body
name: request
required: true
schema:
$ref: '#/definitions/types.EnablePairing'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/types.GeneralResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/types.GeneralResponse'
summary: Enable pairing
tags:
- pairing
swagger: "2.0"
package mqttclient
import (
"encoding/json"
"errors"
"fmt"
"os"
"time"
mqtt "github.com/eclipse/paho.mqtt.golang"
"jonasled.dev/jonasled/firehouse-smokedetection/types"
"jonasled.dev/jonasled/go-libs/config"
"jonasled.dev/jonasled/go-libs/helper"
"jonasled.dev/jonasled/go-libs/log"
)
var client mqtt.Client
var z2mBaseTopic string
var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) {
log.Log.Info("Successfully connected to MQTT broker")
client.Subscribe(config.GetOrDefault("Z2M_BASETOPIC", "zigbee2mqtt")+"/+", 1, messagePubHandlerZ2M)
client.Subscribe(z2mBaseTopic+"/+", 1, messagePubHandlerZ2M)
client.Subscribe(z2mBaseTopic+"/bridge/info", 1, messagePubHandlerBridgeinfo)
}
var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) {
......@@ -28,6 +33,7 @@ var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err
}
func Init() {
z2mBaseTopic = config.GetOrDefault("Z2M_BASETOPIC", "zigbee2mqtt")
opts := mqtt.NewClientOptions()
if config.GetOrDefault("MQTT_SERVER", "embedded") == "embedded" {
opts.AddBroker(fmt.Sprintf("tcp://%s:%s", "localhost", config.GetOrDefault("MQTT_PORT", "1883")))
......@@ -51,3 +57,16 @@ func Init() {
func Connected() bool {
return client.IsConnected()
}
func EnablePairing(pairingTime int) error {
pairingTimeStruct := types.EnablePairing{
Time: pairingTime,
}
if !pairingTimeStruct.Check() {
return errors.New("Pairing time needs to be between 0 and 254")
}
pairingTimeStructjson, _ := json.Marshal(&pairingTimeStruct)
client.Publish(z2mBaseTopic+"/bridge/request/permit_join", 0, false, pairingTimeStructjson)
return nil
}
......@@ -4,6 +4,7 @@ import (
"encoding/json"
mqtt "github.com/eclipse/paho.mqtt.golang"
"jonasled.dev/jonasled/firehouse-smokedetection/cache"
"jonasled.dev/jonasled/firehouse-smokedetection/types"
"jonasled.dev/jonasled/go-libs/log"
)
......@@ -33,3 +34,14 @@ var messagePubHandlerZ2M mqtt.MessageHandler = func(client mqtt.Client, msg mqtt
}
}
var messagePubHandlerBridgeinfo mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
log.Log.Debugf("Received bridge info message for Z2M: %s from topic: %s\n", msg.Payload(), msg.Topic())
log.Log.Info("Received new bridge info MQTT message")
err := json.Unmarshal(msg.Payload(), &cache.BridgeInfo)
if err != nil {
log.Log.Error("Failed parsing MQTT messgae as json: ", err.Error())
}
}
package types
type BridgeInfo struct {
Version string `json:"version"`
PairingEnabled bool `json:"permit_join"`
PairingEnd int `json:"permit_join_end"`
RestartRequired bool `json:"restart_required"`
}
package types
type EnablePairing struct {
Time int `json:"time"`
}
func (e EnablePairing) Check() bool {
return e.Time >= 0 && e.Time <= 254
}
package types
type GeneralResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
}
package types
import "time"
type PairingStatusResponse struct {
PairingEnabled bool `json:"enabled"`
PairingEnd time.Time `json:"end"`
}
package mqtt
import "github.com/gin-gonic/gin"
func Init(r *gin.Engine) {
pair := r.Group("/api/v1/mqtt/pair")
{
pair.GET("", pairingStatus)
pair.POST("", startPairing)
pair.DELETE("", disablepairing)
}
}
package mqtt
import (
"encoding/json"
"io"
"net/http"
"time"
"github.com/gin-gonic/gin"
"jonasled.dev/jonasled/firehouse-smokedetection/cache"
"jonasled.dev/jonasled/firehouse-smokedetection/mqttclient"
"jonasled.dev/jonasled/firehouse-smokedetection/types"
"jonasled.dev/jonasled/go-libs/log"
)
// @Summary Enable pairing
// @Description Enable MQTT pairing
// @Tags pairing
// @Accept json
// @Produce json
// @Param request body types.EnablePairing true "Pairing time"
// @Success 200 {object} types.GeneralResponse
// @Failure 400 {object} types.GeneralResponse
// @router /api/v1/mqtt/pair [post]
func startPairing(ctx *gin.Context) {
log.Log.Info("Enabling pairing")
var messageBody types.EnablePairing
rawMessageBody, _ := io.ReadAll(ctx.Request.Body)
err := json.Unmarshal(rawMessageBody, &messageBody)
if err != nil {
ctx.JSON(http.StatusBadRequest, types.GeneralResponse{
Success: false,
Message: err.Error(),
})
return
}
if !messageBody.Check() {
ctx.JSON(http.StatusBadRequest, types.GeneralResponse{
Success: false,
Message: "Time has to be between 0 and 254",
})
return
}
mqttclient.EnablePairing(messageBody.Time)
ctx.JSON(http.StatusOK, types.GeneralResponse{
Success: true,
Message: "",
})
}
// @Summary Disable pairing
// @Description Disable MQTT pairing
// @Tags pairing
// @Accept json
// @Produce json
// @Success 200 {object} types.GeneralResponse
// @Failure 400 {object} types.GeneralResponse
// @router /api/v1/mqtt/pair [delete]
func disablepairing(ctx *gin.Context) {
log.Log.Info("Disable pairing")
mqttclient.EnablePairing(0)
ctx.JSON(http.StatusOK, types.GeneralResponse{
Success: true,
Message: "",
})
}
// @Summary Pairing status
// @Description Returns the current pairing status
// @Tags pairing
// @Produce json
// @Success 200 {object} types.PairingStatusResponse
// @router /api/v1/mqtt/pair [get]
func pairingStatus(ctx *gin.Context) {
ctx.JSON(http.StatusOK, types.PairingStatusResponse{
PairingEnabled: cache.BridgeInfo.PairingEnabled,
PairingEnd: time.Unix(int64(cache.BridgeInfo.PairingEnd)/1000, 0),
})
}
......@@ -3,6 +3,7 @@ package webserver
import (
"github.com/gin-gonic/gin"
ginlogrus "github.com/toorop/gin-logrus"
"jonasled.dev/jonasled/firehouse-smokedetection/webserver/mqtt"
"jonasled.dev/jonasled/go-libs/log"
)
......@@ -14,6 +15,7 @@ func Init() {
R.Use(ginlogrus.Logger(log.Log), gin.Recovery())
initGeneralRoutes()
initSwaggerRoutes()
mqtt.Init(R)
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment