From 68d0ec05db000526f20c4277f2f7cd5157de436c Mon Sep 17 00:00:00 2001 From: Bennett Wetters Date: Sat, 3 Aug 2024 19:48:33 +0200 Subject: [PATCH 1/2] feat: Add support for PUT/DELETE state.message Fix #33 --- handlers/state.go | 34 ++++++++++++++++++++++++++++++++++ main.go | 6 ++++++ persistence/persistentState.go | 7 ++++--- types/v14.go | 10 ++++++---- 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/handlers/state.go b/handlers/state.go index f965db4..d1ab9d9 100644 --- a/handlers/state.go +++ b/handlers/state.go @@ -34,3 +34,37 @@ func StateOpen( resp.LastChange = time.Now().Unix() } } + +func StateMessagePUT( + authDB config.HTTPBACredentials, validCredentials []config.HTTPBACredentialID, + resp *types.SpaceState, +) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + body, err := updateEndpointValidator(authDB, validCredentials, w, r) + if err != nil { + log.Println(err) + return + } + + // Set SpaceAPI response values + resp.Message = string(body) + resp.LastChange = time.Now().Unix() + } +} + +func StateMessageDELETE( + authDB config.HTTPBACredentials, validCredentials []config.HTTPBACredentialID, + resp *types.SpaceState, +) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + _, err := updateEndpointValidator(authDB, validCredentials, w, r) + if err != nil { + log.Println(err) + return + } + + // Set SpaceAPI response values + resp.Message = "" + resp.LastChange = time.Now().Unix() + } +} diff --git a/main.go b/main.go index 4e16106..2581e17 100644 --- a/main.go +++ b/main.go @@ -57,6 +57,12 @@ func main() { http.HandleFunc("PUT /state/open", handlers.StateOpen(conf.Credentials, conf.Dynamic.State.Open.AllowedCredentials, &conf.Response.State), ) + http.HandleFunc("PUT /state/message", + handlers.StateMessagePUT(conf.Credentials, conf.Dynamic.State.Open.AllowedCredentials, &conf.Response.State), + ) + http.HandleFunc("DELETE /state/message", + handlers.StateMessageDELETE(conf.Credentials, conf.Dynamic.State.Open.AllowedCredentials, &conf.Response.State), + ) // Register handler for environmental sensors for sensorType, envSensorConfigs := range conf.Dynamic.Sensors { for i, envSensorConfig := range envSensorConfigs { diff --git a/persistence/persistentState.go b/persistence/persistentState.go index 01e1912..74fbdb2 100644 --- a/persistence/persistentState.go +++ b/persistence/persistentState.go @@ -89,9 +89,10 @@ func SaveCurrentState(response types.SpaceAPIResponseV14) { // Create persistent state persistentStateV14 := types.PersistentStateV14{ State: struct { - Open bool `json:"open"` - LastChange int64 `json:"lastchange"` - }{Open: response.State.Open, LastChange: response.State.LastChange}, + Open bool `json:"open"` + LastChange int64 `json:"lastchange"` + Message string `json:"message,omitempty"` + }{Open: response.State.Open, LastChange: response.State.LastChange, Message: response.State.Message}, } // Save sensor state persistentStateV14.Sensors = make(map[string][]types.PersistentEnvironmentSensor) diff --git a/types/v14.go b/types/v14.go index 2492639..3763fe4 100644 --- a/types/v14.go +++ b/types/v14.go @@ -38,8 +38,9 @@ type SpaceAPIResponseV14 struct { } type SpaceState struct { - Open bool `json:"open"` - LastChange int64 `json:"lastchange"` + Open bool `json:"open"` + LastChange int64 `json:"lastchange"` + Message string `json:"message,omitempty"` } type EnvironmentSensor struct { @@ -53,8 +54,9 @@ type EnvironmentSensor struct { type PersistentStateV14 struct { State struct { - Open bool `json:"open"` - LastChange int64 `json:"lastchange"` + Open bool `json:"open"` + LastChange int64 `json:"lastchange"` + Message string `json:"message,omitempty"` } `json:"state"` Sensors map[string][]PersistentEnvironmentSensor `json:"sensors,omitempty"` } From 5dacfd0dfed14865b2a1f27c8832d598e3befd07 Mon Sep 17 00:00:00 2001 From: Bennett Wetters Date: Sat, 3 Aug 2024 21:02:44 +0200 Subject: [PATCH 2/2] docs: Update README and code comments --- README.md | 51 ++++++++++++++++++++++++++++++--------------------- main.go | 6 ++++-- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 8eaeb3f..577f3ee 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ `spaceapid` serves a [SpaceAPI](https://spaceapi.io)-compatible JSON on port 8080: ```shell -$ curl http://[::1]:8080 | jq +$ curl http://localhost:8080 | jq { "api_compatibility": [ "14" @@ -28,26 +28,6 @@ The config consists of three parts: See [Running](#Running) for details. -## Updating values - -The state of the boolean `state->open` property can be modified via `/state/open`: - -```shell -curl -X PUT -u user:password -d true http://[::1]:8080/state/open -``` - -The same is true for the endpoints for sensors configured under `"dynamic"`. -Currently only the sensors with the `value/unit/location/name/description` schema are implemented. -At the time of writing this includes `temperature`, `barometer`, `humidity`, `beverage_supply`, `power_consumption`, and `account_balance`. -Out-of-spec sensors may be used as well, as long as they share the same schema. - -```shell -curl -X PUT -u user:password -d 23.42 http://[::1]:8080/sensors/{temperature,humidity,...}[/location[/name]] -``` - -As can be seen in the example, the http urls are generated from sensor type and optionally `location` and `name`. -Depending on sensor type, `location` might be required for your sensors, see the schema for details. - ## Building See the `go.mod` file for minimum required Go version. @@ -62,3 +42,32 @@ env CONFIG_PATH=config-template.json go run . # OR go run . -c config-credentials.json,config-dynamic.json,config-response.json ``` + +## Updating values + +The state of the boolean `state->open` and `state->message` property can be modified via `/state/{open,message}`: + +```shell +curl -X PUT -u user:password -d true http://localhost:8080/state/open +curl -X PUT -u user:password -d "Nur mit Passierschein A38 :3" http://localhost:8080/state/message +``` + +As `state->message` is optional, its value can be deleted by using the `PUT` method with an empty payload, or by using `DELETE`: + +```shell +curl -X PUT -u user:password -d "" http://localhost:8080/state/message +# OR +curl -X DELETE -u user:password http://localhost:8080/state/message +``` + +The same updating procedure applies for the endpoints for sensors configured under `"dynamic"`. +Currently only the sensors with the `value/unit/location/name/description` schema are implemented. +At the time of writing this includes `temperature`, `barometer`, `humidity`, `beverage_supply`, `power_consumption`, and `account_balance`. +Out-of-spec sensors may be used as well, as long as they share the same schema. + +```shell +curl -X PUT -u user:password -d 23.42 http://localhost:8080/sensors/{temperature,humidity,...}[/location[/name]] +``` + +As can be seen in the example, the http urls are generated from sensor type and optionally `location` and `name`. +Depending on sensor type, `location` might be required for your sensors, see the schema for details. diff --git a/main.go b/main.go index 2581e17..35b564d 100644 --- a/main.go +++ b/main.go @@ -50,20 +50,22 @@ func main() { os.Exit(0) }(&conf.Response) - // Register HTTP handlers + // Root handler http.HandleFunc("GET /{$}", handlers.Root(&conf.Response), ) + // state->open http.HandleFunc("PUT /state/open", handlers.StateOpen(conf.Credentials, conf.Dynamic.State.Open.AllowedCredentials, &conf.Response.State), ) + // state->message http.HandleFunc("PUT /state/message", handlers.StateMessagePUT(conf.Credentials, conf.Dynamic.State.Open.AllowedCredentials, &conf.Response.State), ) http.HandleFunc("DELETE /state/message", handlers.StateMessageDELETE(conf.Credentials, conf.Dynamic.State.Open.AllowedCredentials, &conf.Response.State), ) - // Register handler for environmental sensors + // Register handlers for environmental sensors for sensorType, envSensorConfigs := range conf.Dynamic.Sensors { for i, envSensorConfig := range envSensorConfigs { urlPattern := "PUT " + util.GetSensorURLPath(