diff --git a/.gitignore b/.gitignore index 1dbcfe83d7e32a2d92dfb1f8bfcd81a91b5a27bc..e1b97795eb689949283a4511d2f04f7617cd97e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ .env -log/ +/log/ go.work go.work.sum -database.db -plugins/ \ No newline at end of file +database.db* +/plugins/ +/backend +/backend.exe \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c19d2cca03f729e14172ed1a5ffb74d3f3eb60db..43fc472915a8640e7ca8fae8e74ac3b813c0368e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -26,7 +26,7 @@ check-if-api-docs-updated: before_script: - go install github.com/swaggo/swag/cmd/swag@latest script: - - $GOPATH/bin/swag init + - $GOPATH/bin/swag init --parseDependency - | if ! git diff --quiet; then echo "The API docs needs to be updated first" diff --git a/Readme.md b/Readme.md index a294be903d82a6d3d380664f75e5da59398f74cc..3710da44533210361d939e642325c5553ee07e03 100644 --- a/Readme.md +++ b/Readme.md @@ -16,5 +16,6 @@ The configuration can either be done through environment variables or a file cal | EXIT_ON_DISCONNECT | `false` | Exit the application on MQTT disconnect | | Z2M_BASETOPIC | `zigbee2mqtt` | Base topic for ZigBee2MQTT | | DB_DRIVER | `sqlite` | Database driver, currently ony sqlite is supported | -| DB_NAME | `./database.db` | Name of the databse, for sqlite path to the DB file -| EXECUTE_MIGRATIONS | `true` | Execute database migrations on start | \ No newline at end of file +| DB_NAME | `./database.db` | Name of the databse, for sqlite path to the DB file | +| EXECUTE_MIGRATIONS | `true` | Execute database migrations on start | +| PLUGIN_DIR | `./plugins` | Where to search for plugins | diff --git a/alarm/alarm.go b/alarm/alarm.go new file mode 100644 index 0000000000000000000000000000000000000000..627a201ace179c02b58f4892992a6263f4ce54ca --- /dev/null +++ b/alarm/alarm.go @@ -0,0 +1,31 @@ +package alarm + +import ( + "slices" + + "jonasled.dev/firehouse-smokedetection/backend/database/tables" + "jonasled.dev/firehouse-smokedetection/backend/types" + plugintypes "jonasled.dev/firehouse-smokedetection/plugin-interface/types" + "jonasled.dev/jonasled/go-libs/log" +) + +func Alarm(mqttMessage types.Z2MSmoke, smokeDetector tables.SmokeDetector) { + log.Log.Infof("Sensor %s reported smoke", smokeDetector.Name) + if smokeDetector.AlarmPlugin == "" { + return + } + if !slices.Contains(GetAllPlugins(), smokeDetector.AlarmPlugin) { + log.Log.Errorf("Plugin %s for smoke sensor %s not found, can't forward alarm", smokeDetector.AlarmPlugin, smokeDetector.Name) + return + } + err := Plugins[smokeDetector.AlarmPlugin].Alarm(plugintypes.AlarmSmokeSensor{ + Smoke: mqttMessage.Smoke, + Test: mqttMessage.Test, + Name: smokeDetector.Name, + ZigBeeName: smokeDetector.ZigBeeName, + Tamper: mqttMessage.Tamper, + }, smokeDetector.AlarmPluginConfigId) + if err != nil { + log.Log.Error("Failed executing smoke alarm: ", err.Error()) + } +} diff --git a/alarm/getPlugins.go b/alarm/getPlugins.go new file mode 100644 index 0000000000000000000000000000000000000000..865295a08c9d28c79d49c74a92e0e5d4748ba2ec --- /dev/null +++ b/alarm/getPlugins.go @@ -0,0 +1,9 @@ +package alarm + +func GetAllPlugins() []string { + var result []string + for key := range Plugins { + result = append(result, key) + } + return result +} diff --git a/alarm/init.go b/alarm/init.go new file mode 100644 index 0000000000000000000000000000000000000000..a4db94c13574ee7698fac89887d15f91b15b6bdb --- /dev/null +++ b/alarm/init.go @@ -0,0 +1,54 @@ +package alarm + +import ( + "os" + "path/filepath" + "plugin" + "strings" + + plugininterface "jonasled.dev/firehouse-smokedetection/plugin-interface" + "jonasled.dev/jonasled/go-libs/config" + "jonasled.dev/jonasled/go-libs/log" +) + +var Plugins map[string]plugininterface.Plugin + +func Init() { + Plugins = make(map[string]plugininterface.Plugin) + err := filepath.Walk(config.GetOrDefault("PLUGIN_DIR", "./plugins"), func(path string, info os.FileInfo, err error) error { + if err != nil { + log.Log.Fatal("Failed reading plugin dir: ", err.Error()) + } + + if !info.IsDir() && strings.HasSuffix(info.Name(), ".so") { + log.Log.Debug("Found plugin file at ", path) + plg, err := plugin.Open(path) + if err != nil { + log.Log.Fatalf("Failed reading Plugin %s: %s", path, err.Error()) + } + v, err := plg.Lookup("Plugin") + if err != nil { + log.Log.Fatalf("Plugin %s doesn't export Plugin var", path) + } + plugin, ok := v.(plugininterface.Plugin) + if !ok { + log.Log.Fatalf("Failed parsing plugin %s as Plugin Interface", path) + } + + pluginConfig := PluginConfig{ + PluginName: plugin.GetMetadata().Name, + } + + plugin.Setup(&pluginConfig) + + log.Log.Infof("Initialized Plugin %s (v%s)", plugin.GetMetadata().Name, plugin.GetMetadata().Version) + + Plugins[plugin.GetMetadata().Name] = plugin + } + return nil + }) + + if err != nil { + log.Log.Fatal("Failed reading plugin dir: ", err.Error()) + } +} diff --git a/alarm/pluginConfig.go b/alarm/pluginConfig.go new file mode 100644 index 0000000000000000000000000000000000000000..76ff2c5eab565bdada6ecacaa563c212056ec65c --- /dev/null +++ b/alarm/pluginConfig.go @@ -0,0 +1,33 @@ +package alarm + +import ( + "jonasled.dev/firehouse-smokedetection/backend/database" + "jonasled.dev/firehouse-smokedetection/plugin-interface/types" +) + +type PluginConfig struct { + PluginName string +} + +func (p *PluginConfig) GetConfig(id uint) types.AlarmConfig { + var alarmConfig types.AlarmConfig + database.Db.Where(types.AlarmConfig{ID: id, Plugin: p.PluginName}).First(&alarmConfig) + + return alarmConfig +} + +func (p *PluginConfig) SetConfig(config types.AlarmConfig) error { + config.Plugin = p.PluginName + return database.Db.Save(&config).Error +} + +func (p *PluginConfig) DeleteConfig(config types.AlarmConfig) error { + config.Plugin = p.PluginName + return database.Db.Delete(&config).Error +} + +func (p *PluginConfig) ListConfig(filter types.AlarmConfig) []types.AlarmConfig { + var matchedConfigs []types.AlarmConfig + database.Db.Find(&matchedConfigs, &filter) + return matchedConfigs +} diff --git a/database/gorm.go b/database/gorm.go index 4a6517a9fd2e44cd88ef481e48c05507f548e888..e377bd3a94440cc60c865f4e4653379f437e5a8d 100644 --- a/database/gorm.go +++ b/database/gorm.go @@ -9,6 +9,7 @@ import ( "gorm.io/gorm" "gorm.io/gorm/logger" "jonasled.dev/firehouse-smokedetection/backend/database/tables" + plugintypes "jonasled.dev/firehouse-smokedetection/plugin-interface/types" "jonasled.dev/jonasled/go-libs/config" "jonasled.dev/jonasled/go-libs/log" ) @@ -40,6 +41,6 @@ func Init() { if config.GetOrDefault("EXECUTE_MIGRATIONS", "true") == "true" { log.Log.Info("Executing database migrations") - Db.AutoMigrate(&tables.SmokeDetector{}) + Db.AutoMigrate(&tables.SmokeDetector{}, &plugintypes.AlarmConfig{}) } } diff --git a/database/tables/smokeDetector.go b/database/tables/smokeDetector.go index e0b04603dee2ae4f713ba5b706bf523e38b5fd85..45de9640b7ddca8251f6cc9c4b118b498b3f4cfa 100644 --- a/database/tables/smokeDetector.go +++ b/database/tables/smokeDetector.go @@ -11,4 +11,6 @@ type SmokeDetector struct { ZigBeeName string `gorm:"unique"` Name string Battery int + AlarmPlugin string + AlarmPluginConfigId uint } diff --git a/docs/docs.go b/docs/docs.go index bd122488b16e15281a99029311cbe95f6c6caf88..fc1aade0c92ff0008ff6af3e2b5a44b33fc01131 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -135,6 +135,170 @@ const docTemplate = `{ } } }, + "/api/v1/plugins": { + "get": { + "description": "List all registered plugins", + "produces": [ + "application/json" + ], + "tags": [ + "plugins" + ], + "summary": "List plugins", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/types.PluginMetadata" + } + } + } + } + } + }, + "/api/v1/plugins/{plugin}": { + "post": { + "description": "Creates a new config element for the given plugin", + "produces": [ + "application/json" + ], + "tags": [ + "plugins" + ], + "summary": "Create Config", + "parameters": [ + { + "description": "New config entry for plugin", + "name": "config", + "in": "body", + "required": true, + "schema": {} + }, + { + "type": "string", + "description": "Plugin name", + "name": "plugin", + "in": "path", + "required": true + } + ], + "responses": {} + } + }, + "/api/v1/plugins/{plugin}/schema": { + "get": { + "description": "Returns the plugins config JSON schema", + "produces": [ + "application/json" + ], + "tags": [ + "plugins" + ], + "summary": "Plugin config schema", + "parameters": [ + { + "type": "string", + "description": "Plugin name", + "name": "plugin", + "in": "path", + "required": true + } + ], + "responses": {} + } + }, + "/api/v1/plugins/{plugin}/{configId}": { + "get": { + "description": "Get a existing config", + "produces": [ + "application/json" + ], + "tags": [ + "plugins" + ], + "summary": "Get Config", + "parameters": [ + { + "type": "string", + "description": "Plugin name", + "name": "plugin", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the config to update", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": {} + }, + "put": { + "description": "Updates a existing config element for the given plugin", + "produces": [ + "application/json" + ], + "tags": [ + "plugins" + ], + "summary": "Update Config", + "parameters": [ + { + "description": "New config entry for plugin", + "name": "config", + "in": "body", + "required": true, + "schema": {} + }, + { + "type": "string", + "description": "Plugin name", + "name": "plugin", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the config to update", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": {} + }, + "delete": { + "description": "Delete a existing config", + "produces": [ + "application/json" + ], + "tags": [ + "plugins" + ], + "summary": "Delete Config", + "parameters": [ + { + "type": "string", + "description": "Plugin name", + "name": "plugin", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the config to update", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": {} + } + }, "/api/v1/smokedetectors": { "get": { "description": "List all known smoke detectors", @@ -291,6 +455,12 @@ const docTemplate = `{ "tables.SmokeDetector": { "type": "object", "properties": { + "alarmPlugin": { + "type": "string" + }, + "alarmPluginConfigId": { + "type": "integer" + }, "battery": { "type": "integer" }, @@ -341,9 +511,26 @@ const docTemplate = `{ } } }, + "types.PluginMetadata": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, "types.UpdateSmokeDetectorRequest": { "type": "object", "properties": { + "alarmPlugin": { + "type": "string" + }, + "alarmPluginConfigId": { + "type": "integer" + }, "name": { "type": "string" } diff --git a/docs/swagger.json b/docs/swagger.json index 266c47510140916b77892fbcf4dc94bb5216ca3e..51044b5dfbb6f3c9cf5e2a11b02efd0b52a201fc 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -126,6 +126,170 @@ } } }, + "/api/v1/plugins": { + "get": { + "description": "List all registered plugins", + "produces": [ + "application/json" + ], + "tags": [ + "plugins" + ], + "summary": "List plugins", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/types.PluginMetadata" + } + } + } + } + } + }, + "/api/v1/plugins/{plugin}": { + "post": { + "description": "Creates a new config element for the given plugin", + "produces": [ + "application/json" + ], + "tags": [ + "plugins" + ], + "summary": "Create Config", + "parameters": [ + { + "description": "New config entry for plugin", + "name": "config", + "in": "body", + "required": true, + "schema": {} + }, + { + "type": "string", + "description": "Plugin name", + "name": "plugin", + "in": "path", + "required": true + } + ], + "responses": {} + } + }, + "/api/v1/plugins/{plugin}/schema": { + "get": { + "description": "Returns the plugins config JSON schema", + "produces": [ + "application/json" + ], + "tags": [ + "plugins" + ], + "summary": "Plugin config schema", + "parameters": [ + { + "type": "string", + "description": "Plugin name", + "name": "plugin", + "in": "path", + "required": true + } + ], + "responses": {} + } + }, + "/api/v1/plugins/{plugin}/{configId}": { + "get": { + "description": "Get a existing config", + "produces": [ + "application/json" + ], + "tags": [ + "plugins" + ], + "summary": "Get Config", + "parameters": [ + { + "type": "string", + "description": "Plugin name", + "name": "plugin", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the config to update", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": {} + }, + "put": { + "description": "Updates a existing config element for the given plugin", + "produces": [ + "application/json" + ], + "tags": [ + "plugins" + ], + "summary": "Update Config", + "parameters": [ + { + "description": "New config entry for plugin", + "name": "config", + "in": "body", + "required": true, + "schema": {} + }, + { + "type": "string", + "description": "Plugin name", + "name": "plugin", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the config to update", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": {} + }, + "delete": { + "description": "Delete a existing config", + "produces": [ + "application/json" + ], + "tags": [ + "plugins" + ], + "summary": "Delete Config", + "parameters": [ + { + "type": "string", + "description": "Plugin name", + "name": "plugin", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "ID of the config to update", + "name": "configId", + "in": "path", + "required": true + } + ], + "responses": {} + } + }, "/api/v1/smokedetectors": { "get": { "description": "List all known smoke detectors", @@ -282,6 +446,12 @@ "tables.SmokeDetector": { "type": "object", "properties": { + "alarmPlugin": { + "type": "string" + }, + "alarmPluginConfigId": { + "type": "integer" + }, "battery": { "type": "integer" }, @@ -332,9 +502,26 @@ } } }, + "types.PluginMetadata": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, "types.UpdateSmokeDetectorRequest": { "type": "object", "properties": { + "alarmPlugin": { + "type": "string" + }, + "alarmPluginConfigId": { + "type": "integer" + }, "name": { "type": "string" } diff --git a/docs/swagger.yaml b/docs/swagger.yaml index f2141cdae725c3dc1f7ae489307b85120e2f3507..8e1f810995f183d4a4a46b532b440a896e301ac4 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,6 +1,10 @@ definitions: tables.SmokeDetector: properties: + alarmPlugin: + type: string + alarmPluginConfigId: + type: integer battery: type: integer createdAt: @@ -33,8 +37,19 @@ definitions: end: type: string type: object + types.PluginMetadata: + properties: + name: + type: string + version: + type: string + type: object types.UpdateSmokeDetectorRequest: properties: + alarmPlugin: + type: string + alarmPluginConfigId: + type: integer name: type: string type: object @@ -122,6 +137,119 @@ paths: summary: Enable pairing tags: - pairing + /api/v1/plugins: + get: + description: List all registered plugins + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/types.PluginMetadata' + type: array + summary: List plugins + tags: + - plugins + /api/v1/plugins/{plugin}: + post: + description: Creates a new config element for the given plugin + parameters: + - description: New config entry for plugin + in: body + name: config + required: true + schema: {} + - description: Plugin name + in: path + name: plugin + required: true + type: string + produces: + - application/json + responses: {} + summary: Create Config + tags: + - plugins + /api/v1/plugins/{plugin}/{configId}: + delete: + description: Delete a existing config + parameters: + - description: Plugin name + in: path + name: plugin + required: true + type: string + - description: ID of the config to update + in: path + name: configId + required: true + type: integer + produces: + - application/json + responses: {} + summary: Delete Config + tags: + - plugins + get: + description: Get a existing config + parameters: + - description: Plugin name + in: path + name: plugin + required: true + type: string + - description: ID of the config to update + in: path + name: configId + required: true + type: integer + produces: + - application/json + responses: {} + summary: Get Config + tags: + - plugins + put: + description: Updates a existing config element for the given plugin + parameters: + - description: New config entry for plugin + in: body + name: config + required: true + schema: {} + - description: Plugin name + in: path + name: plugin + required: true + type: string + - description: ID of the config to update + in: path + name: configId + required: true + type: integer + produces: + - application/json + responses: {} + summary: Update Config + tags: + - plugins + /api/v1/plugins/{plugin}/schema: + get: + description: Returns the plugins config JSON schema + parameters: + - description: Plugin name + in: path + name: plugin + required: true + type: string + produces: + - application/json + responses: {} + summary: Plugin config schema + tags: + - plugins /api/v1/smokedetectors: get: description: List all known smoke detectors diff --git a/go.mod b/go.mod index 55de059b51d9b3f468e9763c6f98b16213335d77..a81ac27e1660a778201b864f9bbc8d64cd1934d7 100644 --- a/go.mod +++ b/go.mod @@ -6,16 +6,17 @@ require ( github.com/eclipse/paho.mqtt.golang v1.5.0 github.com/gin-gonic/gin v1.10.0 github.com/glebarez/sqlite v1.11.0 + github.com/invopop/jsonschema v0.13.0 github.com/joho/godotenv v1.5.1 github.com/nekomeowww/gorm-logger-logrus v1.0.8 github.com/sirupsen/logrus v1.9.3 github.com/swaggo/files v1.0.1 - github.com/swaggo/files/v2 v2.0.2 github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/swag v1.16.4 github.com/toorop/gin-logrus v0.0.0-20210225092905-2c785434f26f github.com/wind-c/comqtt/v2 v2.6.0 gorm.io/gorm v1.25.12 + jonasled.dev/firehouse-smokedetection/plugin-interface v0.0.14 jonasled.dev/jonasled/go-libs v0.0.3 ) @@ -23,6 +24,8 @@ require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/buger/jsonparser v1.1.1 // indirect github.com/bytedance/sonic v1.11.6 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/cloudwego/base64x v0.1.4 // indirect @@ -47,7 +50,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/mailru/easyjson v0.7.6 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -56,12 +59,13 @@ require ( github.com/rs/xid v1.5.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect golang.org/x/arch v0.8.0 // indirect golang.org/x/crypto v0.25.0 // indirect golang.org/x/net v0.27.0 // indirect - golang.org/x/sync v0.7.0 // indirect + golang.org/x/sync v0.12.0 // indirect golang.org/x/sys v0.22.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/text v0.23.0 // indirect golang.org/x/tools v0.23.0 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect diff --git a/go.sum b/go.sum index 31ecab3477b10045a68ff0d3cbc35665dd639560..77de2d78e8339ffebce8eb16e32a7e4248e0321e 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,10 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= @@ -61,6 +65,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= +github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= @@ -86,8 +92,9 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -125,7 +132,6 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= -github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0= github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A= @@ -138,6 +144,8 @@ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65E github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/wind-c/comqtt/v2 v2.6.0 h1:huLdOwYDrwMTrEwH7+mSs1GftHZ/tDqJw8nOz3iX7kc= github.com/wind-c/comqtt/v2 v2.6.0/go.mod h1:6O4VilBrTQ/cNIcmIgNdMLCK9DTiLRxkal00t0DLN64= +github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= +github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= @@ -158,8 +166,8 @@ golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -179,8 +187,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -204,8 +212,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= -jonasled.dev/jonasled/go-libs v0.0.2 h1:QJE0jy9SH8flqAJIJHMG6NE3VPZurCvvMGf5xN0C+Xc= -jonasled.dev/jonasled/go-libs v0.0.2/go.mod h1:E8Sev4obR0x6y7x0HaIN1tImeLnVJgwJXt++jzTwiFM= +jonasled.dev/firehouse-smokedetection/plugin-interface v0.0.14 h1:3HxNjEiZbQuIPSxdlnffKIqnYsjgtvgerN/4r4WOjmk= +jonasled.dev/firehouse-smokedetection/plugin-interface v0.0.14/go.mod h1:7OmpERugGg+B1OKE5R2glhdbbF2XmYP0OTfbdNNBm40= jonasled.dev/jonasled/go-libs v0.0.3 h1:2Tg1pYJMufBMPkyYVVAvsUJe/R+TRRtRVW9MCbc24bM= jonasled.dev/jonasled/go-libs v0.0.3/go.mod h1:E8Sev4obR0x6y7x0HaIN1tImeLnVJgwJXt++jzTwiFM= modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE= diff --git a/main.go b/main.go index 61c5cb0cb9a0184569a755f1fc2bb022e4ba0f5f..04d33347147418ad02d4cdf924e282746bc2a9e0 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( _ "github.com/joho/godotenv/autoload" + "jonasled.dev/firehouse-smokedetection/backend/alarm" "jonasled.dev/firehouse-smokedetection/backend/database" "jonasled.dev/firehouse-smokedetection/backend/mqttclient" "jonasled.dev/firehouse-smokedetection/backend/mqttserver" @@ -26,6 +27,7 @@ func main() { database.Init() mqttclient.Init() webserver.Init() + alarm.Init() webserver.Run() } diff --git a/mqttclient/mqttmessage.go b/mqttclient/mqttmessage.go index 496920bfcb64507adb50eda917177ad9d8b09350..75e7721bb3935aaedae945aaec934bba5f79efc3 100644 --- a/mqttclient/mqttmessage.go +++ b/mqttclient/mqttmessage.go @@ -5,6 +5,7 @@ import ( "strings" mqtt "github.com/eclipse/paho.mqtt.golang" + "jonasled.dev/firehouse-smokedetection/backend/alarm" "jonasled.dev/firehouse-smokedetection/backend/cache" "jonasled.dev/firehouse-smokedetection/backend/database" "jonasled.dev/firehouse-smokedetection/backend/database/tables" @@ -35,8 +36,8 @@ var messagePubHandlerZ2M mqtt.MessageHandler = func(client mqtt.Client, msg mqtt var smokeDetector tables.SmokeDetector topicSplit := strings.Split(msg.Topic(), "/") deviceName := topicSplit[len(topicSplit)-1] - tx := database.Db.First(&smokeDetector, tables.SmokeDetector{ZigBeeName: deviceName}) - if tx.RowsAffected == 0 { + database.Db.First(&smokeDetector, tables.SmokeDetector{ZigBeeName: deviceName}) + if smokeDetector.ID == 0 { log.Log.Info("Found new smoke detector: ", deviceName) smokeDetector = tables.SmokeDetector{ ZigBeeName: deviceName, @@ -47,7 +48,7 @@ var messagePubHandlerZ2M mqtt.MessageHandler = func(client mqtt.Client, msg mqtt database.Db.Save(&smokeDetector) if mqttMessage.Smoke { - log.Log.Infof("Sensor %s reported smoke", smokeDetector.Name) + alarm.Alarm(mqttMessage, smokeDetector) } } diff --git a/types/updateSmokeDetectorRequest.go b/types/updateSmokeDetectorRequest.go index c7c96e41da771c8f17060b79d3d26d1fd82c069f..286ce16855c358b98896d361ffe6766cdd0529c5 100644 --- a/types/updateSmokeDetectorRequest.go +++ b/types/updateSmokeDetectorRequest.go @@ -1,5 +1,7 @@ package types type UpdateSmokeDetectorRequest struct { - Name string `json:"name"` + Name string `json:"name"` + AlarmPlugin string `json:"alarmPlugin"` + AlarmPluginConfigId uint `json:"alarmPluginConfigId"` } diff --git a/webserver/plugins/deleteConfig.go b/webserver/plugins/deleteConfig.go new file mode 100644 index 0000000000000000000000000000000000000000..a3a609eb33c99ad4324f865e0cf329236fe253e9 --- /dev/null +++ b/webserver/plugins/deleteConfig.go @@ -0,0 +1,47 @@ +package plugins + +import ( + "net/http" + "slices" + "strconv" + + "github.com/gin-gonic/gin" + "jonasled.dev/firehouse-smokedetection/backend/alarm" + "jonasled.dev/firehouse-smokedetection/backend/database" + "jonasled.dev/firehouse-smokedetection/backend/types" + plugintypes "jonasled.dev/firehouse-smokedetection/plugin-interface/types" +) + +// @Summary Delete Config +// @Description Delete a existing config +// @Tags plugins +// @Produce json +// @Param plugin path string true "Plugin name" +// @Param configId path int true "ID of the config to update" +// @router /api/v1/plugins/{plugin}/{configId} [delete] +func deleteConfig(ctx *gin.Context) { + pluginNames := alarm.GetAllPlugins() + pluginName := ctx.Param("plugin") + configId := ctx.Param("id") + if !slices.Contains(pluginNames, pluginName) { + ctx.JSON(http.StatusNotFound, types.GeneralResponse{ + Success: false, + Message: "Plugin with given name not found", + }) + return + } + + configIdInt, err := strconv.Atoi(configId) + if err != nil { + ctx.JSON(http.StatusNotFound, types.GeneralResponse{ + Success: false, + Message: "Failed parsing config id as number", + }) + return + } + database.Db.Delete(plugintypes.AlarmConfig{ID: uint(configIdInt), Plugin: pluginName}) + ctx.JSON(http.StatusOK, types.GeneralResponse{ + Success: true, + Message: "The config has been deleted", + }) +} diff --git a/webserver/plugins/getConfig.go b/webserver/plugins/getConfig.go new file mode 100644 index 0000000000000000000000000000000000000000..3166b78241b3ae6d31761624dbfa63f82d90f6e1 --- /dev/null +++ b/webserver/plugins/getConfig.go @@ -0,0 +1,46 @@ +package plugins + +import ( + "net/http" + "slices" + "strconv" + + "github.com/gin-gonic/gin" + "jonasled.dev/firehouse-smokedetection/backend/alarm" + "jonasled.dev/firehouse-smokedetection/backend/database" + "jonasled.dev/firehouse-smokedetection/backend/types" + plugintypes "jonasled.dev/firehouse-smokedetection/plugin-interface/types" +) + +// @Summary Get Config +// @Description Get a existing config +// @Tags plugins +// @Produce json +// @Param plugin path string true "Plugin name" +// @Param configId path int true "ID of the config to update" +// @router /api/v1/plugins/{plugin}/{configId} [get] +func getConfig(ctx *gin.Context) { + pluginNames := alarm.GetAllPlugins() + pluginName := ctx.Param("plugin") + configId := ctx.Param("id") + if !slices.Contains(pluginNames, pluginName) { + ctx.JSON(http.StatusNotFound, types.GeneralResponse{ + Success: false, + Message: "Plugin with given name not found", + }) + return + } + + configIdInt, err := strconv.Atoi(configId) + if err != nil { + ctx.JSON(http.StatusNotFound, types.GeneralResponse{ + Success: false, + Message: "Failed parsing config id as number", + }) + return + } + var alarmConfig plugintypes.AlarmConfig + database.Db.Where(plugintypes.AlarmConfig{ID: uint(configIdInt), Plugin: pluginName}).First(&alarmConfig) + ctx.Header("Content-Type", "application/json") + ctx.String(http.StatusOK, alarmConfig.Config) +} diff --git a/webserver/plugins/getSchema.go b/webserver/plugins/getSchema.go new file mode 100644 index 0000000000000000000000000000000000000000..17bf76650f50b65e225572f37d1aa037571b9d5a --- /dev/null +++ b/webserver/plugins/getSchema.go @@ -0,0 +1,32 @@ +package plugins + +import ( + "net/http" + "slices" + + "github.com/gin-gonic/gin" + "github.com/invopop/jsonschema" + "jonasled.dev/firehouse-smokedetection/backend/alarm" + "jonasled.dev/firehouse-smokedetection/backend/types" +) + +// @Summary Plugin config schema +// @Description Returns the plugins config JSON schema +// @Tags plugins +// @Produce json +// @Param plugin path string true "Plugin name" +// @router /api/v1/plugins/{plugin}/schema [get] +func getSchema(ctx *gin.Context) { + pluginNames := alarm.GetAllPlugins() + pluginName := ctx.Param("plugin") + if !slices.Contains(pluginNames, pluginName) { + ctx.JSON(http.StatusNotFound, types.GeneralResponse{ + Success: false, + Message: "Plugin with given name not found", + }) + return + } + + jsonSchema := jsonschema.Reflect(alarm.Plugins[pluginName].GetConfigType()) + ctx.JSON(http.StatusOK, jsonSchema) +} diff --git a/webserver/plugins/init.go b/webserver/plugins/init.go new file mode 100644 index 0000000000000000000000000000000000000000..433fe1e884b97ece3d97885c1a557d7bf8aaddbe --- /dev/null +++ b/webserver/plugins/init.go @@ -0,0 +1,16 @@ +package plugins + +import "github.com/gin-gonic/gin" + +func Init(r *gin.Engine) { + plugins := r.Group("/api/v1/plugins") + { + plugins.GET("", listAll) + plugins.GET(":plugin/schema", getSchema) + plugins.POST(":plugin", setNewConfig) + plugins.PUT(":plugin/:id", updateConfig) + plugins.GET(":plugin/:id", getConfig) + plugins.DELETE(":plugin/:id", deleteConfig) + } + +} diff --git a/webserver/plugins/list.go b/webserver/plugins/list.go new file mode 100644 index 0000000000000000000000000000000000000000..d9dafe9965f24f7a175b647819ed087914f6ff53 --- /dev/null +++ b/webserver/plugins/list.go @@ -0,0 +1,26 @@ +package plugins + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "jonasled.dev/firehouse-smokedetection/backend/alarm" + plugintypes "jonasled.dev/firehouse-smokedetection/plugin-interface/types" +) + +// @Summary List plugins +// @Description List all registered plugins +// @Tags plugins +// @Produce json +// @Success 200 {object} []plugintypes.PluginMetadata +// @router /api/v1/plugins [get] +func listAll(ctx *gin.Context) { + pluginNames := alarm.GetAllPlugins() + pluginResponse := []plugintypes.PluginMetadata{} + + for _, pluginName := range pluginNames { + pluginResponse = append(pluginResponse, alarm.Plugins[pluginName].GetMetadata()) + } + + ctx.JSON(http.StatusOK, pluginResponse) +} diff --git a/webserver/plugins/setConfig.go b/webserver/plugins/setConfig.go new file mode 100644 index 0000000000000000000000000000000000000000..deb62947e2ea2fedae18971c1f7f09a604e0d41a --- /dev/null +++ b/webserver/plugins/setConfig.go @@ -0,0 +1,109 @@ +package plugins + +import ( + "encoding/json" + "io" + "net/http" + "slices" + "strconv" + + "github.com/gin-gonic/gin" + "jonasled.dev/firehouse-smokedetection/backend/alarm" + "jonasled.dev/firehouse-smokedetection/backend/database" + "jonasled.dev/firehouse-smokedetection/backend/types" + plugintypes "jonasled.dev/firehouse-smokedetection/plugin-interface/types" +) + +// @Summary Create Config +// @Description Creates a new config element for the given plugin +// @Tags plugins +// @Produce json +// @Param config body interface{} true "New config entry for plugin" +// @Param plugin path string true "Plugin name" +// @router /api/v1/plugins/{plugin} [post] +func setNewConfig(ctx *gin.Context) { + pluginNames := alarm.GetAllPlugins() + pluginName := ctx.Param("plugin") + if !slices.Contains(pluginNames, pluginName) { + ctx.JSON(http.StatusNotFound, types.GeneralResponse{ + Success: false, + Message: "Plugin with given name not found", + }) + return + } + + body, _ := io.ReadAll(ctx.Request.Body) + pluginConfig := alarm.Plugins[pluginName].GetConfigType() + err := json.Unmarshal(body, &pluginConfig) + if err != nil { + ctx.JSON(http.StatusBadRequest, types.GeneralResponse{ + Success: true, + Message: "failed parsing configuration: " + err.Error(), + }) + return + } + configJson, _ := json.Marshal(&pluginConfig) + dbPluginConfig := plugintypes.AlarmConfig{ + Plugin: pluginName, + Config: string(configJson), + } + database.Db.Save(&dbPluginConfig) + + ctx.JSON(http.StatusOK, types.GeneralResponse{ + Success: true, + Message: "The config has been created", + }) +} + +// @Summary Update Config +// @Description Updates a existing config element for the given plugin +// @Tags plugins +// @Produce json +// @Param config body interface{} true "New config entry for plugin" +// @Param plugin path string true "Plugin name" +// @Param configId path int true "ID of the config to update" +// @router /api/v1/plugins/{plugin}/{configId} [put] +func updateConfig(ctx *gin.Context) { + pluginNames := alarm.GetAllPlugins() + pluginName := ctx.Param("plugin") + configId := ctx.Param("id") + if !slices.Contains(pluginNames, pluginName) { + ctx.JSON(http.StatusNotFound, types.GeneralResponse{ + Success: false, + Message: "Plugin with given name not found", + }) + return + } + + configIdInt, err := strconv.Atoi(configId) + if err != nil { + ctx.JSON(http.StatusNotFound, types.GeneralResponse{ + Success: false, + Message: "Failed parsing config id as number", + }) + return + } + + body, _ := io.ReadAll(ctx.Request.Body) + pluginConfig := alarm.Plugins[pluginName].GetConfigType() + err = json.Unmarshal(body, &pluginConfig) + if err != nil { + ctx.JSON(http.StatusBadRequest, types.GeneralResponse{ + Success: true, + Message: "failed parsing configuration: " + err.Error(), + }) + return + } + configJson, _ := json.Marshal(&pluginConfig) + dbPluginConfig := plugintypes.AlarmConfig{ + Plugin: pluginName, + ID: uint(configIdInt), + Config: string(configJson), + } + database.Db.Save(&dbPluginConfig) + + ctx.JSON(http.StatusOK, types.GeneralResponse{ + Success: true, + Message: "The config has been updated", + }) +} diff --git a/webserver/smokeDetectors/update.go b/webserver/smokeDetectors/update.go index 4a529dc53737995ff4f296372d463bfb16d25be8..ad695da0ebb6bc658c56866f7ad0e78cdede2d36 100644 --- a/webserver/smokeDetectors/update.go +++ b/webserver/smokeDetectors/update.go @@ -53,6 +53,13 @@ func update(ctx *gin.Context) { if requestBody.Name != "" { smokeDetectors.Name = requestBody.Name } + if requestBody.AlarmPlugin != "" { + smokeDetectors.AlarmPlugin = requestBody.AlarmPlugin + } + if requestBody.AlarmPluginConfigId != 0 { + smokeDetectors.AlarmPluginConfigId = requestBody.AlarmPluginConfigId + } + database.Db.Save(&smokeDetectors) ctx.JSON(http.StatusOK, &smokeDetectors) diff --git a/webserver/webserver.go b/webserver/webserver.go index 2c2f33b68dbfdfa53b462abc366d2f13d8d4131c..e1efd97330aae9481a5757d1aa4c28abfbb3f445 100644 --- a/webserver/webserver.go +++ b/webserver/webserver.go @@ -4,6 +4,7 @@ import ( "github.com/gin-gonic/gin" ginlogrus "github.com/toorop/gin-logrus" "jonasled.dev/firehouse-smokedetection/backend/webserver/mqtt" + "jonasled.dev/firehouse-smokedetection/backend/webserver/plugins" "jonasled.dev/firehouse-smokedetection/backend/webserver/smokeDetectors" "jonasled.dev/jonasled/go-libs/log" ) @@ -18,6 +19,7 @@ func Init() { initSwaggerRoutes() mqtt.Init(R) smokeDetectors.Init(R) + plugins.Init(R) }