feat(pkg/db): postgres database

This commit is contained in:
Markus Pesch 2019-08-20 21:37:45 +02:00
parent c0aa155f36
commit 60ee044b88
Signed by: volker.raschek
GPG Key ID: 852BCC170D81A982
53 changed files with 2269 additions and 81 deletions

7
.env Normal file
View File

@ -0,0 +1,7 @@
PG_INTERN_PORT=5432
PG_EXTERN_PORT=5432
PG_NAME=public
PG_USER=postgres
PG_PASSWORD=postgres
TZ=Europe/Berlin

15
.env_dev Normal file
View File

@ -0,0 +1,15 @@
SERVER_PORT=80
DATABASE_DRIVER=postgres
DATABASE_NAME=postgres
DATABASE_SCHEMA=public
DATABASE_USER=postgres
DATABASE_PASSWORD=postgres
PG_HOST=flucky_db
PG_INTERN_PORT=5432
PG_EXTERN_PORT=5433
PG_NAME=public
PG_USER=postgres
PG_PASSWORD=postgres
TZ=Europe/Berlin

View File

@ -2,4 +2,4 @@ services:
- docker
script:
- make container-build
- make container-go-build

View File

@ -8,7 +8,7 @@ GID?=$(shell id --group)
# VERSION
# If no version is specified as a parameter of make, the last git hash
# value is taken.
VERSION:=$(or ${TRAVIS_TAG}, ${TRAVIS_TAG}, $(shell git rev-parse --short HEAD)-git)
VERSION:=$(or ${TRAVIS_TAG}, $(shell git rev-parse --short HEAD)-git)
CONTAINER_RUNTIME?=$(shell which docker)
@ -16,23 +16,24 @@ GOARCH?=amd64
GOOS?=linux
# default build
build:
go-build: bindata
GOOS=${GOOS} \
GOARCH=${GOARCH} \
go build -ldflags "-X main.version=${VERSION}"
chown -R ${UID}:${GID} *
test: bindata
go-test: go-build
go test -v ./pkg/...
bindata:
go-bindata -pkg logfile_test -o ./pkg/logfile/bindata_test.go ./pkg/logfile/test/***
go-bindata -pkg db -o ./pkg/db/bindataSQL.go ./pkg/db/sql/***
go-bindata -pkg goldenfiles -o ./test/goldenfiles/bindata.go ./test/goldenfiles/json/***
container-build:
$(MAKE) container-run COMMAND=build
container-go-build:
$(MAKE) container-run COMMAND=go-build
container-test:
$(MAKE) container-run COMMAND=test
container-go-test:
$(MAKE) container-run COMMAND=go-test
container-run:
${CONTAINER_RUNTIME} run \
@ -42,6 +43,6 @@ container-run:
make ${COMMAND} UID=${UID} GID=${GID} VERSION=${VERSION} GOOS=${GOOS} GOARCH=${GOARCH}
remote:
$(MAKE) build GOARCH=arm
$(MAKE) go-build GOARCH=arm
scp flucky ${FLUCKY_REMOTE}:/usr/local/bin
ssh ${FLUCKY_REMOTE} "chmod +x /usr/local/bin/flucky"

View File

@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"math"
"os"
"time"
@ -34,12 +35,19 @@ var rootCmd = &cobra.Command{
return fmt.Errorf("Can not locate the hostname: %v", err)
}
// Time must be truncted for postgres
// Postgres currently does not support nanoseconds which is automatically
// include into the go time object
t := time.Now()
l, _ := time.LoadLocation("Europe/Berlin")
t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), int(math.Round(float64(t.Nanosecond())/1000000)*1000000), l)
cnf := config.Configuration{
Device: &types.Device{
DeviceID: uuid.NewV4().String(),
DeviceName: hostname,
Logfile: "/var/log/flucky/logfile.csv",
CreationDate: time.Now(),
CreationDate: t,
},
}
@ -62,6 +70,7 @@ func Execute(version string) {
compression.InitCmd(rootCmd, &configFile)
convert.InitCmd(rootCmd, &configFile)
daemon.InitCmd(rootCmd, &configFile)
// db.InitCmd(rootCmd, &configFile)
humidity.InitCmd(rootCmd, &configFile)
pressure.InitCmd(rootCmd, &configFile)
rgbled.InitCmd(rootCmd, &configFile)

49
cmd/db/db.go Normal file
View File

@ -0,0 +1,49 @@
package db
import (
"context"
"log"
database "github.com/go-flucky/flucky/pkg/db"
"github.com/go-flucky/flucky/pkg/types"
"github.com/spf13/cobra"
)
var configFile *string
var dbCmd = &cobra.Command{
Use: "db",
Short: "Operates with the configured database",
Run: func(cmd *cobra.Command, args []string) {
postgresDB, err := database.New(database.DBOTypePostgres, "localhost", "5432", "postgres", "postgres", "postgres")
if err != nil {
log.Fatalf("%v", err)
}
ctx := context.Background()
devices := []*types.Device{
&types.Device{
DeviceID: "1684df26-bc72-4435-a4f9-74b24bdb286c",
DeviceName: "raspberr-pi",
},
&types.Device{
DeviceID: "1684df26-bc72-4435-a4f9-74b24bdb286c",
DeviceName: "raspberr-pi",
},
}
if err := postgresDB.InsertDevices(ctx, devices); err != nil {
log.Fatalln(err)
}
},
}
// Execute a
func InitCmd(cmd *cobra.Command, cnfFile *string) {
configFile = cnfFile
cmd.AddCommand(dbCmd)
}

17
docker-compose.yml Normal file
View File

@ -0,0 +1,17 @@
version: '3'
services:
flucky-db:
environment:
- PGTZ=${TZ}
- POSTGRES_PASSWORD=${PG_PASSWORD}
- POSTGRES_USER=${PG_USER}
- POSTGRES_DB=${PG_NAME}
- TZ=${TZ}
image: postgres:11.5-alpine
ports:
- ${PG_EXTERN_PORT}:${PG_INTERN_PORT}/tcp
restart: always
volumes:
- /etc/localtime:/etc/localtime:ro

1
go.mod
View File

@ -9,6 +9,7 @@ require (
github.com/go-flucky/go-dht v0.1.1
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/lib/pq v1.2.0
github.com/satori/go.uuid v1.2.0
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3 // indirect

2
go.sum
View File

@ -15,6 +15,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=

View File

@ -5,6 +5,7 @@ import (
"time"
"github.com/go-flucky/flucky/pkg/internal/format"
"github.com/go-flucky/flucky/pkg/rgbled"
"github.com/go-flucky/flucky/pkg/sensor"
@ -101,7 +102,7 @@ func (c *Configuration) AddSensor(sensor *types.Sensor) error {
}
// overwrite creation date
sensor.CreationDate = time.Now()
sensor.CreationDate = format.FormatedTime()
//TODO: check if wire sensor exists in /dev/bus/w1/devices

View File

@ -8,6 +8,7 @@ import (
"time"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/db"
"github.com/go-flucky/flucky/pkg/logfile"
"github.com/go-flucky/flucky/pkg/logger"
"github.com/go-flucky/flucky/pkg/rgbled"
@ -15,6 +16,14 @@ import (
"github.com/go-flucky/flucky/pkg/types"
)
var (
postgresHost = "markus-pc.trier.cryptic.systems"
postgresPort = "5432"
postgresDatabase = "postgres"
postgresUser = "postgres"
postgresPassword = "postgres"
)
// Start the daemon
func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compression bool, round float64, logger logger.Logger) {
@ -67,7 +76,9 @@ func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compress
logger.Error("Can not turn on blue info light: %v", err)
}
err = logfile.Append(measuredValuesLogfile, compression, round, measuredValuesCache)
// err = logfile.Append(measuredValuesLogfile, compression, round, measuredValuesCache)
postgres, err := db.New(db.DBOTypePostgres, postgresHost, postgresPort, postgresDatabase, postgresUser, postgresPassword)
if err != nil {
err = rgbled.Error(rgbLEDs)
@ -76,8 +87,22 @@ func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compress
}
cancel()
logger.Fatal("Can not save temperatures: %v", err)
logger.Error("Can not open database connection: %v", err)
}
postgresCtx := context.Background()
err = postgres.InsertMeasuredValues(postgresCtx, measuredValuesCache)
if err != nil {
err = rgbled.Error(rgbLEDs)
if err != nil {
logger.Error("Can not turn on red info light: %v", err)
}
cancel()
logger.Error("Can not save caches measured values in database: %v", err)
}
measuredValuesCache = make([]*types.MeasuredValue, 0)
case measuredValues, _ := <-measuredValuesChannel:

36
pkg/db/db.go Normal file
View File

@ -0,0 +1,36 @@
package db
import (
"database/sql"
"fmt"
_ "github.com/lib/pq"
)
type DBOType string
func (dboType DBOType) String() string {
return string(dboType)
}
const (
DBOTypePostgres DBOType = "postgres"
DBOTypeOracle = "oracle"
)
func New(dboType DBOType, host string, port string, database string, user string, password string) (Database, error) {
connStr := fmt.Sprintf("%v://%v:%v@%v:%v/%v?sslmode=disable", dboType.String(), user, password, host, port, database)
newDBO, err := sql.Open(dboType.String(), connStr)
if err != nil {
return nil, err
}
switch dboType {
case "postgres":
return &Postgres{
dbo: newDBO,
}, nil
default:
return nil, fmt.Errorf("Unknown Database Type")
}
}

16
pkg/db/errors.go Normal file
View File

@ -0,0 +1,16 @@
package db
import (
"errors"
)
var (
errorBeginTransaction = errors.New("Can not start new transaction")
errorRowNotFound = errors.New("Can not find row by given ID")
errorPrepareStatement = errors.New("Can not prepare sql statement")
errorRollbackTransaction = errors.New("Can not rollback transaction")
errorScanRow = errors.New("Can not scan row")
errorStatementExecute = errors.New("Can not execute statement")
errorStatementQuery = errors.New("Can not query statement")
errorUnknownMeasuredValueType = errors.New("Unknown measured value type")
)

39
pkg/db/interfaces.go Normal file
View File

@ -0,0 +1,39 @@
package db
import (
"context"
"github.com/go-flucky/flucky/pkg/types"
)
type Database interface {
// Close DB Connction
Close() error
// Delete
DeleteDevices(ctx context.Context, devices []*types.Device) error
DeleteMeasuredValues(ctx context.Context, measuredValues []*types.MeasuredValue) error
DeleteSensors(ctx context.Context, sensors []*types.Sensor) error
// Insert
InsertDevices(ctx context.Context, devices []*types.Device) error
InsertMeasuredValues(ctx context.Context, measuredValues []*types.MeasuredValue) error
InsertSensors(ctx context.Context, sensors []*types.Sensor) error
// Select
SelectDeviceByID(ctx context.Context, id string) (*types.Device, error)
SelectHumidities(ctx context.Context) ([]*types.MeasuredValue, error)
SelectHumidityByID(ctx context.Context, id string) (*types.MeasuredValue, error)
SelectMeasuredValuesByIDAndType(ctx context.Context, id string, valueType types.MeasuredValueType) (*types.MeasuredValue, error)
SelectPressures(ctx context.Context) ([]*types.MeasuredValue, error)
SelectPressureByID(ctx context.Context, id string) (*types.MeasuredValue, error)
SelectSensorByID(ctx context.Context, id string) (*types.Sensor, error)
SelectTemperatures(ctx context.Context) ([]*types.MeasuredValue, error)
SelectTemperatureByID(ctx context.Context, id string) (*types.MeasuredValue, error)
// Update
UpdateDevices(ctx context.Context, devices []*types.Device) error
UpdateMeasuredValues(ctx context.Context, measuredValues []*types.MeasuredValue) error
UpdateSensors(ctx context.Context, sensots []*types.Sensor) error
}

410
pkg/db/postgres.go Normal file
View File

@ -0,0 +1,410 @@
package db
import (
"context"
"database/sql"
"fmt"
"github.com/go-flucky/flucky/pkg/types"
_ "github.com/lib/pq"
)
type Postgres struct {
dbo *sql.DB
}
func (p *Postgres) Close() error {
return p.Close()
}
func (p *Postgres) DeleteDevices(ctx context.Context, devices []*types.Device) error {
query := string(MustAsset("pkg/db/sql/psql/deleteDevice.sql"))
stmt, err := p.dbo.PrepareContext(ctx, query)
if err != nil {
return fmt.Errorf("%v: %v", errorPrepareStatement, err)
}
defer stmt.Close()
for _, device := range devices {
_, err := stmt.ExecContext(ctx, &device.DeviceID)
if err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
}
return nil
}
func (p *Postgres) DeleteSensors(ctx context.Context, sensors []*types.Sensor) error {
query := string(MustAsset("pkg/db/sql/psql/deleteSensor.sql"))
stmt, err := p.dbo.PrepareContext(ctx, query)
if err != nil {
return fmt.Errorf("%v: %v", errorPrepareStatement, err)
}
defer stmt.Close()
for _, sensor := range sensors {
_, err := stmt.ExecContext(ctx, &sensor.SensorID)
if err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
}
return nil
}
func (p *Postgres) DeleteMeasuredValues(ctx context.Context, measuredValues []*types.MeasuredValue) error {
deleteMeasuredValue := func(ctx context.Context, query string, measuredValues []*types.MeasuredValue) error {
stmt, err := p.dbo.PrepareContext(ctx, query)
if err != nil {
return fmt.Errorf("%v: %v", errorPrepareStatement, err)
}
defer stmt.Close()
for _, measuredValue := range measuredValues {
_, err := stmt.ExecContext(ctx, &measuredValue.ID)
if err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
}
return nil
}
sortedMeasuredValueTypes := make(map[types.MeasuredValueType][]*types.MeasuredValue)
for _, measuredValue := range measuredValues {
if _, ok := sortedMeasuredValueTypes[measuredValue.ValueType]; !ok {
sortedMeasuredValueTypes[measuredValue.ValueType] = make([]*types.MeasuredValue, 0)
}
sortedMeasuredValueTypes[measuredValue.ValueType] = append(sortedMeasuredValueTypes[measuredValue.ValueType], measuredValue)
}
for measuredValueType, sortedMeasuredValues := range sortedMeasuredValueTypes {
switch measuredValueType {
case types.MeasuredValueTypeHumidity:
if err := deleteMeasuredValue(ctx, string(MustAsset("pkg/db/sql/psql/deleteHumidity.sql")), sortedMeasuredValues); err != nil {
return err
}
case types.MeasuredValueTypePressure:
if err := deleteMeasuredValue(ctx, string(MustAsset("pkg/db/sql/psql/deletePressure.sql")), sortedMeasuredValues); err != nil {
return err
}
case types.MeasuredValueTypeTemperature:
if err := deleteMeasuredValue(ctx, string(MustAsset("pkg/db/sql/psql/deleteTemperature.sql")), sortedMeasuredValues); err != nil {
return err
}
}
}
return nil
}
func (p *Postgres) InsertDevices(ctx context.Context, devices []*types.Device) error {
query := string(MustAsset("pkg/db/sql/psql/insertDevice.sql"))
stmt, err := p.dbo.PrepareContext(ctx, query)
if err != nil {
return fmt.Errorf("%v: %v", errorPrepareStatement, err)
}
defer stmt.Close()
for _, device := range devices {
_, err := stmt.ExecContext(ctx, &device.DeviceID, &device.DeviceName, &device.DeviceLocation, &device.CreationDate)
if err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
}
return nil
}
func (p *Postgres) InsertMeasuredValues(ctx context.Context, measuredValues []*types.MeasuredValue) error {
sortedMeasuredValueTypes := make(map[types.MeasuredValueType][]*types.MeasuredValue)
for _, measuredValue := range measuredValues {
if _, ok := sortedMeasuredValueTypes[measuredValue.ValueType]; !ok {
sortedMeasuredValueTypes[measuredValue.ValueType] = make([]*types.MeasuredValue, 0)
}
sortedMeasuredValueTypes[measuredValue.ValueType] = append(sortedMeasuredValueTypes[measuredValue.ValueType], measuredValue)
}
for measuredValueType, sortedMeasuredValues := range sortedMeasuredValueTypes {
switch measuredValueType {
case types.MeasuredValueTypeHumidity:
if err := p.insertHumidity(ctx, sortedMeasuredValues); err != nil {
return err
}
case types.MeasuredValueTypePressure:
if err := p.insertPressure(ctx, sortedMeasuredValues); err != nil {
return err
}
case types.MeasuredValueTypeTemperature:
if err := p.insertTemperature(ctx, sortedMeasuredValues); err != nil {
return err
}
}
}
return nil
}
func (p *Postgres) insertHumidity(ctx context.Context, measuredValues []*types.MeasuredValue) error {
query := string(MustAsset("pkg/db/sql/psql/insertHumidity.sql"))
stmt, err := p.dbo.PrepareContext(ctx, query)
if err != nil {
return fmt.Errorf("%v: %v", errorPrepareStatement, err)
}
defer stmt.Close()
for _, measuredValue := range measuredValues {
if measuredValue.ValueType != types.MeasuredValueTypeHumidity {
continue
}
_, err := stmt.ExecContext(ctx, &measuredValue.ID, &measuredValue.Value, &measuredValue.FromDate, &measuredValue.TillDate, &measuredValue.SensorID, &measuredValue.CreationDate, &measuredValue.UpdateDate)
if err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
}
return nil
}
func (p *Postgres) insertPressure(ctx context.Context, measuredValues []*types.MeasuredValue) error {
query := string(MustAsset("pkg/db/sql/psql/insertPressure.sql"))
stmt, err := p.dbo.PrepareContext(ctx, query)
if err != nil {
return fmt.Errorf("%v: %v", errorPrepareStatement, err)
}
defer stmt.Close()
for _, measuredValue := range measuredValues {
if measuredValue.ValueType != types.MeasuredValueTypePressure {
continue
}
_, err := stmt.ExecContext(ctx, &measuredValue.ID, &measuredValue.Value, &measuredValue.FromDate, &measuredValue.TillDate, &measuredValue.SensorID, &measuredValue.CreationDate, &measuredValue.UpdateDate)
if err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
}
return nil
}
func (p *Postgres) insertTemperature(ctx context.Context, measuredValues []*types.MeasuredValue) error {
query := string(MustAsset("pkg/db/sql/psql/insertTemperature.sql"))
stmt, err := p.dbo.PrepareContext(ctx, query)
if err != nil {
return fmt.Errorf("%v: %v", errorPrepareStatement, err)
}
defer stmt.Close()
for _, measuredValue := range measuredValues {
if measuredValue.ValueType != types.MeasuredValueTypeTemperature {
continue
}
_, err := stmt.ExecContext(ctx, &measuredValue.ID, &measuredValue.Value, &measuredValue.FromDate, &measuredValue.TillDate, &measuredValue.SensorID, &measuredValue.CreationDate, &measuredValue.UpdateDate)
if err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
}
return nil
}
func (p *Postgres) InsertSensors(ctx context.Context, sensors []*types.Sensor) error {
query := string(MustAsset("pkg/db/sql/psql/insertSensor.sql"))
stmt, err := p.dbo.PrepareContext(ctx, query)
if err != nil {
return fmt.Errorf("%v: %v", errorPrepareStatement, err)
}
defer stmt.Close()
for _, sensor := range sensors {
_, err := stmt.ExecContext(ctx, &sensor.SensorID, &sensor.SensorName, &sensor.SensorLocation, &sensor.WireID, &sensor.I2CBus, &sensor.I2CAddress, &sensor.GPIONumber, &sensor.SensorModel, &sensor.SensorEnabled, &sensor.DeviceID, &sensor.CreationDate)
if err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
}
return nil
}
func (p *Postgres) SelectDeviceByID(ctx context.Context, id string) (*types.Device, error) {
query := string(MustAsset("pkg/db/sql/psql/selectDeviceByID.sql"))
stmt, err := p.dbo.PrepareContext(ctx, query)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorPrepareStatement, err)
}
row := stmt.QueryRowContext(ctx, id)
if row == nil {
return nil, errorRowNotFound
}
device := new(types.Device)
err = row.Scan(&device.DeviceID, &device.DeviceName, &device.DeviceLocation, &device.CreationDate)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorScanRow, err)
}
return device, nil
}
func (p *Postgres) SelectMeasuredValuesByIDAndType(ctx context.Context, id string, valueType types.MeasuredValueType) (*types.MeasuredValue, error) {
switch valueType {
case types.MeasuredValueTypeHumidity:
return p.SelectHumidityByID(ctx, id)
case types.MeasuredValueTypePressure:
return p.SelectPressureByID(ctx, id)
case types.MeasuredValueTypeTemperature:
return p.SelectTemperatureByID(ctx, id)
default:
return nil, fmt.Errorf("%v: %v", errorUnknownMeasuredValueType, valueType)
}
}
func (p *Postgres) SelectHumidities(ctx context.Context) ([]*types.MeasuredValue, error) {
queryFile := "pkg/db/sql/psql/selectHumidities.sql"
measuredValues, err := p.selectMeasuredValues(ctx, types.MeasuredValueTypeHumidity, queryFile, nil)
if err != nil {
return nil, err
}
return measuredValues, nil
}
func (p *Postgres) SelectHumidityByID(ctx context.Context, id string) (*types.MeasuredValue, error) {
queryFile := "pkg/db/sql/psql/selectHumidityByID.sql"
args := []interface{}{id}
measuredValues, err := p.selectMeasuredValues(ctx, types.MeasuredValueTypeHumidity, queryFile, args)
if err != nil {
return nil, err
}
if len(measuredValues) == 0 {
return nil, fmt.Errorf("%v: %v", errorRowNotFound, id)
}
return measuredValues[0], nil
}
func (p *Postgres) SelectPressures(ctx context.Context) ([]*types.MeasuredValue, error) {
queryFile := "pkg/db/sql/psql/selectPressures.sql"
measuredValues, err := p.selectMeasuredValues(ctx, types.MeasuredValueTypePressure, queryFile, nil)
if err != nil {
return nil, err
}
return measuredValues, nil
}
func (p *Postgres) SelectPressureByID(ctx context.Context, id string) (*types.MeasuredValue, error) {
queryFile := "pkg/db/sql/psql/selectPressureByID.sql"
args := []interface{}{id}
measuredValues, err := p.selectMeasuredValues(ctx, types.MeasuredValueTypePressure, queryFile, args)
if err != nil {
return nil, err
}
if len(measuredValues) == 0 {
return nil, fmt.Errorf("%v: %v", errorRowNotFound, id)
}
return measuredValues[0], nil
}
func (p *Postgres) SelectSensorByID(ctx context.Context, id string) (*types.Sensor, error) {
query := string(MustAsset("pkg/db/sql/psql/selectSensorByID.sql"))
stmt, err := p.dbo.PrepareContext(ctx, query)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorPrepareStatement, err)
}
row := stmt.QueryRowContext(ctx, id)
if row == nil {
return nil, errorRowNotFound
}
sensor := new(types.Sensor)
err = row.Scan(&sensor.SensorID, &sensor.SensorName, &sensor.SensorLocation, &sensor.WireID, &sensor.I2CBus, &sensor.I2CAddress, &sensor.GPIONumber, &sensor.SensorModel, &sensor.SensorEnabled, &sensor.DeviceID, &sensor.CreationDate)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorScanRow, err)
}
return sensor, nil
}
func (p *Postgres) SelectTemperatures(ctx context.Context) ([]*types.MeasuredValue, error) {
queryFile := "pkg/db/sql/psql/selectTemperatures.sql"
measuredValues, err := p.selectMeasuredValues(ctx, types.MeasuredValueTypeTemperature, queryFile, nil)
if err != nil {
return nil, err
}
return measuredValues, nil
}
func (p *Postgres) SelectTemperatureByID(ctx context.Context, id string) (*types.MeasuredValue, error) {
queryFile := "pkg/db/sql/psql/selectTemperatureByID.sql"
args := []interface{}{id}
measuredValues, err := p.selectMeasuredValues(ctx, types.MeasuredValueTypeTemperature, queryFile, args)
if err != nil {
return nil, err
}
if len(measuredValues) == 0 {
return nil, fmt.Errorf("%v: %v", errorRowNotFound, id)
}
return measuredValues[0], nil
}
func (p *Postgres) selectMeasuredValues(ctx context.Context, measuredValueType types.MeasuredValueType, queryFile string, queryArgs []interface{}) ([]*types.MeasuredValue, error) {
query := string(MustAsset(queryFile))
stmt, err := p.dbo.PrepareContext(ctx, query)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorPrepareStatement, err)
}
rows, err := stmt.QueryContext(ctx, queryArgs...)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorStatementQuery, err)
}
measuredValues := make([]*types.MeasuredValue, 0)
for rows.Next() {
measuredValue := new(types.MeasuredValue)
measuredValue.ValueType = measuredValueType
rows.Scan(&measuredValue.ID, &measuredValue.Value, &measuredValue.FromDate, &measuredValue.TillDate, &measuredValue.SensorID, &measuredValue.CreationDate, &measuredValue.UpdateDate)
measuredValues = append(measuredValues, measuredValue)
}
return measuredValues, nil
}
func (p *Postgres) UpdateDevices(ctx context.Context, devices []*types.Device) error {
return nil
}
func (p *Postgres) UpdateMeasuredValues(ctx context.Context, measuredValues []*types.MeasuredValue) error {
return nil
}
func (p *Postgres) UpdateSensors(ctx context.Context, sensots []*types.Sensor) error {
return nil
}

273
pkg/db/postgres_test.go Normal file
View File

@ -0,0 +1,273 @@
package db_test
import (
"context"
"testing"
"github.com/go-flucky/flucky/pkg/db"
"github.com/go-flucky/flucky/pkg/types"
"github.com/go-flucky/flucky/test/goldenfiles"
"github.com/stretchr/testify/require"
)
type test struct {
Name string
Test func(*testing.T)
}
var (
database db.Database
postgresContainerImage string = "docker.io/postgres/postgres"
postgresHost string = "localhost"
postgresPort string = "5432"
postgresUser string = "postgres"
postgresPassword string = "postgres"
postgresDatabase string = "postgres"
goldenDevicesFilePath string = "test/goldenfiles/json/goldenDevices.json"
goldenSensorsFilePath string = "test/goldenfiles/json/goldenSensors.json"
goldenMeasuredValuesFilePath string = "test/goldenfiles/json/goldenMeasuredValues.json"
goldenPressuresFilePath string = "test/goldenfiles/json/goldenPressures.json"
goldenHumiditiesFilePath string = "test/goldenfiles/json/goldenHumidities.json"
goldenTemperaturesFilePath string = "test/goldenfiles/json/goldenTemperatures.json"
goldenDevices []*types.Device
goldenSensors []*types.Sensor
goldenMeasuredValues []*types.MeasuredValue
goldenPressures []*types.MeasuredValue
goldenHumidites []*types.MeasuredValue
goldenTemperatures []*types.MeasuredValue
)
func load(t *testing.T) {
require := require.New(t)
d, err := goldenfiles.GetGoldenDevices(goldenDevicesFilePath)
require.NoError(err)
goldenDevices = d
s, err := goldenfiles.GetGoldenSensors(goldenSensorsFilePath)
require.NoError(err)
goldenSensors = s
hum, err := goldenfiles.GetGoldenMeasuredValues(goldenHumiditiesFilePath)
require.NoError(err)
goldenHumidites = hum
mv, err := goldenfiles.GetGoldenMeasuredValues(goldenMeasuredValuesFilePath)
require.NoError(err)
goldenMeasuredValues = mv
pres, err := goldenfiles.GetGoldenMeasuredValues(goldenPressuresFilePath)
require.NoError(err)
goldenPressures = pres
temp, err := goldenfiles.GetGoldenMeasuredValues(goldenTemperaturesFilePath)
require.NoError(err)
goldenTemperatures = temp
}
func TestPostgres(t *testing.T) {
require := require.New(t)
load(t)
db, err := db.New(db.DBOTypePostgres, postgresHost, postgresPort, postgresDatabase, postgresUser, postgresPassword)
database = db
require.Nil(err)
tests := []*test{
&test{
Name: "insertDevices",
Test: testInsertDevices,
},
&test{
Name: "insertSensors",
Test: testInsertSensors,
},
&test{
Name: "insertHumidity",
Test: testInsertHumidity,
},
&test{
Name: "insertPressure",
Test: testInsertPressure,
},
&test{
Name: "insertTemperatures",
Test: testInsertTemperatures,
},
&test{
Name: "deleteHumidities",
Test: testDeleteHumidity,
},
&test{
Name: "deletePressures",
Test: testDeletePressures,
},
&test{
Name: "deleteTemperatures",
Test: testDeleteTemperatures,
},
&test{
Name: "insertMeasuredValues",
Test: testInsertMeasuredValues,
},
&test{
Name: "deleteMeasuredValues",
Test: testDeleteMeasuredValues,
},
&test{
Name: "deleteSensors",
Test: testDeleteSensors,
},
&test{
Name: "deleteDevices",
Test: testDeleteDevices,
},
}
for _, test := range tests {
t.Run(test.Name, test.Test)
}
}
func testInsertDevices(t *testing.T) {
require := require.New(t)
ctx := context.Background()
err := database.InsertDevices(ctx, goldenDevices)
require.NoError(err)
for _, goldenDevice := range goldenDevices {
testDevice, err := database.SelectDeviceByID(ctx, goldenDevice.DeviceID)
require.NoError(err)
goldenfiles.CompareMeasuredValues(t, goldenDevice, testDevice)
}
}
func testInsertSensors(t *testing.T) {
require := require.New(t)
ctx := context.Background()
err := database.InsertSensors(ctx, goldenSensors)
require.NoError(err)
for _, goldenSensor := range goldenSensors {
testSensor, err := database.SelectSensorByID(ctx, goldenSensor.SensorID)
require.NoError(err)
goldenfiles.CompareMeasuredValues(t, goldenSensor, testSensor)
}
}
func testInsertHumidity(t *testing.T) {
require := require.New(t)
ctx := context.Background()
err := database.InsertMeasuredValues(ctx, goldenHumidites)
require.NoError(err)
for _, goldenHumidity := range goldenHumidites {
testHumidity, err := database.SelectHumidityByID(ctx, goldenHumidity.ID)
require.NoError(err)
goldenfiles.CompareMeasuredValues(t, []*types.MeasuredValue{testHumidity}, []*types.MeasuredValue{testHumidity})
}
}
func testInsertMeasuredValues(t *testing.T) {
require := require.New(t)
ctx := context.Background()
err := database.InsertMeasuredValues(ctx, goldenMeasuredValues)
require.NoError(err)
for _, goldenMeasuredValue := range goldenMeasuredValues {
testMeasuredValue, err := database.SelectMeasuredValuesByIDAndType(ctx, goldenMeasuredValue.ID, goldenMeasuredValue.ValueType)
require.NoError(err)
goldenfiles.CompareMeasuredValues(t, []*types.MeasuredValue{goldenMeasuredValue}, []*types.MeasuredValue{testMeasuredValue})
}
}
func testInsertPressure(t *testing.T) {
require := require.New(t)
ctx := context.Background()
err := database.InsertMeasuredValues(ctx, goldenPressures)
require.NoError(err)
for _, goldenPressure := range goldenPressures {
testPressure, err := database.SelectPressureByID(ctx, goldenPressure.ID)
require.NoError(err)
goldenfiles.CompareMeasuredValues(t, []*types.MeasuredValue{testPressure}, []*types.MeasuredValue{testPressure})
}
}
func testInsertTemperatures(t *testing.T) {
require := require.New(t)
ctx := context.Background()
err := database.InsertMeasuredValues(ctx, goldenTemperatures)
require.NoError(err)
for _, goldenTemperature := range goldenTemperatures {
testTemperature, err := database.SelectTemperatureByID(ctx, goldenTemperature.ID)
require.NoError(err)
goldenfiles.CompareMeasuredValues(t, []*types.MeasuredValue{goldenTemperature}, []*types.MeasuredValue{testTemperature})
}
}
func testDeleteDevices(t *testing.T) {
require := require.New(t)
ctx := context.Background()
err := database.DeleteDevices(ctx, goldenDevices)
require.NoError(err)
for _, goldenDevice := range goldenDevices {
_, err := database.SelectDeviceByID(ctx, goldenDevice.DeviceID)
require.Error(err)
}
}
func testDeleteSensors(t *testing.T) {
require := require.New(t)
ctx := context.Background()
err := database.DeleteSensors(ctx, goldenSensors)
require.NoError(err)
for _, goldenSensor := range goldenSensors {
_, err := database.SelectDeviceByID(ctx, goldenSensor.SensorID)
require.Error(err)
}
}
func testDeleteHumidity(t *testing.T) {
require := require.New(t)
ctx := context.Background()
err := database.DeleteMeasuredValues(ctx, goldenHumidites)
require.NoError(err)
for _, goldenHumidity := range goldenHumidites {
_, err := database.SelectHumidityByID(ctx, goldenHumidity.ID)
require.Error(err)
}
}
func testDeleteMeasuredValues(t *testing.T) {
require := require.New(t)
ctx := context.Background()
err := database.DeleteMeasuredValues(ctx, goldenMeasuredValues)
require.NoError(err)
for _, goldenMeasuredValue := range goldenMeasuredValues {
_, err := database.SelectPressureByID(ctx, goldenMeasuredValue.ID)
require.Error(err)
}
}
func testDeletePressures(t *testing.T) {
require := require.New(t)
ctx := context.Background()
err := database.DeleteMeasuredValues(ctx, goldenPressures)
require.NoError(err)
for _, goldenPressure := range goldenPressures {
_, err := database.SelectPressureByID(ctx, goldenPressure.ID)
require.Error(err)
}
}
func testDeleteTemperatures(t *testing.T) {
require := require.New(t)
ctx := context.Background()
err := database.DeleteMeasuredValues(ctx, goldenTemperatures)
require.NoError(err)
for _, goldenTemperature := range goldenTemperatures {
_, err := database.SelectTemperatureByID(ctx, goldenTemperature.ID)
require.Error(err)
}
}

View File

@ -0,0 +1,2 @@
DELETE FROM devices
WHERE device_id = $1;

View File

@ -0,0 +1,2 @@
DELETE FROM humidities
WHERE humidity_id = $1;

View File

@ -0,0 +1,2 @@
DELETE FROM pressures
WHERE pressure_id = $1;

View File

@ -0,0 +1,2 @@
DELETE FROM sensors
WHERE sensor_id = $1;

View File

@ -0,0 +1,2 @@
DELETE FROM temperatures
WHERE temperature_id = $1;

View File

@ -0,0 +1,7 @@
INSERT INTO devices (
device_id,
device_name,
device_location,
creation_date
)
VALUES ($1, $2, $3, $4);

View File

@ -0,0 +1,10 @@
INSERT INTO humidities (
humidity_id,
humidity_value,
humidity_from_date,
humidity_till_date,
sensor_id,
creation_date,
update_date
)
VALUES ($1, $2, $3, $4, $5, $6, $7);

View File

@ -0,0 +1,10 @@
INSERT INTO pressures (
pressure_id,
pressure_value,
pressure_from_date,
pressure_till_date,
sensor_id,
creation_date,
update_date
)
VALUES ($1, $2, $3, $4, $5, $6, $7);

View File

@ -0,0 +1,14 @@
INSERT INTO sensors (
sensor_id,
sensor_name,
sensor_location,
wire_id,
i2c_bus,
i2c_address,
gpio_number,
sensor_model,
sensor_enabled,
device_id,
creation_date
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11);

View File

@ -0,0 +1,10 @@
INSERT INTO temperatures (
temperature_id,
temperature_value,
temperature_from_date,
temperature_till_date,
sensor_id,
creation_date,
update_date
)
VALUES ($1, $2, $3, $4, $5, $6, $7);

146
pkg/db/sql/psql/schema.sql Normal file
View File

@ -0,0 +1,146 @@
DROP TABLE IF EXISTS devices CASCADE;
DROP TABLE IF EXISTS sensors CASCADE;
DROP TABLE IF EXISTS humidities CASCADE;
DROP TABLE IF EXISTS pressures CASCADE;
DROP TABLE IF EXISTS temperatures CASCADE;
-- +----------------------------------------+
-- | TABLES |
-- +----------------------------------------+
CREATE TABLE IF NOT EXISTS devices(
device_id CHAR(36) CONSTRAINT pk_devices PRIMARY KEY,
device_name VARCHAR(32) NOT NULL,
device_location VARCHAR(32),
device_last_contact TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
creation_date TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS sensors (
sensor_id CHAR(36) CONSTRAINT pk_sensors PRIMARY KEY,
sensor_name VARCHAR(32) NOT NULL,
sensor_location VARCHAR(32) NOT NULL,
wire_id VARCHAR(15),
i2c_bus VARCHAR(255),
i2c_address VARCHAR(12),
gpio_number VARCHAR(6),
sensor_model VARCHAR(16) NOT NULL,
sensor_enabled BOOLEAN DEFAULT TRUE NOT NULL,
sensor_last_contact TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
device_id CHAR(36),
creation_date TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL
);
CREATE TABLE IF NOT EXISTS humidities (
humidity_id CHAR(36) CONSTRAINT pk_humidities PRIMARY KEY,
humidity_value NUMERIC(9,3) NOT NULL,
humidity_from_date TIMESTAMP WITH TIME ZONE NOT NULL,
humidity_till_date TIMESTAMP WITH TIME ZONE,
sensor_id CHAR(36),
creation_date TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
update_date TIMESTAMP WITH TIME ZONE
);
CREATE TABLE IF NOT EXISTS pressures (
pressure_id CHAR(36) CONSTRAINT pk_pressures PRIMARY KEY,
pressure_value NUMERIC(10,3) NOT NULL,
pressure_from_date TIMESTAMP WITH TIME ZONE NOT NULL,
pressure_till_date TIMESTAMP WITH TIME ZONE,
sensor_id CHAR(36),
creation_date TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
update_date TIMESTAMP WITH TIME ZONE
);
CREATE TABLE IF NOT EXISTS temperatures (
temperature_id CHAR(36) CONSTRAINT pk_temperatures PRIMARY KEY,
temperature_value NUMERIC(5,3) NOT NULL,
temperature_from_date TIMESTAMP WITH TIME ZONE NOT NULL,
temperature_till_date TIMESTAMP WITH TIME ZONE,
sensor_id CHAR(36),
creation_date TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
update_date TIMESTAMP WITH TIME ZONE
);
-- +----------------------------------------+
-- | FOREIGN-KEYS |
-- +----------------------------------------+
ALTER TABLE sensors
ADD FOREIGN KEY (device_id)
REFERENCES devices(device_id)
ON DELETE CASCADE
ON UPDATE CASCADE;
ALTER TABLE humidities
ADD FOREIGN KEY (sensor_id)
REFERENCES sensors(sensor_id)
ON DELETE CASCADE
ON UPDATE CASCADE;
ALTER TABLE pressures
ADD FOREIGN KEY (sensor_id)
REFERENCES sensors(sensor_id)
ON DELETE CASCADE
ON UPDATE CASCADE;
ALTER TABLE temperatures
ADD FOREIGN KEY (sensor_id)
REFERENCES sensors(sensor_id)
ON DELETE CASCADE
ON UPDATE CASCADE;
-- +----------------------------------------+
-- | Trigger-Functions |
-- +----------------------------------------+
CREATE OR REPLACE FUNCTION device_last_contact()
RETURNS trigger AS
$BODY$
BEGIN
UPDATE devices
SET device_last_contact = CURRENT_TIMESTAMP
WHERE device_id = NEW.device_id;
RETURN NEW;
END;
$BODY$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION sensor_last_contact()
RETURNS trigger AS
$BODY$
BEGIN
UPDATE sensors
SET sensor_last_contact = CURRENT_TIMESTAMP,
sensor_enabled = true
WHERE sensor_id = NEW.sensor_id;
RETURN NEW;
END;
$BODY$ LANGUAGE plpgsql;
-- +----------------------------------------+
-- | Trigger |
-- +----------------------------------------+
DROP TRIGGER IF EXISTS ai_humidities ON humidities;
DROP TRIGGER IF EXISTS ai_pressure ON pressures;
DROP TRIGGER IF EXISTS ai_temperatures ON temperatures;
CREATE TRIGGER au_sensors
AFTER UPDATE
ON sensors
FOR EACH ROW
EXECUTE PROCEDURE device_last_contact();
CREATE TRIGGER ai_humidities
AFTER INSERT
ON humidities
FOR EACH ROW
EXECUTE PROCEDURE sensor_last_contact();
CREATE TRIGGER ai_pressures
AFTER INSERT
ON pressures
FOR EACH ROW
EXECUTE PROCEDURE sensor_last_contact();
CREATE TRIGGER ai_temperatures
AFTER INSERT
ON temperatures
FOR EACH ROW
EXECUTE PROCEDURE sensor_last_contact();

View File

@ -0,0 +1,8 @@
SELECT
device_id,
device_name,
device_location,
creation_date
FROM
devices
WHERE device_id = $1;

View File

@ -0,0 +1,10 @@
SELECT
humidity_id,
humidity_value,
humidity_from_date,
humidity_till_date,
sensor_id,
creation_date,
update_date
FROM
humidities;

View File

@ -0,0 +1,12 @@
SELECT
humidity_id,
humidity_value,
humidity_from_date,
humidity_till_date,
sensor_id,
creation_date,
update_date
FROM
humidities
WHERE
humidity_id = $1;

View File

@ -0,0 +1,12 @@
SELECT
pressure_id,
pressure_value,
pressure_from_date,
pressure_till_date,
sensor_id,
creation_date,
update_date
FROM
pressures
WHERE
pressure_id = $1;

View File

@ -0,0 +1,10 @@
SELECT
pressure_id,
pressure_value,
pressure_from_date,
pressure_till_date,
sensor_id,
creation_date,
update_date
FROM
pressures;

View File

@ -0,0 +1,16 @@
SELECT
sensor_id,
sensor_name,
sensor_location,
wire_id,
i2c_bus,
i2c_address,
gpio_number,
sensor_model,
sensor_enabled,
device_id,
creation_date
FROM
sensors
WHERE
sensor_id = $1;

View File

@ -0,0 +1,12 @@
SELECT
temperature_id,
temperature_value,
temperature_from_date,
temperature_till_date,
sensor_id,
creation_date,
update_date
FROM
temperatures
WHERE
temperature_id = $1;

View File

@ -0,0 +1,10 @@
SELECT
temperature_id,
temperature_value,
temperature_from_date,
temperature_till_date,
sensor_id,
creation_date,
update_date
FROM
temperatures;

View File

@ -0,0 +1,23 @@
package format
import (
"errors"
"math"
"time"
)
var (
errorPraseTime = errors.New("Can not parse time")
TimeFormat = "2006-01-02T15:04:05.999999Z07:00"
)
// FormatedTime returns a current timestamp without nano seconds. Postgres
// currently does not support nanoseconds which is automatically include into
// the go time object
func FormatedTime() time.Time {
t := time.Now()
l, _ := time.LoadLocation("Europe/Berlin")
return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), int(math.Round(float64(t.Nanosecond())/1000000)*1000000), l)
}

View File

@ -7,6 +7,7 @@ import (
"strconv"
"time"
"github.com/go-flucky/flucky/pkg/internal/format"
"github.com/go-flucky/flucky/pkg/types"
)
@ -50,7 +51,7 @@ func (cl *csvLogfile) Read() ([]*types.MeasuredValue, error) {
// Times
times := make([]time.Time, 0)
for _, i := range []int{3, 4} {
time, err := time.Parse(timeFormat, record[i])
time, err := time.Parse(format.TimeFormat, record[i])
if err != nil {
return nil, fmt.Errorf("%v %v: %v", errorParseTime, record[i], err)
}
@ -67,14 +68,14 @@ func (cl *csvLogfile) Read() ([]*types.MeasuredValue, error) {
}
// Creation date
creationDate, err := time.Parse(timeFormat, record[6])
creationDate, err := time.Parse(format.TimeFormat, record[6])
if err != nil {
return nil, fmt.Errorf("%v %v: %v", errorParseTime, record[6], err)
}
measuredValue.CreationDate = &creationDate
measuredValue.CreationDate = creationDate
if record[7] != "null" {
updateDate, err := time.Parse(timeFormat, record[7])
updateDate, err := time.Parse(format.TimeFormat, record[7])
if err != nil {
return nil, fmt.Errorf("%v %v: %v", errorParseTime, record[7], err)
}
@ -106,19 +107,15 @@ func (cl *csvLogfile) Write(measuredValues []*types.MeasuredValue) error {
measuredValue.ID,
fmt.Sprintf("%v", measuredValue.ValueType),
fmt.Sprintf("%v", measuredValue.Value),
measuredValue.FromDate.Format(timeFormat),
measuredValue.TillDate.Format(timeFormat),
measuredValue.FromDate.Format(format.TimeFormat),
measuredValue.TillDate.Format(format.TimeFormat),
measuredValue.SensorID,
}
if measuredValue.CreationDate != nil {
record = append(record, measuredValue.CreationDate.Format(timeFormat))
} else {
record = append(record, "null")
}
record = append(record, measuredValue.CreationDate.Format(format.TimeFormat))
if measuredValue.UpdateDate != nil {
record = append(record, measuredValue.UpdateDate.Format(timeFormat))
record = append(record, measuredValue.UpdateDate.Format(format.TimeFormat))
} else {
record = append(record, "null")
}

View File

@ -2,25 +2,27 @@ package logfile
import "errors"
var errorLogfileCreate = errors.New("Can not create logfile")
var errorLogfileDecode = errors.New("Can not decode from reader")
var errorLogfileEncode = errors.New("Can not encode from writer")
var errorLogfileMarshal = errors.New("Can not marshal values")
var errorLogfileNotFound = errors.New("Can not find logfile")
var errorLogfileOpen = errors.New("Can not open logfile")
var errorLogfileRead = errors.New("Can not read from given reader")
var errorLogfileUnmarshal = errors.New("Can not unmarshal values")
var errorLogfileWrite = errors.New("Can not write with given writer")
var (
errorLogfileCreate = errors.New("Can not create logfile")
errorLogfileDecode = errors.New("Can not decode from reader")
errorLogfileEncode = errors.New("Can not encode from writer")
errorLogfileMarshal = errors.New("Can not marshal values")
errorLogfileNotFound = errors.New("Can not find logfile")
errorLogfileOpen = errors.New("Can not open logfile")
errorLogfileRead = errors.New("Can not read from given reader")
errorLogfileUnmarshal = errors.New("Can not unmarshal values")
errorLogfileWrite = errors.New("Can not write with given writer")
var errorParseFloat = errors.New("Can not parse float")
var errorParseMeasurementUnit = errors.New("Can not parse mesaurement unit")
var errorParseTime = errors.New("Can not parse time")
errorParseFloat = errors.New("Can not parse float")
errorParseMeasurementUnit = errors.New("Can not parse mesaurement unit")
errorParseTime = errors.New("Can not parse time")
var errorNoValidHumidityID = errors.New("No valid humidity id detected or available")
var errorNoValidMesuredValue = errors.New("No mesured value detected or available")
var errorNoValidSensorID = errors.New("No sensor id detected or available")
var errorNoValidTemperatureID = errors.New("No valid temperature id detected or available")
var errorNoValidTime = errors.New("No time detected or available")
var errorNoValidTimePeriods = errors.New("No valid time periods")
errorNoValidHumidityID = errors.New("No valid humidity id detected or available")
errorNoValidMesuredValue = errors.New("No mesured value detected or available")
errorNoValidSensorID = errors.New("No sensor id detected or available")
errorNoValidTemperatureID = errors.New("No valid temperature id detected or available")
errorNoValidTime = errors.New("No time detected or available")
errorNoValidTimePeriods = errors.New("No valid time periods")
var errorTypeSwitch = errors.New("Can not detect type via type switch")
errorTypeSwitch = errors.New("Can not detect type via type switch")
)

View File

@ -4,13 +4,12 @@ import (
"math"
"path/filepath"
"sort"
"time"
"github.com/go-flucky/flucky/pkg/internal/format"
"github.com/go-flucky/flucky/pkg/types"
)
// var validUUID = regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$")
var timeFormat = "2006-01-02T15:04:05.999999Z07:00"
// Append adds an array of several measured values to a logfile
func Append(logfile Logfile, compression bool, round float64, measuredValues []*types.MeasuredValue) error {
@ -56,7 +55,7 @@ func Compression(measuredValues []*types.MeasuredValue) []*types.MeasuredValue {
return measuredValues[i].FromDate.Before(measuredValues[j].TillDate)
})
now := time.Now()
now := format.FormatedTime()
for _, measuredValue := range measuredValues {
if _, ok := lastMeasuredValuesBySensors[measuredValue.SensorID]; !ok {
@ -124,11 +123,10 @@ func New(logfile string) Logfile {
}
func writeCreationDate(measuredValues []*types.MeasuredValue) {
now := time.Now()
func writeCreationDate(measuredValues []*types.MeasuredValue) error {
for _, measuredValue := range measuredValues {
if measuredValue.CreationDate == nil {
measuredValue.CreationDate = &now
}
now := format.FormatedTime()
measuredValue.CreationDate = now
}
return nil
}

View File

@ -5,11 +5,11 @@ import (
"fmt"
"log"
"sync"
"time"
"github.com/d2r2/go-bsbmp"
"github.com/d2r2/go-i2c"
"github.com/d2r2/go-logger"
"github.com/go-flucky/flucky/pkg/internal/format"
"github.com/go-flucky/flucky/pkg/types"
uuid "github.com/satori/go.uuid"
)
@ -67,24 +67,24 @@ func (s *BME280) Read() ([]*types.MeasuredValue, error) {
ID: uuid.NewV4().String(),
Value: float64(humidityValue),
ValueType: types.MeasuredValueTypeHumidity,
FromDate: time.Now(),
TillDate: time.Now(),
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
&types.MeasuredValue{
ID: uuid.NewV4().String(),
Value: float64(pressureValue),
ValueType: types.MeasuredValueTypePressure,
FromDate: time.Now(),
TillDate: time.Now(),
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
&types.MeasuredValue{
ID: uuid.NewV4().String(),
Value: float64(temperatureValue),
ValueType: types.MeasuredValueTypeTemperature,
FromDate: time.Now(),
TillDate: time.Now(),
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
}

View File

@ -4,8 +4,8 @@ import (
"context"
"fmt"
"sync"
"time"
"github.com/go-flucky/flucky/pkg/internal/format"
"github.com/go-flucky/flucky/pkg/types"
"github.com/go-flucky/go-dht"
uuid "github.com/satori/go.uuid"
@ -49,16 +49,16 @@ func (s *DHT11) Read() ([]*types.MeasuredValue, error) {
ID: uuid.NewV4().String(),
Value: float64(humidityValue),
ValueType: types.MeasuredValueTypeHumidity,
FromDate: time.Now(),
TillDate: time.Now(),
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
&types.MeasuredValue{
ID: uuid.NewV4().String(),
Value: float64(temperatureValue),
ValueType: types.MeasuredValueTypeTemperature,
FromDate: time.Now(),
TillDate: time.Now(),
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
}

View File

@ -4,8 +4,8 @@ import (
"context"
"fmt"
"sync"
"time"
"github.com/go-flucky/flucky/pkg/internal/format"
"github.com/go-flucky/flucky/pkg/types"
"github.com/go-flucky/go-dht"
uuid "github.com/satori/go.uuid"
@ -49,16 +49,16 @@ func (s *DHT22) Read() ([]*types.MeasuredValue, error) {
ID: uuid.NewV4().String(),
Value: float64(humidityValue),
ValueType: types.MeasuredValueTypeHumidity,
FromDate: time.Now(),
TillDate: time.Now(),
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
&types.MeasuredValue{
ID: uuid.NewV4().String(),
Value: float64(temperatureValue),
ValueType: types.MeasuredValueTypeTemperature,
FromDate: time.Now(),
TillDate: time.Now(),
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
}

View File

@ -8,8 +8,8 @@ import (
"strconv"
"strings"
"sync"
"time"
"github.com/go-flucky/flucky/pkg/internal/format"
"github.com/go-flucky/flucky/pkg/types"
uuid "github.com/satori/go.uuid"
)
@ -55,8 +55,8 @@ func (s *DS18B20) Read() ([]*types.MeasuredValue, error) {
ID: uuid.NewV4().String(),
Value: float64(temperatureValue),
ValueType: types.MeasuredValueTypeTemperature,
FromDate: time.Now(),
TillDate: time.Now(),
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
}

View File

@ -7,7 +7,7 @@ type Device struct {
DeviceID string `json:"device_id" xml:"device_id"`
DeviceName string `json:"device_name" xml:"device_name"`
DeviceLocation *string `json:"device_location" xml:"device_location"`
DeviceLastContact time.Time `json:"device_last_contact" xml:"device_last_contact"`
DeviceLastContact *time.Time `json:"device_last_contact" xml:"device_last_contact"`
Logfile string `json:"logfile" xml:"logfile"`
CreationDate time.Time `json:"creation_date" xml:"creation_date"`
}

View File

@ -12,7 +12,7 @@ type MeasuredValue struct {
FromDate time.Time `json:"from_date" xml:"from_date"`
TillDate time.Time `json:"till_date" xml:"till_date"`
SensorID string `json:"sensor_id" xml:"sensor_id"`
CreationDate *time.Time `json:"creation_date" xml:"creation_date"`
CreationDate time.Time `json:"creation_date" xml:"creation_date"`
UpdateDate *time.Time `json:"update_date" xml:"update_date"`
}

View File

@ -18,7 +18,7 @@ type Sensor struct {
GPIONumber *GPIO `json:"gpio_number" xml:"gpio_number"`
SensorModel SensorModel `json:"sensor_model" xml:"sensor_model"`
SensorEnabled bool `json:"sensor_enabled" xml:"sensor_enabled"`
SensorLastContact time.Time `json:"sensor_last_contact" xml:"sensor_last_contact"`
SensorLastContact *time.Time `json:"sensor_last_contact" xml:"sensor_last_contact"`
DeviceID string `json:"device_id" xml:"device_id"`
CreationDate time.Time `json:"creation_date" xml:"creation_date"`
}

View File

@ -0,0 +1,69 @@
package goldenfiles
import (
"bytes"
"encoding/json"
"strings"
"testing"
"github.com/go-flucky/flucky/pkg/types"
"github.com/stretchr/testify/require"
)
func CompareMeasuredValues(t *testing.T, goldenInterface interface{}, testInterface interface{}) {
require := require.New(t)
goldenBuffer := new(bytes.Buffer)
testBuffer := new(bytes.Buffer)
jsonEncoder := json.NewEncoder(goldenBuffer)
jsonEncoder.SetIndent("", " ")
err := jsonEncoder.Encode(&goldenInterface)
require.NoError(err)
jsonEncoder = json.NewEncoder(testBuffer)
jsonEncoder.SetIndent("", " ")
err = jsonEncoder.Encode(&testInterface)
require.NoError(err)
// t.Log(goldenBuffer.String(), testBuffer.String())
require.Equal(goldenBuffer.String(), testBuffer.String(), "Expected are not equal with actual value")
}
func GetGoldenDevices(goldenFilePath string) ([]*types.Device, error) {
file := string(MustAsset(goldenFilePath))
reader := strings.NewReader(file)
devices := make([]*types.Device, 0)
jsonDecoder := json.NewDecoder(reader)
err := jsonDecoder.Decode(&devices)
if err != nil {
return nil, err
}
return devices, nil
}
func GetGoldenMeasuredValues(goldenFilePath string) ([]*types.MeasuredValue, error) {
f := string(MustAsset(goldenFilePath))
r := strings.NewReader(f)
measuredValues := make([]*types.MeasuredValue, 0)
jsonDecoder := json.NewDecoder(r)
err := jsonDecoder.Decode(&measuredValues)
if err != nil {
return nil, err
}
return measuredValues, nil
}
func GetGoldenSensors(goldenFilePath string) ([]*types.Sensor, error) {
f := string(MustAsset(goldenFilePath))
r := strings.NewReader(f)
sensors := make([]*types.Sensor, 0)
jsonDecoder := json.NewDecoder(r)
err := jsonDecoder.Decode(&sensors)
if err != nil {
return nil, err
}
return sensors, nil
}

View File

@ -0,0 +1,9 @@
[
{
"device_id": "1efc3092-871e-4ac4-b0d4-ea5e5715460d",
"device_name": "poseidon",
"device_location": null,
"device_last_contact": null,
"creation_date": "2019-03-06T20:02:48.839308+01:00"
}
]

View File

@ -0,0 +1,142 @@
[
{
"id": "f94bb22a-d896-4bf7-bb48-74acd9211698",
"value": "45",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:20.409105+02:00",
"till_date": "2019-08-23T20:38:21.369122+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.401141+02:00",
"update_date": "2019-08-23T20:41:05.347411+02:00"
},
{
"id": "4bf1f15f-2175-454d-b711-7642600437e4",
"value": "75",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:22.249136+02:00",
"till_date": "2019-08-23T20:38:22.249137+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.401477+02:00",
"update_date": null
},
{
"id": "8e7653a4-c9c6-418c-8acb-73e73023cad2",
"value": "66",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:23.289123+02:00",
"till_date": "2019-08-23T20:38:23.289124+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.401789+02:00",
"update_date": null
},
{
"id": "4dd27ac3-ec52-4453-9391-2f8483574161",
"value": "64.5",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:24.16919+02:00",
"till_date": "2019-08-23T20:38:24.169192+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.401447+02:00",
"update_date": null
},
{
"id": "0e5ccd47-3ca0-4fe1-875b-a248790d44df",
"value": "64.25",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:25.209098+02:00",
"till_date": "2019-08-23T20:38:25.209117+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.401447+02:00",
"update_date": null
},
{
"id": "9de64ca1-b4bd-4a0b-a750-1f751eb36031",
"value": "64",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:26.169711+02:00",
"till_date": "2019-08-23T20:38:27.049147+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.401975+02:00",
"update_date": "2019-08-23T20:41:05.347447+02:00"
},
{
"id": "8c952afc-6824-4b06-bfd0-16a0d8c5fc80",
"value": "78",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:28.089+02:00",
"till_date": "2019-08-23T20:38:28.089+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.401457+02:00",
"update_date": null
},
{
"id": "780c1008-fb17-494b-9c61-e0a34245d63c",
"value": "77",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:28.969711+02:00",
"till_date": "2019-08-23T20:38:28.96974+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.401141+02:00",
"update_date": null
},
{
"id": "b206b39d-9f80-4ba8-bb6b-d171abc3c458",
"value": "61",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:30.009741+02:00",
"till_date": "2019-08-23T20:38:30.969711+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.401668+02:00",
"update_date": "2019-08-23T20:41:05.347446+02:00"
},
{
"id": "4d381d84-a56c-4bc8-89c7-ea41b2432eaf",
"value": "55.25",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:31.929108+02:00",
"till_date": "2019-08-23T20:38:38.569141+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.407891+02:00",
"update_date": "2019-08-23T20:41:05.347789+02:00"
},
{
"id": "07ce5f6c-d860-448e-98cd-b390bdcf63b3",
"value": "55",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:39.529164+02:00",
"till_date": "2019-08-23T20:38:40.569106+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.401456+02:00",
"update_date": "2019-08-23T20:41:05.347789+02:00"
},
{
"id": "18145034-237e-4066-a998-0dd1b4a0a3b4",
"value": "50",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:41.529216+02:00",
"till_date": "2019-08-23T20:38:41.529217+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.401789+02:00",
"update_date": null
},
{
"id": "56794741-0758-401b-8697-e42998a53d2d",
"value": "49.5",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:42.489113+02:00",
"till_date": "2019-08-23T20:38:42.489114+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.401789+02:00",
"update_date": null
},
{
"id": "7dbf1aa5-a122-4ba7-8444-fc7aac22246f",
"value": "48.5",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:43.369131+02:00",
"till_date": "2019-08-23T20:46:04.409158+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.401221+02:00",
"update_date": "2019-08-23T20:46:05.341739+02:00"
}
]

View File

@ -0,0 +1,422 @@
[
{
"id": "f94bb22a-d896-4bf7-bb48-74acd9211698",
"value": "45",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:20.409105+02:00",
"till_date": "2019-08-23T20:38:21.369122+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "4bf1f15f-2175-454d-b711-7642600437e4",
"value": "75",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:22.249136+02:00",
"till_date": "2019-08-23T20:38:22.249137+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "8e7653a4-c9c6-418c-8acb-73e73023cad2",
"value": "66",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:23.289123+02:00",
"till_date": "2019-08-23T20:38:23.289124+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "4dd27ac3-ec52-4453-9391-2f8483574161",
"value": "64.5",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:24.16919+02:00",
"till_date": "2019-08-23T20:38:24.169192+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "0e5ccd47-3ca0-4fe1-875b-a248790d44df",
"value": "64.25",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:25.209098+02:00",
"till_date": "2019-08-23T20:38:25.2091+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "9de64ca1-b4bd-4a0b-a750-1f751eb36031",
"value": "64",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:26.169175+02:00",
"till_date": "2019-08-23T20:38:27.049148+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "8c952afc-6824-4b06-bfd0-16a0d8c5fc80",
"value": "78",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:28.089115+02:00",
"till_date": "2019-08-23T20:38:28.089116+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "780c1008-fb17-494b-9c61-e0a34245d63c",
"value": "77",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:28.969102+02:00",
"till_date": "2019-08-23T20:38:28.969104+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "b206b39d-9f80-4ba8-bb6b-d171abc3c458",
"value": "61",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:30.009139+02:00",
"till_date": "2019-08-23T20:38:30.969136+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "4d381d84-a56c-4bc8-89c7-ea41b2432eaf",
"value": "55.25",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:31.929108+02:00",
"till_date": "2019-08-23T20:38:38.569141+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "07ce5f6c-d860-448e-98cd-b390bdcf63b3",
"value": "55",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:39.529164+02:00",
"till_date": "2019-08-23T20:38:40.569106+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "18145034-237e-4066-a998-0dd1b4a0a3b4",
"value": "50",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:41.529216+02:00",
"till_date": "2019-08-23T20:38:41.529217+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "56794741-0758-401b-8697-e42998a53d2d",
"value": "49.5",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:42.489113+02:00",
"till_date": "2019-08-23T20:38:42.489114+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "7dbf1aa5-a122-4ba7-8444-fc7aac22246f",
"value": "48.5",
"value_type": "humidity",
"from_date": "2019-08-23T20:38:43.369131+02:00",
"till_date": "2019-08-23T20:46:04.409158+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:46:05.341739+02:00"
},
{
"id": "f94bb22a-d896-4bf7-bb48-74acd9211698",
"value": "100146.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:20.409105+02:00",
"till_date": "2019-08-23T20:38:21.369122+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "4bf1f15f-2175-454d-b711-7642600437e4",
"value": "100147.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:22.249136+02:00",
"till_date": "2019-08-23T20:38:22.249137+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "8e7653a4-c9c6-418c-8acb-73e73023cad2",
"value": "100144.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:23.289123+02:00",
"till_date": "2019-08-23T20:38:23.289124+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "4dd27ac3-ec52-4453-9391-2f8483574161",
"value": "100666.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:24.16919+02:00",
"till_date": "2019-08-23T20:38:24.169192+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "0e5ccd47-3ca0-4fe1-875b-a248790d44df",
"value": "100612.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:25.209098+02:00",
"till_date": "2019-08-23T20:38:25.2091+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "9de64ca1-b4bd-4a0b-a750-1f751eb36031",
"value": "100550.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:26.169175+02:00",
"till_date": "2019-08-23T20:38:27.049148+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "8c952afc-6824-4b06-bfd0-16a0d8c5fc80",
"value": "100609.750",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:28.089115+02:00",
"till_date": "2019-08-23T20:38:28.089116+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "780c1008-fb17-494b-9c61-e0a34245d63c",
"value": "100609.250",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:28.969102+02:00",
"till_date": "2019-08-23T20:38:28.969104+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "b206b39d-9f80-4ba8-bb6b-d171abc3c458",
"value": "100146.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:30.009139+02:00",
"till_date": "2019-08-23T20:38:30.969136+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "4d381d84-a56c-4bc8-89c7-ea41b2432eaf",
"value": "100149.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:31.929108+02:00",
"till_date": "2019-08-23T20:38:38.569141+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "07ce5f6c-d860-448e-98cd-b390bdcf63b3",
"value": "100114.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:39.529164+02:00",
"till_date": "2019-08-23T20:38:40.569106+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "18145034-237e-4066-a998-0dd1b4a0a3b4",
"value": "100149.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:41.529216+02:00",
"till_date": "2019-08-23T20:38:41.529217+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "56794741-0758-401b-8697-e42998a53d2d",
"value": "100166.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:42.489113+02:00",
"till_date": "2019-08-23T20:38:42.489114+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "7dbf1aa5-a122-4ba7-8444-fc7aac22246f",
"value": "100178.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:43.369131+02:00",
"till_date": "2019-08-23T20:46:04.409158+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:46:05.341739+02:00"
},
{
"id": "f94bb22a-d896-4bf7-bb48-74acd9211698",
"value": "25",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:20.409105+02:00",
"till_date": "2019-08-23T20:38:21.369122+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "4bf1f15f-2175-454d-b711-7642600437e4",
"value": "25.5",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:22.249136+02:00",
"till_date": "2019-08-23T20:38:22.249137+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "8e7653a4-c9c6-418c-8acb-73e73023cad2",
"value": "25",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:23.289123+02:00",
"till_date": "2019-08-23T20:38:23.289124+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "4dd27ac3-ec52-4453-9391-2f8483574161",
"value": "25.5",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:24.16919+02:00",
"till_date": "2019-08-23T20:38:24.169192+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "0e5ccd47-3ca0-4fe1-875b-a248790d44df",
"value": "25",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:25.209098+02:00",
"till_date": "2019-08-23T20:38:25.2091+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "9de64ca1-b4bd-4a0b-a750-1f751eb36031",
"value": "25.5",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:26.169175+02:00",
"till_date": "2019-08-23T20:38:27.049148+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "8c952afc-6824-4b06-bfd0-16a0d8c5fc80",
"value": "25",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:28.089115+02:00",
"till_date": "2019-08-23T20:38:28.089116+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "780c1008-fb17-494b-9c61-e0a34245d63c",
"value": "25.5",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:28.969102+02:00",
"till_date": "2019-08-23T20:38:28.969104+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "b206b39d-9f80-4ba8-bb6b-d171abc3c458",
"value": "25",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:30.009139+02:00",
"till_date": "2019-08-23T20:38:30.969136+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "4d381d84-a56c-4bc8-89c7-ea41b2432eaf",
"value": "25.5",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:31.929108+02:00",
"till_date": "2019-08-23T20:38:38.569141+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "07ce5f6c-d860-448e-98cd-b390bdcf63b3",
"value": "25",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:39.529164+02:00",
"till_date": "2019-08-23T20:38:40.569106+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "18145034-237e-4066-a998-0dd1b4a0a3b4",
"value": "25.5",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:41.529216+02:00",
"till_date": "2019-08-23T20:38:41.529217+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "56794741-0758-401b-8697-e42998a53d2d",
"value": "25",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:42.489113+02:00",
"till_date": "2019-08-23T20:38:42.489114+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "7dbf1aa5-a122-4ba7-8444-fc7aac22246f",
"value": "25.5",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:43.369131+02:00",
"till_date": "2019-08-23T20:46:04.409158+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:46:05.341739+02:00"
}
]

View File

@ -0,0 +1,142 @@
[
{
"id": "f94bb22a-d896-4bf7-bb48-74acd9211698",
"value": "100146.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:20.409105+02:00",
"till_date": "2019-08-23T20:38:21.369122+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "4bf1f15f-2175-454d-b711-7642600437e4",
"value": "100147.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:22.249136+02:00",
"till_date": "2019-08-23T20:38:22.249137+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "8e7653a4-c9c6-418c-8acb-73e73023cad2",
"value": "100144.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:23.289123+02:00",
"till_date": "2019-08-23T20:38:23.289124+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "4dd27ac3-ec52-4453-9391-2f8483574161",
"value": "100666.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:24.16919+02:00",
"till_date": "2019-08-23T20:38:24.169192+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881587+02:00",
"update_date": null
},
{
"id": "0e5ccd47-3ca0-4fe1-875b-a248790d44df",
"value": "100612.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:25.209098+02:00",
"till_date": "2019-08-23T20:38:25.2091+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "9de64ca1-b4bd-4a0b-a750-1f751eb36031",
"value": "100550.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:26.169175+02:00",
"till_date": "2019-08-23T20:38:27.049148+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "8c952afc-6824-4b06-bfd0-16a0d8c5fc80",
"value": "100609.750",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:28.089115+02:00",
"till_date": "2019-08-23T20:38:28.089116+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "780c1008-fb17-494b-9c61-e0a34245d63c",
"value": "100609.250",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:28.969102+02:00",
"till_date": "2019-08-23T20:38:28.969104+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "b206b39d-9f80-4ba8-bb6b-d171abc3c458",
"value": "100146.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:30.009139+02:00",
"till_date": "2019-08-23T20:38:30.969136+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "4d381d84-a56c-4bc8-89c7-ea41b2432eaf",
"value": "100149.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:31.929108+02:00",
"till_date": "2019-08-23T20:38:38.569141+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "07ce5f6c-d860-448e-98cd-b390bdcf63b3",
"value": "100114.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:39.529164+02:00",
"till_date": "2019-08-23T20:38:40.569106+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "18145034-237e-4066-a998-0dd1b4a0a3b4",
"value": "100149.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:41.529216+02:00",
"till_date": "2019-08-23T20:38:41.529217+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "56794741-0758-401b-8697-e42998a53d2d",
"value": "100166.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:42.489113+02:00",
"till_date": "2019-08-23T20:38:42.489114+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "7dbf1aa5-a122-4ba7-8444-fc7aac22246f",
"value": "100178.500",
"value_type": "pressure",
"from_date": "2019-08-23T20:38:43.369131+02:00",
"till_date": "2019-08-23T20:46:04.409158+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:46:05.341739+02:00"
}
]

View File

@ -0,0 +1,44 @@
[
{
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"sensor_name": "DS18B20-rechts",
"sensor_location": "Wohnzimmer",
"wire_id": "28-02131dbffeaa",
"i2c_bus": null,
"i2c_address": null,
"gpio_number": "GPIO14",
"sensor_model": "DS18B20",
"sensor_enabled": false,
"sensor_last_contact": null,
"device_id": "1efc3092-871e-4ac4-b0d4-ea5e5715460d",
"creation_date": "2019-03-06T20:02:48.839304+01:00"
},
{
"sensor_id": "84eac248-6927-4db6-b6f9-7891ce2d301e",
"sensor_name": "DS18B20-links",
"sensor_location": "Wohnzimmer",
"wire_id": "28-01131646f11d",
"i2c_bus": null,
"i2c_address": null,
"gpio_number": "GPIO14",
"sensor_model": "DS18B20",
"sensor_enabled": false,
"sensor_last_contact": null,
"device_id": "1efc3092-871e-4ac4-b0d4-ea5e5715460d",
"creation_date": "2019-03-06T20:03:08.215219+01:00"
},
{
"sensor_id": "4c1a4549-483f-44de-9ce0-459b577e6244",
"sensor_name": "BME280",
"sensor_location": "",
"wire_id": null,
"i2c_bus": 1,
"i2c_address": 118,
"gpio_number": null,
"sensor_model": "BME280",
"sensor_enabled": true,
"sensor_last_contact": null,
"device_id": "1efc3092-871e-4ac4-b0d4-ea5e5715460d",
"creation_date": "2019-06-30T13:42:24.089319+02:00"
}
]

View File

@ -0,0 +1,142 @@
[
{
"id": "f94bb22a-d896-4bf7-bb48-74acd9211698",
"value": "25",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:20.409105+02:00",
"till_date": "2019-08-23T20:38:21.369122+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.839308+02:00"
},
{
"id": "4bf1f15f-2175-454d-b711-7642600437e4",
"value": "25.5",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:22.249136+02:00",
"till_date": "2019-08-23T20:38:22.249137+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "8e7653a4-c9c6-418c-8acb-73e73023cad2",
"value": "25",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:23.289123+02:00",
"till_date": "2019-08-23T20:38:23.289124+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "4dd27ac3-ec52-4453-9391-2f8483574161",
"value": "25.5",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:24.16919+02:00",
"till_date": "2019-08-23T20:38:24.169192+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "0e5ccd47-3ca0-4fe1-875b-a248790d44df",
"value": "25",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:25.209098+02:00",
"till_date": "2019-08-23T20:38:25.2091+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "9de64ca1-b4bd-4a0b-a750-1f751eb36031",
"value": "25.5",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:26.169175+02:00",
"till_date": "2019-08-23T20:38:27.049148+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "8c952afc-6824-4b06-bfd0-16a0d8c5fc80",
"value": "25",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:28.089115+02:00",
"till_date": "2019-08-23T20:38:28.089116+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "780c1008-fb17-494b-9c61-e0a34245d63c",
"value": "25.5",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:28.969102+02:00",
"till_date": "2019-08-23T20:38:28.969104+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "b206b39d-9f80-4ba8-bb6b-d171abc3c458",
"value": "25",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:30.009139+02:00",
"till_date": "2019-08-23T20:38:30.969136+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "4d381d84-a56c-4bc8-89c7-ea41b2432eaf",
"value": "25.5",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:31.929108+02:00",
"till_date": "2019-08-23T20:38:38.569141+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "07ce5f6c-d860-448e-98cd-b390bdcf63b3",
"value": "25",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:39.529164+02:00",
"till_date": "2019-08-23T20:38:40.569106+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:41:05.347223+02:00"
},
{
"id": "18145034-237e-4066-a998-0dd1b4a0a3b4",
"value": "25.5",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:41.529216+02:00",
"till_date": "2019-08-23T20:38:41.529217+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "56794741-0758-401b-8697-e42998a53d2d",
"value": "25",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:42.489113+02:00",
"till_date": "2019-08-23T20:38:42.489114+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": null
},
{
"id": "7dbf1aa5-a122-4ba7-8444-fc7aac22246f",
"value": "25.5",
"value_type": "temperature",
"from_date": "2019-08-23T20:38:43.369131+02:00",
"till_date": "2019-08-23T20:46:04.409158+02:00",
"sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8",
"creation_date": "2019-08-23T20:41:05.40881+02:00",
"update_date": "2019-08-23T20:46:05.341739+02:00"
}
]