Compare commits
No commits in common. "b98174c7da4dd5734b60969464e26ad4e2d5be2f" and "17753f535c68157e80f30f50deb488133cc98258" have entirely different histories.
b98174c7da
...
17753f535c
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -11,6 +11,6 @@ spaceapid
|
||||||
spaceapid-state.json
|
spaceapid-state.json
|
||||||
|
|
||||||
# Config shards
|
# Config shards
|
||||||
config-credentials.json
|
credentials.json
|
||||||
config-dynamic.json
|
dynamic.json
|
||||||
config-response.json
|
response.json
|
||||||
|
|
16
README.md
16
README.md
|
@ -3,7 +3,7 @@
|
||||||
`spaceapid` serves a [SpaceAPI](https://spaceapi.io)-compatible JSON on port 8080:
|
`spaceapid` serves a [SpaceAPI](https://spaceapi.io)-compatible JSON on port 8080:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ curl http://[::1]:8080 | jq
|
$ curl -X GET http://[::1]:8080 | jq
|
||||||
{
|
{
|
||||||
"api_compatibility": [
|
"api_compatibility": [
|
||||||
"14"
|
"14"
|
||||||
|
@ -26,7 +26,7 @@ The config consists of three parts:
|
||||||
- `"response"`
|
- `"response"`
|
||||||
- The static (pre-filled) parts of the response
|
- The static (pre-filled) parts of the response
|
||||||
|
|
||||||
See [Running](#Running) for details.
|
See [Running](#running) for details.
|
||||||
|
|
||||||
## Updating values
|
## Updating values
|
||||||
|
|
||||||
|
@ -37,18 +37,12 @@ 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"`.
|
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.
|
Currently only `temperature` and `humidity` are implemented.
|
||||||
At the time of writing this includes `temperature`, `barometer`, `humidity`, `beverage_supply`, `power_consumption`,
|
|
||||||
and `account_balance`.
|
|
||||||
Out-of-spec sensors may also be used as long as they share the same schema.
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl -X PUT -u user:password -d 23.42 http://[::1]:8080/sensors/{temperature,humidity,...}[/location[/name]]
|
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
|
## Building
|
||||||
|
|
||||||
See the `go.mod` file for minimum required Go version.
|
See the `go.mod` file for minimum required Go version.
|
||||||
|
@ -61,5 +55,5 @@ Set the environment variable to a comma-separated list of config files or pass t
|
||||||
```shell
|
```shell
|
||||||
env CONFIG_PATH=config-template.json go run .
|
env CONFIG_PATH=config-template.json go run .
|
||||||
# OR
|
# OR
|
||||||
go run . -c config-credentials.json,config-dynamic.json,config-response.json
|
go run . -c credentials.json,dynamic.json,response.json
|
||||||
```
|
```
|
||||||
|
|
|
@ -46,16 +46,6 @@
|
||||||
"club-assistant"
|
"club-assistant"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"beverage_supply": [
|
|
||||||
{
|
|
||||||
"sensor_data": {
|
|
||||||
"unit": "btl"
|
|
||||||
},
|
|
||||||
"allowed_credentials": [
|
|
||||||
"club-assistant"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"state": {
|
"state": {
|
||||||
|
@ -76,8 +66,8 @@
|
||||||
"url": "https://hamburg.ccc.de/",
|
"url": "https://hamburg.ccc.de/",
|
||||||
"location": {
|
"location": {
|
||||||
"address": "Zeiseweg 9, 22765 Hamburg, Germany",
|
"address": "Zeiseweg 9, 22765 Hamburg, Germany",
|
||||||
"lon": 9.94444,
|
"lon": 9.9445899999999998,
|
||||||
"lat": 53.5584
|
"lat": 53.55836
|
||||||
},
|
},
|
||||||
"contact": {
|
"contact": {
|
||||||
"phone": "+49 40 23830150",
|
"phone": "+49 40 23830150",
|
||||||
|
@ -89,7 +79,7 @@
|
||||||
},
|
},
|
||||||
"feeds": {
|
"feeds": {
|
||||||
"blog": {
|
"blog": {
|
||||||
"type": "application/rss+xml",
|
"type": "application/atom+xml",
|
||||||
"url": "https://hamburg.ccc.de/feed.xml"
|
"url": "https://hamburg.ccc.de/feed.xml"
|
||||||
},
|
},
|
||||||
"calendar": {
|
"calendar": {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/types"
|
"gitlab.hamburg.ccc.de/ccchh/spaceapid/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/types"
|
"gitlab.hamburg.ccc.de/ccchh/spaceapid/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HTTPBACredentialID string
|
type HTTPBACredentialID string
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -1,3 +1,3 @@
|
||||||
module git.hamburg.ccc.de/ccchh/spaceapid
|
module gitlab.hamburg.ccc.de/ccchh/spaceapid
|
||||||
|
|
||||||
go 1.21
|
go 1.21
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/types"
|
"gitlab.hamburg.ccc.de/ccchh/spaceapid/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Root(resp *types.SpaceAPIResponseV14) func(http.ResponseWriter, *http.Request) {
|
func Root(resp *types.SpaceAPIResponseV14) func(http.ResponseWriter, *http.Request) {
|
||||||
|
|
|
@ -7,8 +7,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/config"
|
"gitlab.hamburg.ccc.de/ccchh/spaceapid/config"
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/types"
|
"gitlab.hamburg.ccc.de/ccchh/spaceapid/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func EnvironmentSensor(
|
func EnvironmentSensor(
|
||||||
|
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/config"
|
"gitlab.hamburg.ccc.de/ccchh/spaceapid/config"
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/types"
|
"gitlab.hamburg.ccc.de/ccchh/spaceapid/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StateOpen(
|
func StateOpen(
|
||||||
|
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/config"
|
"gitlab.hamburg.ccc.de/ccchh/spaceapid/config"
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/util"
|
"gitlab.hamburg.ccc.de/ccchh/spaceapid/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// updateEndpointValidator checks BasicAuth credentials,
|
// updateEndpointValidator checks BasicAuth credentials,
|
||||||
|
|
29
main.go
29
main.go
|
@ -2,17 +2,18 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/config"
|
"gitlab.hamburg.ccc.de/ccchh/spaceapid/config"
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/handlers"
|
"gitlab.hamburg.ccc.de/ccchh/spaceapid/handlers"
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/persistence"
|
"gitlab.hamburg.ccc.de/ccchh/spaceapid/types"
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/types"
|
"gitlab.hamburg.ccc.de/ccchh/spaceapid/util"
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -38,7 +39,7 @@ func main() {
|
||||||
conf := config.ParseConfiguration()
|
conf := config.ParseConfiguration()
|
||||||
|
|
||||||
// Merge old state if present
|
// Merge old state if present
|
||||||
persistence.MergeOldState(&conf.Response)
|
util.MergeOldState(&conf.Response)
|
||||||
|
|
||||||
// Register signal handler
|
// Register signal handler
|
||||||
sc := make(chan os.Signal, 1)
|
sc := make(chan os.Signal, 1)
|
||||||
|
@ -46,7 +47,7 @@ func main() {
|
||||||
go func(resp *types.SpaceAPIResponseV14) {
|
go func(resp *types.SpaceAPIResponseV14) {
|
||||||
<-sc
|
<-sc
|
||||||
log.Println("Saving state and shutting down...")
|
log.Println("Saving state and shutting down...")
|
||||||
persistence.SaveCurrentState(*resp)
|
util.SaveCurrentState(*resp)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}(&conf.Response)
|
}(&conf.Response)
|
||||||
|
|
||||||
|
@ -58,15 +59,15 @@ func main() {
|
||||||
handlers.StateOpen(conf.Credentials, conf.Dynamic.State.Open.AllowedCredentials, &conf.Response.State),
|
handlers.StateOpen(conf.Credentials, conf.Dynamic.State.Open.AllowedCredentials, &conf.Response.State),
|
||||||
)
|
)
|
||||||
// Register handlers for Environmental Sensors
|
// Register handlers for Environmental Sensors
|
||||||
for sensorType, envSensorConfigs := range conf.Dynamic.Sensors {
|
for key, envSensorConfigs := range conf.Dynamic.Sensors {
|
||||||
for i, envSensorConfig := range envSensorConfigs {
|
for i, envSensorConfig := range envSensorConfigs {
|
||||||
urlPath := util.GetSensorURLPath(
|
pattern := fmt.Sprintf("/sensors/%s/%s", key, envSensorConfig.SensorData.Location)
|
||||||
sensorType, envSensorConfig.SensorData.Location, envSensorConfig.SensorData.Name,
|
if envSensorConfig.SensorData.Name != "" {
|
||||||
)
|
pattern += "/" + envSensorConfig.SensorData.Name
|
||||||
http.HandleFunc(
|
}
|
||||||
urlPath,
|
http.HandleFunc(strings.ToLower(pattern),
|
||||||
handlers.EnvironmentSensor(
|
handlers.EnvironmentSensor(
|
||||||
conf.Credentials, envSensorConfig.AllowedCredentials, &conf.Response.Sensors[sensorType][i],
|
conf.Credentials, envSensorConfig.AllowedCredentials, &conf.Response.Sensors[key][i],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
package persistence
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
persistentStateFile = "spaceapid-state.json"
|
|
||||||
persistentStatePath = persistentStateDir + persistentStateFile
|
|
||||||
)
|
|
||||||
|
|
||||||
// MergeOldState merges a given SpaceAPIResponse with the state saved at the time of last program exit and then deletes
|
|
||||||
// the file containing the old state.
|
|
||||||
func MergeOldState(response *types.SpaceAPIResponseV14) {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
oldState []byte
|
|
||||||
persistedState types.PersistentStateV14
|
|
||||||
)
|
|
||||||
|
|
||||||
log.Println("Merging old state from", persistentStatePath, "...")
|
|
||||||
|
|
||||||
// Check if state.json is present
|
|
||||||
_, err = os.Stat(persistentStatePath)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Old state json not accessible at", persistentStatePath, ", skipping merge... error:", err)
|
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
goto removeOld
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read file and load persisted state
|
|
||||||
oldState, err = os.ReadFile(persistentStatePath)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Error reading old state from", persistentStatePath, ", skipping merge... error:", err)
|
|
||||||
goto removeOld
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(oldState, &persistedState)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(persistentStatePath, "doesn't seem to contain valid data... error:", err)
|
|
||||||
goto removeOld
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge state
|
|
||||||
response.State = persistedState.State
|
|
||||||
|
|
||||||
// Merge sensors
|
|
||||||
for key, environmentSensors := range persistedState.Sensors {
|
|
||||||
for _, sensor := range environmentSensors {
|
|
||||||
// Order or amount of sensors might have changed, so check sensors already present in response
|
|
||||||
// and then look for matching values in persisted state
|
|
||||||
for i := range response.Sensors[key] {
|
|
||||||
if rs := &response.Sensors[key][i]; rs.Location == sensor.Location && rs.Name == sensor.Name {
|
|
||||||
rs.Value = sensor.Value
|
|
||||||
rs.LastChange = sensor.LastChange
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete old state json
|
|
||||||
removeOld:
|
|
||||||
err = os.RemoveAll(persistentStatePath)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Failed to remove", persistentStatePath, ", continuing... error:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveCurrentState(response types.SpaceAPIResponseV14) {
|
|
||||||
// Create state directory if not present
|
|
||||||
err := os.MkdirAll(persistentStateDir, 0750)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("Failed creating", persistentStateDir, ", aborting... error:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open persistent state file
|
|
||||||
file, err := os.OpenFile(persistentStatePath, os.O_WRONLY|os.O_CREATE, 0644)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("Failed opening", persistentStatePath, "while trying to save current state... error:", err)
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
// Create persistent state
|
|
||||||
persistentStateV14 := types.PersistentStateV14{
|
|
||||||
State: struct {
|
|
||||||
Open bool `json:"open"`
|
|
||||||
LastChange int64 `json:"lastchange"`
|
|
||||||
}{Open: response.State.Open, LastChange: response.State.LastChange},
|
|
||||||
}
|
|
||||||
// Save sensor state
|
|
||||||
persistentStateV14.Sensors = make(map[string][]types.PersistentEnvironmentSensor)
|
|
||||||
for key, environmentSensors := range response.Sensors {
|
|
||||||
persistentStateV14.Sensors[key] = make([]types.PersistentEnvironmentSensor, len(environmentSensors))
|
|
||||||
for i, sensor := range environmentSensors {
|
|
||||||
persistentStateV14.Sensors[key][i].Value = sensor.Value
|
|
||||||
persistentStateV14.Sensors[key][i].Location = sensor.Location
|
|
||||||
persistentStateV14.Sensors[key][i].Name = sensor.Name
|
|
||||||
persistentStateV14.Sensors[key][i].LastChange = sensor.LastChange
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize persistent state
|
|
||||||
marshal, err := json.MarshalIndent(persistentStateV14, "", "\t")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("Failed serializing persistent state... error:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write to file
|
|
||||||
_, err = file.Write(marshal)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("Failed writing persistent state to file", file.Name(), "... error:", err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
//go:build !linux
|
|
||||||
|
|
||||||
package persistence
|
|
||||||
|
|
||||||
const (
|
|
||||||
persistentStateDir = "./"
|
|
||||||
)
|
|
|
@ -1,5 +0,0 @@
|
||||||
package persistence
|
|
||||||
|
|
||||||
const (
|
|
||||||
persistentStateDir = "/var/lib/spaceapid/"
|
|
||||||
)
|
|
|
@ -45,7 +45,7 @@ type SpaceState struct {
|
||||||
type EnvironmentSensor struct {
|
type EnvironmentSensor struct {
|
||||||
Value float64 `json:"value"`
|
Value float64 `json:"value"`
|
||||||
Unit string `json:"unit"`
|
Unit string `json:"unit"`
|
||||||
Location string `json:"location,omitempty"`
|
Location string `json:"location"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
LastChange int64 `json:"lastchange,omitempty"`
|
LastChange int64 `json:"lastchange,omitempty"`
|
||||||
|
@ -61,7 +61,7 @@ type PersistentStateV14 struct {
|
||||||
|
|
||||||
type PersistentEnvironmentSensor struct {
|
type PersistentEnvironmentSensor struct {
|
||||||
Value float64 `json:"value"`
|
Value float64 `json:"value"`
|
||||||
Location string `json:"location,omitempty"`
|
Location string `json:"location"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
LastChange int64 `json:"lastchange"`
|
LastChange int64 `json:"lastchange"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.hamburg.ccc.de/ccchh/spaceapid/config"
|
"gitlab.hamburg.ccc.de/ccchh/spaceapid/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CheckCredentials validates whether a given username/password pair matches an
|
// CheckCredentials validates whether a given username/password pair matches an
|
||||||
|
|
123
util/util.go
123
util/util.go
|
@ -1,20 +1,119 @@
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"encoding/json"
|
||||||
"strings"
|
"errors"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"gitlab.hamburg.ccc.de/ccchh/spaceapid/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetSensorURLPath generates the URL path for the given sensor details.
|
const (
|
||||||
// location and name may be optional depending on sensorType, see the schema definition for details.
|
savedStateJSONPath = "/var/lib/spaceapid/spaceapid-state.json"
|
||||||
// The path is always all-lowercase.
|
)
|
||||||
func GetSensorURLPath(sensorType, location, name string) string {
|
|
||||||
path := fmt.Sprintf("/sensors/%s", sensorType)
|
// MergeOldState merges a given SpaceAPIResponse with the state saved at the time of last program exit and then deletes
|
||||||
if location != "" {
|
// the file containing the old state.
|
||||||
path += "/" + location
|
func MergeOldState(response *types.SpaceAPIResponseV14) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
oldState []byte
|
||||||
|
persistedState types.PersistentStateV14
|
||||||
|
)
|
||||||
|
|
||||||
|
log.Println("Merging old state from", savedStateJSONPath, "...")
|
||||||
|
|
||||||
|
// Check if state.json is present
|
||||||
|
_, err = os.Stat(savedStateJSONPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Old state json not accessible at", savedStateJSONPath, ", skipping merge... error:", err)
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
goto removeOld
|
||||||
}
|
}
|
||||||
if name != "" {
|
|
||||||
path += "/" + name
|
// Read file and load persisted state
|
||||||
|
oldState, err = os.ReadFile(savedStateJSONPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error reading old state from", savedStateJSONPath, ", skipping merge... error:", err)
|
||||||
|
goto removeOld
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(oldState, &persistedState)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(savedStateJSONPath, "doesn't seem to contain valid data... error:", err)
|
||||||
|
goto removeOld
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge state
|
||||||
|
response.State = persistedState.State
|
||||||
|
|
||||||
|
// Merge sensors
|
||||||
|
for key, environmentSensors := range persistedState.Sensors {
|
||||||
|
for _, sensor := range environmentSensors {
|
||||||
|
// Order or amount of sensors might have changed, so check sensors already present in response
|
||||||
|
// and then look for matching values in persisted state
|
||||||
|
for i := range response.Sensors[key] {
|
||||||
|
if rs := &response.Sensors[key][i]; rs.Location == sensor.Location && rs.Name == sensor.Name {
|
||||||
|
rs.Value = sensor.Value
|
||||||
|
rs.LastChange = sensor.LastChange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete old state json
|
||||||
|
removeOld:
|
||||||
|
err = os.RemoveAll(savedStateJSONPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to remove", savedStateJSONPath, ", continuing... error:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveCurrentState(response types.SpaceAPIResponseV14) {
|
||||||
|
// Create state directory if not present
|
||||||
|
err := os.MkdirAll(path.Dir(savedStateJSONPath), 0750)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed creating", savedStateJSONPath, ", aborting... error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open persistent state file
|
||||||
|
file, err := os.OpenFile(savedStateJSONPath, os.O_WRONLY|os.O_CREATE, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed opening", savedStateJSONPath, "while trying to save current state... error:", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// Create persistent state
|
||||||
|
persistentStateV14 := types.PersistentStateV14{
|
||||||
|
State: struct {
|
||||||
|
Open bool `json:"open"`
|
||||||
|
LastChange int64 `json:"lastchange"`
|
||||||
|
}{Open: response.State.Open, LastChange: response.State.LastChange},
|
||||||
|
}
|
||||||
|
// Save sensor state
|
||||||
|
persistentStateV14.Sensors = make(map[string][]types.PersistentEnvironmentSensor)
|
||||||
|
for key, environmentSensors := range response.Sensors {
|
||||||
|
persistentStateV14.Sensors[key] = make([]types.PersistentEnvironmentSensor, len(environmentSensors))
|
||||||
|
for i, sensor := range environmentSensors {
|
||||||
|
persistentStateV14.Sensors[key][i].Value = sensor.Value
|
||||||
|
persistentStateV14.Sensors[key][i].Location = sensor.Location
|
||||||
|
persistentStateV14.Sensors[key][i].Name = sensor.Name
|
||||||
|
persistentStateV14.Sensors[key][i].LastChange = sensor.LastChange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize persistent state
|
||||||
|
marshal, err := json.MarshalIndent(persistentStateV14, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed serializing persistent state... error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to file
|
||||||
|
_, err = file.Write(marshal)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed writing persistent state to file", file.Name(), "... error:", err)
|
||||||
}
|
}
|
||||||
return strings.ToLower(path)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue