fix: new implementation

changes:
- Remove cli
  Some cli commands are not complete tested and are deprecated.

- Daemon
  - Old version has a very bad implementation of how to verify, if the
    device or the sensors are in the database insert. The current
    implementation can be improved but this one is betten then the old
    one.
  - Remove complete the cache store implementation. Use a normal array
    and query the length and capacity to determine how the array cache
    must be cleaned.

- Type
  Remove unused types and functions
This commit is contained in:
Markus Pesch 2020-05-03 14:04:08 +02:00
parent 84d052184e
commit fb8d4dd5eb
Signed by: volker.raschek
GPG Key ID: 852BCC170D81A982
137 changed files with 658 additions and 14048 deletions

View File

@ -1,15 +0,0 @@
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

@ -173,17 +173,13 @@ bin/tmp/${EXECUTABLE}: bindata
# GO-BINDATA
# ==============================================================================
BINDATA_TARGETS := \
pkg/storage/db/bindataSQL.go \
test/goldenfiles/bindata.go
pkg/storage/bindataSQL.go \
PHONY+=bindata
bindata: ${BINDATA_TARGETS}
bindata: clean ${BINDATA_TARGETS}
pkg/storage/db/bindataSQL.go:
go-bindata -pkg db -o ./pkg/storage/db/bindataSQL.go ./pkg/storage/db/sql/*** ./pkg/storage/db/sql/psql/schema/***
test/goldenfiles/bindata.go:
go-bindata -pkg goldenfiles -ignore ".*\.go" -o ./test/goldenfiles/bindata.go ./test/goldenfiles/***
pkg/storage/bindataSQL.go:
go-bindata -pkg storage -o ./pkg/storage/bindataSQL.go pkg/storage/postgres/***
# TEST
# ==============================================================================
@ -288,9 +284,9 @@ container-run:
PHONY+=${FLUCKY_REMOTE:%=remote/%}
remote/${FLUCKY_REMOTE}: bin/linux/arm/7/${EXECUTABLE}
scp bin/linux/arm/7/${EXECUTABLE} root@${FLUCKY_REMOTE}:/usr/local/bin/${EXECUTABLE}
ssh root@${FLUCKY_REMOTE} 'mkdir --parent /etc/bash_completion.d || true'
ssh root@${FLUCKY_REMOTE} 'flucky completion bash > /etc/bash_completion.d/flucky.sh && chmod +x /etc/bash_completion.d/flucky.sh'
ssh root@${FLUCKY_REMOTE} 'flucky completion zsh > /etc/bash_completion.d/flucky.zsh && chmod +x /etc/bash_completion.d/flucky.zsh'
# ssh root@${FLUCKY_REMOTE} 'mkdir --parent /etc/bash_completion.d || true'
# ssh root@${FLUCKY_REMOTE} 'flucky completion bash > /etc/bash_completion.d/flucky.sh && chmod +x /etc/bash_completion.d/flucky.sh'
# ssh root@${FLUCKY_REMOTE} 'flucky completion zsh > /etc/bash_completion.d/flucky.zsh && chmod +x /etc/bash_completion.d/flucky.zsh'
ssh root@${FLUCKY_REMOTE} 'chmod +x /usr/local/bin/${EXECUTABLE}'
# PHONY

View File

@ -1,42 +0,0 @@
package completion
import (
"os"
"github.com/spf13/cobra"
)
// InitCmd initialize all completion subcommands
func InitCmd(cmd *cobra.Command) error {
completionCmd := &cobra.Command{
Use: "completion",
Short: "Generates a shell completion file",
}
bashCompletionCmd := &cobra.Command{
Use: "bash",
Short: "Generates a bash completion file",
Args: cobra.NoArgs,
Example: "flucky completion bash",
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.GenBashCompletion(os.Stdout)
},
}
zshCompletionCmd := &cobra.Command{
Use: "zsh",
Short: "Generate a zsh completion file",
Args: cobra.NoArgs,
Example: "flucky completion zsh",
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.GenZshCompletion(os.Stdout)
},
}
completionCmd.AddCommand(bashCompletionCmd)
completionCmd.AddCommand(zshCompletionCmd)
cmd.AddCommand(completionCmd)
return nil
}

View File

@ -1,50 +0,0 @@
package compression
import (
"github.com/volker-raschek/flucky/pkg/storage"
"github.com/volker-raschek/flucky/pkg/storage/logfile"
"github.com/spf13/cobra"
)
var (
round float64
)
// InitCmd initialize all compression subcommands
func InitCmd(cmd *cobra.Command) error {
compressionCmd := &cobra.Command{
Use: "compression",
Short: "Compress a logfile",
Args: cobra.ExactArgs(1),
Example: "flucky compression /var/log/flucky/logfile.csv",
RunE: run,
}
compressionCmd.Flags().Float64Var(&round, "round", 0, "Round values. The value 0 deactivates the function")
cmd.AddCommand(compressionCmd)
return nil
}
func run(cmd *cobra.Command, args []string) error {
logfileInput := logfile.New(args[0])
measuredValues, err := logfileInput.Read()
if err != nil {
return err
}
if round != 0 {
storage.Round(measuredValues, round)
}
storage.Compression(measuredValues)
err = logfileInput.Write(measuredValues)
if err != nil {
return err
}
return nil
}

View File

@ -1,55 +0,0 @@
package convert
import (
"github.com/volker-raschek/flucky/pkg/storage"
"github.com/volker-raschek/flucky/pkg/storage/logfile"
"github.com/spf13/cobra"
)
var (
compression bool
round float64
)
// InitCmd initialize all convert subcommands
func InitCmd(cmd *cobra.Command) error {
convertCmd := &cobra.Command{
Use: "convert",
Short: "Convert logfiles into other markup language",
Args: cobra.ExactArgs(2),
Example: "flucky convert /var/log/flucky/logfile.json /var/log/flucky/logfile.csv",
RunE: run,
}
convertCmd.Flags().BoolVar(&compression, "compression", false, "Compress measured values")
convertCmd.Flags().Float64Var(&round, "round", 0, "Round values. The value 0 deactivates the function")
cmd.AddCommand(convertCmd)
return nil
}
func run(cmd *cobra.Command, args []string) error {
logfileInput := logfile.New(args[0])
measuredValues, err := logfileInput.Read()
if err != nil {
return err
}
if round != 0 {
storage.Round(measuredValues, round)
}
if compression {
measuredValues = storage.Compression(measuredValues)
}
logfileOutput := logfile.New(args[1])
err = logfileOutput.Write(measuredValues)
if err != nil {
return err
}
return nil
}

View File

@ -1,56 +0,0 @@
package daemon
import (
"fmt"
"github.com/spf13/cobra"
"github.com/volker-raschek/flucky/cmd/internal"
"github.com/volker-raschek/flucky/pkg/config"
"github.com/volker-raschek/flucky/pkg/daemon"
)
var (
cachedMeasuredValues uint
compression bool
round float64
temperatureUnit string
)
// InitCmd initialize all daemon subcommands
func InitCmd(cmd *cobra.Command) error {
daemonCmd := &cobra.Command{
Use: "daemon",
Short: "Read continuously data from all enabled sensors",
Example: "flucky daemon",
RunE: run,
}
daemonCmd.Flags().BoolVar(&compression, "compression", true, "Compress measured values")
daemonCmd.Flags().UintVar(&cachedMeasuredValues, "cached-values", 500, "Number of cached values before saveing into the storage endpoint")
daemonCmd.Flags().Float64Var(&round, "round", 0.5, "Round values. The value 0 deactivates the function")
cmd.AddCommand(daemonCmd)
return nil
}
func run(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined: %v", err)
}
logLevel, err := cmd.Flags().GetString("loglevel")
if err != nil {
return fmt.Errorf("No loglevel defined: %v", err)
}
flogger := internal.InitializeLogger(logLevel)
daemon.SetLogger(flogger)
cnf, err := config.Read(configFile)
if err != nil {
return err
}
return daemon.Start(cnf, cachedMeasuredValues, compression, round)
}

View File

@ -1,106 +0,0 @@
package db
import (
"context"
"fmt"
"net/url"
"github.com/Masterminds/semver"
"github.com/volker-raschek/flucky/cmd/internal"
"github.com/volker-raschek/flucky/pkg/config"
"github.com/volker-raschek/flucky/pkg/storage/db"
"github.com/volker-raschek/flucky/pkg/types"
"github.com/spf13/cobra"
)
var (
semanticVersion *semver.Version
)
// InitCmd initialize all convert subcommands
func InitCmd(cmd *cobra.Command, semver *semver.Version) error {
semanticVersion = semver
databaseCmd := &cobra.Command{
Use: "db",
Short: "Interagte with the database configured as storage endpoint",
}
updateDatabaseCmd := &cobra.Command{
Use: "init",
Short: "Initialize the database scheme for the version of the flucky binary",
Args: cobra.NoArgs,
Example: "flucky db update",
RunE: initializeDatabase,
}
databaseCmd.AddCommand(updateDatabaseCmd)
cmd.AddCommand(databaseCmd)
return nil
}
func initializeDatabase(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
storageEndpointURL, err := url.Parse(cnf.StorageEndpoint)
if err != nil {
return fmt.Errorf("Failed to parse storage endpoint: %v", err)
}
logLevel, err := cmd.Flags().GetString("loglevel")
if err != nil {
return fmt.Errorf("No loglevel defined: %v", err)
}
flogger := internal.InitializeLogger(logLevel)
database, err := db.New(storageEndpointURL)
if err != nil {
return err
}
defer database.Close()
ctx := context.Background()
flogger.Debug("Initialize database scheme")
err = database.Schema(ctx, semanticVersion)
if err != nil {
return err
}
_, err = database.SelectDeviceByID(ctx, cnf.Device.ID)
if err != nil {
flogger.Debug("Add device into database")
devices := []*types.Device{cnf.Device}
if err := database.InsertDevices(ctx, devices); err != nil {
return err
}
} else {
flogger.Debug("Device already availabe in the database")
}
for _, sensor := range cnf.Sensors {
_, err = database.SelectSensorByID(ctx, sensor.ID)
if err != nil {
flogger.Debug("Add sensor %v into the database", sensor.FullName())
sensors := []*types.Sensor{sensor}
if err := database.InsertSensors(ctx, sensors); err != nil {
return err
}
} else {
flogger.Debug("Sensor %v already available in the database", sensor.FullName())
}
}
return nil
}

View File

@ -1,133 +0,0 @@
package humidity
import (
"context"
"fmt"
"os"
"github.com/volker-raschek/flucky/pkg/cli"
"github.com/volker-raschek/flucky/pkg/config"
"github.com/volker-raschek/flucky/pkg/sensor"
"github.com/volker-raschek/flucky/pkg/storage"
"github.com/volker-raschek/flucky/pkg/types"
"github.com/spf13/cobra"
)
var (
compression bool
configFile string
save bool
round float64
)
// InitCmd initialize all humidity subcommands
func InitCmd(cmd *cobra.Command) error {
humidityCmd := &cobra.Command{
Use: "humidity",
Short: "Operates with humidity values",
}
listHumiditiesCmd := &cobra.Command{
Use: "list",
Short: "List humidity values from specified or all sensors",
Example: fmt.Sprintf("flucky humidity list"),
RunE: listHumidities,
}
readHumiditiesCmd := &cobra.Command{
Use: "read",
Short: "Read humidity values from specified or all sensors",
Example: fmt.Sprintf("flucky humidity read"),
RunE: readHumidities,
}
readHumiditiesCmd.Flags().BoolVar(&save, "save", true, "Save humidities")
readHumiditiesCmd.Flags().BoolVar(&compression, "compression", true, "Compress measured with logged temperatures")
readHumiditiesCmd.Flags().Float64VarP(&round, "round", "r", 0.25, "Round values. The value 0 deactivates the function")
humidityCmd.AddCommand(listHumiditiesCmd)
humidityCmd.AddCommand(readHumiditiesCmd)
cmd.AddCommand(humidityCmd)
return nil
}
func listHumidities(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
// read configuration
cnf, err := config.Read(configFile)
if err != nil {
return err
}
ctx := context.Background()
storageEndpoint, err := cnf.GetStorageEndpointURL()
if err != nil {
return err
}
measuredValues, err := storage.Read(ctx, storageEndpoint)
if err != nil {
return err
}
measuredValues = types.SelectMeasuredValues(types.MeasuredValueTypeHumidity, measuredValues)
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
return nil
}
func readHumidities(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
// fetch all temperature sensors or sensors by args
sensors := make([]sensor.Sensor, 0)
if len(args) == 0 {
sensors = cnf.GetHumiditySensors(config.ENABLED)
} else {
sensors = cnf.GetHumiditySensorsByName(args)
}
if len(sensors) == 0 {
return fmt.Errorf("No sensors matched")
}
measuredValues, err := sensor.Read(sensors, types.MeasuredValueTypeHumidity)
if err != nil {
return err
}
measuredValues = types.SelectMeasuredValues(types.MeasuredValueTypeHumidity, measuredValues)
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
if save {
storageEndpoint, err := cnf.GetStorageEndpointURL()
if err != nil {
return err
}
ctx := context.Background()
err = storage.Write(ctx, measuredValues, storageEndpoint)
if err != nil {
return err
}
}
return nil
}

View File

@ -1,20 +0,0 @@
package internal
import "github.com/volker-raschek/go-logger/pkg/logger"
func InitializeLogger(logLevel string) logger.Logger {
switch logLevel {
case "debug", "DEBUG":
return logger.NewDefaultLogger(logger.LogLevelDebug)
case "info", "INFO":
return logger.NewDefaultLogger(logger.LogLevelInfo)
case "warn", "WARN":
return logger.NewDefaultLogger(logger.LogLevelWarn)
case "error", "ERROR":
return logger.NewDefaultLogger(logger.LogLevelError)
case "fatal", "FATAL":
return logger.NewDefaultLogger(logger.LogLevelFatal)
default:
return nil
}
}

View File

@ -1,133 +0,0 @@
package pressure
import (
"context"
"fmt"
"os"
"github.com/volker-raschek/flucky/pkg/cli"
"github.com/volker-raschek/flucky/pkg/config"
"github.com/volker-raschek/flucky/pkg/sensor"
"github.com/volker-raschek/flucky/pkg/storage"
"github.com/volker-raschek/flucky/pkg/types"
"github.com/spf13/cobra"
)
var (
compression bool
configFile string
save bool
round float64
)
// InitCmd initialize all pressure subcommands
func InitCmd(cmd *cobra.Command) error {
pressureCmd := &cobra.Command{
Use: "pressure",
Short: "Operates with pressure values",
}
listPressuresCmd := &cobra.Command{
Use: "list",
Short: "List pressure values from specified or all sensors",
Example: fmt.Sprintf("flucky pressure list"),
RunE: listHumidities,
}
readPressuresCmd := &cobra.Command{
Use: "read",
Short: "Read pressure values from specified or all sensors",
Example: fmt.Sprintf("flucky pressure read"),
RunE: readPressure,
}
readPressuresCmd.Flags().BoolVar(&save, "save", true, "Save humidities")
readPressuresCmd.Flags().BoolVar(&compression, "compression", true, "Compress measured with logged temperatures")
readPressuresCmd.Flags().Float64VarP(&round, "round", "r", 0.25, "Round values. The value 0 deactivates the function")
pressureCmd.AddCommand(listPressuresCmd)
pressureCmd.AddCommand(readPressuresCmd)
cmd.AddCommand(pressureCmd)
return nil
}
func listHumidities(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
ctx := context.Background()
storageEndpoint, err := cnf.GetStorageEndpointURL()
if err != nil {
return err
}
measuredValues, err := storage.Read(ctx, storageEndpoint)
if err != nil {
return err
}
measuredValues = types.SelectMeasuredValues(types.MeasuredValueTypePressure, measuredValues)
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
return nil
}
func readPressure(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
// fetch all temperature sensors or sensors by args
sensors := make([]sensor.Sensor, 0)
if len(args) == 0 {
sensors = cnf.GetPressureSensors(config.ENABLED)
} else {
sensors = cnf.GetPressureSensorsByName(args)
}
if len(sensors) == 0 {
return fmt.Errorf("No sensors matched")
}
measuredValues, err := sensor.Read(sensors, types.MeasuredValueTypePressure)
if err != nil {
return err
}
measuredValues = types.SelectMeasuredValues(types.MeasuredValueTypePressure, measuredValues)
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
if save {
storageEndpoint, err := cnf.GetStorageEndpointURL()
if err != nil {
return err
}
ctx := context.Background()
err = storage.Write(ctx, measuredValues, storageEndpoint)
if err != nil {
return err
}
}
return nil
}

View File

@ -1,347 +0,0 @@
package rgbled
import (
"fmt"
"os"
"github.com/Masterminds/semver"
"github.com/volker-raschek/flucky/pkg/cli"
"github.com/volker-raschek/flucky/pkg/config"
"github.com/volker-raschek/flucky/pkg/rgbled"
"github.com/volker-raschek/flucky/pkg/types"
"github.com/spf13/cobra"
)
var (
enabled bool
location string
version *semver.Version
)
// InitCmd initialize all rgb-led subcommands
func InitCmd(cmd *cobra.Command) error {
rgbLedCmd := &cobra.Command{
Use: "rgb-led",
Short: "Manage RGB-LEDs",
}
addRGBLEDCmd := &cobra.Command{
Use: "add",
Short: "Add a RGB-LED",
Aliases: []string{"append"},
Args: cobra.ExactArgs(4),
Example: fmt.Sprintf(`flucky rgb-led add <name> <gpio-for-blue> <gpio-for-green> <gpio-for-red>
flucky rgb-led add my-led GPIO13 GPIO17 GPIO26`),
RunE: addRGBLED,
}
addRGBLEDCmd.Flags().BoolVarP(&enabled, "enabled", "e", true, "Enable Sensor")
addRGBLEDCmd.Flags().StringVarP(&location, "location", "l", "", "Sensor location")
disableRGBLEDCmd := &cobra.Command{
Use: "disable",
Short: "Disable a RGB-LED",
Args: cobra.ExactArgs(1),
Example: `flucky rgb-led disable <name/uuid>
flucky rgb-led disable my-led
flucky rgb-led disable 9f8abfc5-91f3-480c-a42d-b990b6f89e5d`,
RunE: disableRGBLED,
}
enableRGBLEDCmd := &cobra.Command{
Use: "enable",
Short: "Enable a RGB-LED",
Args: cobra.ExactArgs(1),
Example: `flucky rgb-led enable <name/uuid>
flucky rgb-led enable my-led
flucky rgb-led enable 9f8abfc5-91f3-480c-a42d-b990b6f89e5d`,
RunE: enableRGBLED,
}
listRGBLEDCmd := &cobra.Command{
Use: "list",
Short: "List RGB-LEDs",
Aliases: []string{"ls"},
RunE: listRGBLED,
}
turnOffRGBLEDCmd := &cobra.Command{
Use: "off",
Short: "Turn a RGB-LED color off",
Example: `flucky rgb-led off <name/uuid> <blue>
flucky rgb-led off my-led`,
RunE: turnOffRGBLED,
}
turnOnRGBLEDCmd := &cobra.Command{
Use: "on",
Short: "Turn a RGB-LED color on",
Example: `flucky rgb-led on <names/uuids> <blue/green/purple/red/turquoise/white/yellow>
flucky rgb-led on my-led blue
flucky rgb-led on my-led my-sweet-led white
flucky rgb-led on 1c5b9424-f6e9-4a37-be5c-77e531e94aab red`,
Args: cobra.MinimumNArgs(1),
RunE: turnOnRGBLED,
}
removeRGBLEDCmd := &cobra.Command{
Use: "remove",
Short: "Remove a RGB-LED",
Example: `flucky rgb-led remove <name/uuid>
flucky rgb-led remove my-led
flucky rgb-led remove 9f8abfc5-91f3-480c-a42d-b990b6f89e5d`,
Aliases: []string{"rm"},
Args: cobra.ExactArgs(1),
RunE: removeRGBLED,
}
renameRGBLEDCmd := &cobra.Command{
Use: "rename",
Short: "Rename a RGB-LED",
Args: cobra.ExactArgs(2),
Example: `flucky rgb-led disable <name/uuid> <new-name>
flucky rgb-led disable my-led my-sweet-led
flucky rgb-led disable 9f8abfc5-91f3-480c-a42d-b990b6f89e5d my-sweet-led`,
RunE: renameRGBLED,
}
rgbLedCmd.AddCommand(addRGBLEDCmd)
rgbLedCmd.AddCommand(disableRGBLEDCmd)
rgbLedCmd.AddCommand(enableRGBLEDCmd)
rgbLedCmd.AddCommand(listRGBLEDCmd)
rgbLedCmd.AddCommand(turnOffRGBLEDCmd)
rgbLedCmd.AddCommand(turnOnRGBLEDCmd)
rgbLedCmd.AddCommand(removeRGBLEDCmd)
rgbLedCmd.AddCommand(renameRGBLEDCmd)
cmd.AddCommand(rgbLedCmd)
return nil
}
func addRGBLED(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
gpioBlue, err := types.StringToGPIO(args[1])
if err != nil {
return err
}
gpioGreen, err := types.StringToGPIO(args[2])
if err != nil {
return err
}
gpioRed, err := types.StringToGPIO(args[3])
if err != nil {
return err
}
rgbLED := &types.RGBLED{
RGBLEDName: args[0],
RGBLEDLocation: location,
RGBLEDEnabled: enabled,
ActionMapping: types.DefaultActionMapping,
BaseColorsToGPIO: map[types.BaseColor]*types.GPIO{
types.BaseColorBlue: &gpioBlue,
types.BaseColorGreen: &gpioGreen,
types.BaseColorRed: &gpioRed,
},
}
err = cnf.AddRGBLED(rgbLED)
if err != nil {
return err
}
err = config.Write(cnf, configFile)
if err != nil {
return err
}
return nil
}
func disableRGBLED(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
err = cnf.DisableRGBLED(args[0])
if err != nil {
return err
}
err = config.Write(cnf, configFile)
if err != nil {
return err
}
return nil
}
func enableRGBLED(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
err = cnf.EnableRGBLED(args[0])
if err != nil {
return err
}
err = config.Write(cnf, configFile)
if err != nil {
return err
}
return nil
}
func listRGBLED(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
// print sensors on stdout
cli.PrintRGBLEDs(cnf, os.Stdout)
return nil
}
func turnOffRGBLED(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
rgbLEDs := make([]rgbled.RGBLED, 0)
if len(args) != 0 {
rgbLEDs = cnf.GetRGBLEDsByName(args)
} else {
rgbLEDs = cnf.GetRGBLEDs(config.ENABLED)
}
err = rgbled.Off(rgbLEDs)
if err != nil {
return err
}
return nil
}
func turnOnRGBLED(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
rgbLEDs := make([]rgbled.RGBLED, 0)
if len(args) > 1 {
rgbLEDs = cnf.GetRGBLEDsByName(args[0 : len(args)-1])
} else {
rgbLEDs = cnf.GetRGBLEDs(config.ENABLED)
}
color, err := types.StringToLEDColor(args[len(args)-1])
if err != nil {
return err
}
err = rgbled.CustomColor(rgbLEDs, color)
if err != nil {
return err
}
return nil
}
func removeRGBLED(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
err = cnf.RemoveRGBLED(args[0])
if err != nil {
return err
}
err = config.Write(cnf, configFile)
if err != nil {
return err
}
return nil
}
func renameRGBLED(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
err = cnf.RenameRGBLED(args[0], args[1])
if err != nil {
return err
}
err = config.Write(cnf, configFile)
if err != nil {
return err
}
return nil
}

View File

@ -1,130 +0,0 @@
package cmd
import (
"fmt"
"log"
"math"
"os"
"time"
"github.com/Masterminds/semver"
"github.com/volker-raschek/flucky/cmd/completion"
"github.com/volker-raschek/flucky/cmd/compression"
"github.com/volker-raschek/flucky/cmd/convert"
"github.com/volker-raschek/flucky/cmd/daemon"
"github.com/volker-raschek/flucky/cmd/db"
"github.com/volker-raschek/flucky/cmd/humidity"
"github.com/volker-raschek/flucky/cmd/pressure"
"github.com/volker-raschek/flucky/cmd/rgbled"
"github.com/volker-raschek/flucky/cmd/sensor"
"github.com/volker-raschek/flucky/cmd/temperature"
"github.com/volker-raschek/flucky/pkg/types"
"github.com/volker-raschek/go-logger/pkg/logger"
"github.com/volker-raschek/flucky/pkg/config"
uuid "github.com/satori/go.uuid"
"github.com/spf13/cobra"
)
var (
configFile string
)
// Execute a
func Execute(version *semver.Version) error {
rootCmd := &cobra.Command{
Use: "flucky",
Short: "flucky - operate with differen sensors, his values and remote servers to synchronize measured values",
PersistentPreRunE: preRunError,
Version: version.String(),
}
logLevel := ""
rootCmd.PersistentFlags().StringVar(&configFile, "config", "/etc/flucky/config.json", "Config file")
rootCmd.PersistentFlags().StringVar(&logLevel, "loglevel", "info", "Set the Loglevel. Possible values: debug, info, warn, error, fatal")
subCommands := []func(cmd *cobra.Command) error{
completion.InitCmd,
compression.InitCmd,
convert.InitCmd,
daemon.InitCmd,
humidity.InitCmd,
pressure.InitCmd,
rgbled.InitCmd,
sensor.InitCmd,
temperature.InitCmd,
}
for _, subCommand := range subCommands {
if err := subCommand(rootCmd); err != nil {
return err
}
}
err := db.InitCmd(rootCmd, version)
if err != nil {
return err
}
err = rootCmd.Execute()
if err != nil {
return err
}
return nil
}
func parseLogger(logLevel string) logger.Logger {
log.Println(logLevel)
switch logLevel {
case "debug", "DEBUG":
return logger.NewDefaultLogger(logger.LogLevelDebug)
case "info", "INFO":
return logger.NewDefaultLogger(logger.LogLevelInfo)
case "warn", "WARN":
return logger.NewDefaultLogger(logger.LogLevelWarn)
case "error", "ERROR":
return logger.NewDefaultLogger(logger.LogLevelError)
case "fatal", "FATAL":
return logger.NewDefaultLogger(logger.LogLevelFatal)
default:
return logger.NewDefaultLogger(logger.LogLevelInfo)
}
}
func preRunError(cmd *cobra.Command, args []string) error {
// check if config file exists
if _, err := os.Stat(configFile); os.IsNotExist(err) {
hostname, err := os.Hostname()
if err != nil {
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
postgresTimeStamp := time.Now()
location, err := time.LoadLocation("Europe/Berlin")
if err != nil {
return err
}
postgresTimeStamp = time.Date(postgresTimeStamp.Year(), postgresTimeStamp.Month(), postgresTimeStamp.Day(), postgresTimeStamp.Hour(), postgresTimeStamp.Minute(), postgresTimeStamp.Second(), int(math.Round(float64(postgresTimeStamp.Nanosecond())/1000000)*1000000), location)
// Default configuration
cnf := config.Configuration{
Device: &types.Device{
ID: uuid.NewV4().String(),
Name: hostname,
CreationDate: postgresTimeStamp,
},
StorageEndpoint: "file:///var/log/flucky/logfile.csv",
}
err = config.Write(&cnf, configFile)
if err != nil {
return err
}
}
return nil
}

View File

@ -1,291 +0,0 @@
package sensor
import (
"fmt"
"os"
"github.com/volker-raschek/flucky/pkg/cli"
"github.com/volker-raschek/flucky/pkg/config"
"github.com/volker-raschek/flucky/pkg/types"
"github.com/spf13/cobra"
)
var (
enabled bool
gpioIn string
i2cAddress uint8
i2cBus int
location string
tickDuration string
wireID string
)
// InitCmd initialize all sensor subcommands
func InitCmd(cmd *cobra.Command) error {
sensorCmd := &cobra.Command{
Use: "sensor",
Short: "Manage Sensors",
}
addSensorCmd := &cobra.Command{
Use: "add",
Short: "Add Sensor",
Aliases: []string{"append"},
Args: cobra.ExactArgs(2),
Example: `flucky sensor add --gpio GPIO14 indoor DHT11
flucky sensor add --wire-id 28-011432f0bb3d outdoor DS18B20
flucky sensor add --i2c-bus 1 --i2c-address 0x76 wetter-station BME280`,
RunE: addSensor,
}
addSensorCmd.Flags().BoolVar(&enabled, "enabled", true, "Enable new sensor")
addSensorCmd.Flags().StringVar(&gpioIn, "gpio", "", "Defines the GPIO port")
addSensorCmd.Flags().Uint8Var(&i2cAddress, "i2c-address", 0, "Defines the I2C address on the I2C bus")
addSensorCmd.Flags().IntVar(&i2cBus, "i2c-bus", 0, "Defines the I2C bus")
addSensorCmd.Flags().StringVar(&location, "location", "", "Location of the sensor")
addSensorCmd.Flags().StringVar(&tickDuration, "tick-duration", "1m", "Controls how often values should be read from the sensor when running flucky in daemon mode")
addSensorCmd.Flags().StringVar(&wireID, "wire-id", "", "Defines the Wire-ID")
disableSensorCmd := &cobra.Command{
Use: "disable",
Short: "Disable Sensor",
Args: cobra.ExactArgs(1),
Example: "flucky sensor disable outdoor",
RunE: deleteSensor,
}
enableSensorCmd := &cobra.Command{
Use: "enable",
Short: "Enable Sensor",
Example: "flucky sensor enable outdoor",
Args: cobra.ExactArgs(1),
RunE: enableSensor,
}
listSensorCmd := &cobra.Command{
Use: "list",
Short: "List Sensors",
Aliases: []string{"ls"},
RunE: listSensors,
}
removeSensorCmd := &cobra.Command{
Use: "remove",
Short: "Remove Sensor",
Aliases: []string{"rm"},
Example: "flucky sensor remove outdoor",
Args: cobra.ExactArgs(1),
RunE: removeSensor,
}
renameSensorCmd := &cobra.Command{
Use: "rename",
Short: "Rename Sensor",
Args: cobra.ExactArgs(2),
Example: `flucky sensor rename indoor outdoor
flucky sensor rename f98b00ea-a9b2-4e00-924f-113859d0af2d outdoor`,
RunE: renameSensor,
}
sensorCmd.AddCommand(addSensorCmd)
sensorCmd.AddCommand(disableSensorCmd)
sensorCmd.AddCommand(enableSensorCmd)
sensorCmd.AddCommand(listSensorCmd)
sensorCmd.AddCommand(removeSensorCmd)
sensorCmd.AddCommand(renameSensorCmd)
cmd.AddCommand(sensorCmd)
return nil
}
func addSensor(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
sensorModel, err := types.SelectSensorModel(args[1])
if err != nil {
return err
}
sensor := &types.Sensor{
Name: args[0],
Model: sensorModel,
Location: location,
Enabled: enabled,
TickDuration: tickDuration,
}
// determine gpio port if set
if gpioIn != "" &&
i2cAddress == 0 &&
i2cBus == 0 &&
wireID == "" {
gpio, err := types.StringToGPIO(gpioIn)
if err != nil {
return err
}
sensor.GPIONumber = &gpio
}
// set i2c connection settings
if gpioIn == "" &&
i2cAddress != 0 &&
i2cBus != 0 &&
wireID == "" {
sensor.I2CAddress = &i2cAddress
sensor.I2CBus = &i2cBus
}
// set wire connection settings
if gpioIn == "" &&
i2cAddress == 0 &&
i2cBus == 0 &&
wireID != "" {
sensor.WireID = &wireID
}
// add sensor entry to list
err = cnf.AddSensor(sensor)
if err != nil {
return err
}
// save new configuration
err = config.Write(cnf, configFile)
if err != nil {
return err
}
return nil
}
func deleteSensor(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
err = cnf.DisableSensor(args[0])
if err != nil {
return err
}
err = config.Write(cnf, configFile)
if err != nil {
return err
}
return nil
}
func enableSensor(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
err = cnf.EnableSensor(args[0])
if err != nil {
return err
}
err = config.Write(cnf, configFile)
if err != nil {
return err
}
return nil
}
func listSensors(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
err = cli.PrintSensors(cnf, os.Stdout)
if err != nil {
return err
}
err = config.Write(cnf, configFile)
if err != nil {
return err
}
return nil
}
func removeSensor(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
err = cnf.RemoveSensor(args[0])
if err != nil {
return err
}
err = config.Write(cnf, configFile)
if err != nil {
return err
}
return nil
}
func renameSensor(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
err = cnf.RenameSensor(args[0], args[1])
if err != nil {
return err
}
err = config.Write(cnf, configFile)
if err != nil {
return err
}
return nil
}

View File

@ -1,131 +0,0 @@
package temperature
import (
"context"
"fmt"
"os"
"github.com/volker-raschek/flucky/pkg/cli"
"github.com/volker-raschek/flucky/pkg/config"
"github.com/volker-raschek/flucky/pkg/sensor"
"github.com/volker-raschek/flucky/pkg/storage"
"github.com/volker-raschek/flucky/pkg/types"
"github.com/spf13/cobra"
)
var (
compression bool
save bool
round float64
)
// InitCmd initialize all temperature subcommands
func InitCmd(cmd *cobra.Command) error {
temperatureCmd := &cobra.Command{
Use: "temperature",
Short: "Operates with temperature values",
}
listTemperaturesCmd := &cobra.Command{
Use: "list",
Short: "List temperature values from specified or all sensors",
Example: fmt.Sprintf("flucky temperature list"),
RunE: listTemperatures,
}
readTemperatures := &cobra.Command{
Use: "read",
Short: "Read temperature values from specified or all sensors",
Example: fmt.Sprintf("flucky temperature read"),
RunE: readTemperature,
}
readTemperatures.Flags().BoolVar(&save, "save", true, "Save humidities")
readTemperatures.Flags().BoolVar(&compression, "compression", true, "Compress measured with logged temperatures")
readTemperatures.Flags().Float64VarP(&round, "round", "r", 0.25, "Round values. The value 0 deactivates the function")
temperatureCmd.AddCommand(listTemperaturesCmd)
temperatureCmd.AddCommand(readTemperatures)
cmd.AddCommand(temperatureCmd)
return nil
}
func listTemperatures(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
ctx := context.Background()
storageEndpoint, err := cnf.GetStorageEndpointURL()
if err != nil {
return err
}
measuredValues, err := storage.Read(ctx, storageEndpoint)
if err != nil {
return err
}
measuredValues = types.SelectMeasuredValues(types.MeasuredValueTypeTemperature, measuredValues)
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
return nil
}
func readTemperature(cmd *cobra.Command, args []string) error {
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return fmt.Errorf("No config file defined")
}
cnf, err := config.Read(configFile)
if err != nil {
return err
}
sensors := make([]sensor.Sensor, 0)
if len(args) == 0 {
sensors = cnf.GetTemperatureSensors(config.ENABLED)
} else {
sensors = cnf.GetTemperatureSensorsByName(args)
}
if len(sensors) == 0 {
return fmt.Errorf("No sensors matched")
}
measuredValues, err := sensor.Read(sensors, types.MeasuredValueTypeTemperature)
if err != nil {
return err
}
measuredValues = types.SelectMeasuredValues(types.MeasuredValueTypeTemperature, measuredValues)
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
if save {
storageEndpoint, err := cnf.GetStorageEndpointURL()
if err != nil {
return err
}
ctx := context.Background()
err = storage.Write(ctx, measuredValues, storageEndpoint)
if err != nil {
return err
}
}
return nil
}

View File

@ -3,11 +3,9 @@ services:
flucky-db:
container_name: postgres
environment:
- PGTZ=${TZ}
- POSTGRES_DB=${POSTGRES_DB_NAME}
- POSTGRES_USER=${POSTGRES_DB_USER}
- POSTGRES_PASSWORD=${POSTGRES_DB_PASS}
- TZ=${TZ}
- PGTZ=Europe/Berlin
- POSTGRES_PASSWORD=postgres
- TZ=Europe/Berlin
image: postgres:11.5-alpine
ports:
- 5432:5432

5
go.mod
View File

@ -3,16 +3,13 @@ module github.com/volker-raschek/flucky
go 1.12
require (
github.com/Masterminds/semver v1.4.2
github.com/d2r2/go-bsbmp v0.0.0-20190515110334-3b4b3aea8375
github.com/d2r2/go-i2c v0.0.0-20181113114621-14f8dd4e89ce
github.com/d2r2/go-logger v0.0.0-20181221090742-9998a510495e
github.com/go-flucky/flucky v0.0.0-20190714170626-0dd156f480be
github.com/go-flucky/go-dht v0.1.1
github.com/lib/pq v1.2.0
github.com/lib/pq v1.4.0
github.com/satori/go.uuid v1.2.0
github.com/spf13/cobra v0.0.3
github.com/stianeikeland/go-rpio v4.2.0+incompatible
github.com/stretchr/testify v1.4.0
github.com/volker-raschek/go-logger v0.0.0-20190924132822-0c50a64d7f26
)

6
go.sum
View File

@ -1,5 +1,3 @@
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/d2r2/go-bsbmp v0.0.0-20190515110334-3b4b3aea8375 h1:vdUOwcZdV+bBfGUUh5oPPWSzw9p+lBnNSuGgQwGpCH4=
github.com/d2r2/go-bsbmp v0.0.0-20190515110334-3b4b3aea8375/go.mod h1:3iz1WHlYJU9b4NJei+Q8G7DN3K05arcCMlOQ+qNCDjo=
github.com/d2r2/go-i2c v0.0.0-20181113114621-14f8dd4e89ce h1:Dog7PLNz1fPaXqHPOHonpERqsF57Oh4X76pM80T1GDY=
@ -19,8 +17,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/lib/pq v1.4.0 h1:TmtCFbH+Aw0AixwyttznSMQDgbR5Yed/Gg6S8Funrhc=
github.com/lib/pq v1.4.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=

80
main.go
View File

@ -2,10 +2,11 @@ package main
import (
"log"
"os"
"strings"
"github.com/Masterminds/semver"
"github.com/volker-raschek/flucky/cmd"
"github.com/volker-raschek/flucky/pkg/config"
"github.com/volker-raschek/flucky/pkg/daemon"
"github.com/volker-raschek/go-logger/pkg/logger"
)
var (
@ -13,13 +14,78 @@ var (
)
func main() {
sversion, err := semver.NewVersion(version)
// sversion, err := semver.NewVersion(version)
// if err != nil {
// log.Printf("The sematic versioning is invalid: %v", version)
// os.Exit(1)
// }
cnfString := `{
"device": {
"id": "d0c600bc-6eab-4b9b-b93f-dc0477b4658f",
"name": "poseidon",
"location": null,
"last_contact": null,
"creation_date": "2020-04-23T22:09:20.932+02:00"
},
"storage_endpoint": "postgres://postgres:postgres@markus-pc.trier.cryptic.systems:5432/postgres?sslmode=disable",
"sensors": [
{
"id": "1c180121-9e98-4053-a6f6-4ed7fad3935b",
"name": "bme280",
"location": "",
"wire_id": null,
"i2c_bus": 1,
"i2c_address": 118,
"gpio_number": null,
"model": "BME280",
"enabled": true,
"last_contact": null,
"tick_duration": "5s",
"device_id": "d0c600bc-6eab-4b9b-b93f-dc0477b4658f",
"creation_date": "2020-04-23T22:09:20.934+02:00"
},
{
"id": "5cebd127-8976-4b66-b1bb-e551f26c4af4",
"name": "ds18b20_2",
"location": "",
"wire_id": "28-02131dbffeaa",
"i2c_bus": null,
"i2c_address": null,
"gpio_number": null,
"model": "DS18B20",
"enabled": true,
"last_contact": null,
"tick_duration": "1m",
"device_id": "d0c600bc-6eab-4b9b-b93f-dc0477b4658f",
"creation_date": "2020-04-23T22:09:27.901+02:00"
},
{
"id": "579628cc-b667-4b56-b305-73256c0c9dce",
"name": "ds18b20",
"location": "",
"wire_id": "28-01131646f11d",
"i2c_bus": null,
"i2c_address": null,
"gpio_number": null,
"model": "DS18B20",
"enabled": true,
"last_contact": null,
"tick_duration": "1m",
"device_id": "d0c600bc-6eab-4b9b-b93f-dc0477b4658f",
"creation_date": "2020-04-23T22:09:56.468+02:00"
}
]
}`
cnf, err := config.Decode(strings.NewReader(cnfString))
if err != nil {
log.Printf("The sematic versioning is invalid: %v", version)
os.Exit(1)
log.Fatal(err)
}
err = cmd.Execute(sversion)
flogger := logger.NewDefaultLogger(logger.LogLevelDebug)
err = daemon.Start(cnf, flogger)
if err != nil {
log.Fatal(err)
}

View File

@ -1,158 +0,0 @@
package cli
import (
"errors"
"fmt"
"io"
"regexp"
"text/tabwriter"
"github.com/volker-raschek/flucky/pkg/config"
"github.com/volker-raschek/flucky/pkg/types"
)
var (
errorMeasuredValueIDNotValid = errors.New("Measured value id is not a valid uuid")
errorSensorIDNotValid = errors.New("Sensor id is not a valid uuid")
validUUID = regexp.MustCompile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
)
// GetSensorIDsByMeasuredValues returns commulated list of sensors by measured values
func GetSensorIDsByMeasuredValues(measuredValues []*types.MeasuredValue, cnf *config.Configuration) (map[string]*types.Sensor, error) {
sensors := make(map[string]*types.Sensor)
for _, measuredValue := range measuredValues {
// check if sensor id is valid
if !validUUID.MatchString(measuredValue.ID) {
return nil, errorMeasuredValueIDNotValid
}
// check if sensor id is valid
if !validUUID.MatchString(measuredValue.SensorID) {
return nil, errorSensorIDNotValid
}
if _, ok := sensors[measuredValue.SensorID]; !ok {
sensors[measuredValue.SensorID] = cnf.GetSensorByID(measuredValue.SensorID)
}
}
return sensors, nil
}
// PrintRGBLEDs displays a list with all configured RGBLEDs
func PrintRGBLEDs(cnf *config.Configuration, w io.Writer) {
// declare tabwriter
tw := tabwriter.NewWriter(w, 0, 0, 3, ' ', 0)
// headline
fmt.Fprintln(tw, "name\tlocation\tblue\tgreen\tred\tenabled\taction")
for _, rgbled := range cnf.RGBLEDs {
fmt.Fprintf(tw, "%v\t%v\t%v\t%v\t%v\t%v\t", rgbled.RGBLEDName, rgbled.RGBLEDLocation, *rgbled.BaseColorsToGPIO[types.BaseColorBlue], *rgbled.BaseColorsToGPIO[types.BaseColorGreen], *rgbled.BaseColorsToGPIO[types.BaseColorRed], rgbled.RGBLEDEnabled)
for action, color := range rgbled.ActionMapping {
fmt.Fprintf(tw, "%v=%v,", action, color)
}
fmt.Fprintf(tw, "\n")
}
tw.Flush()
}
// PrintSensors displays a list with all configured sensors
func PrintSensors(cnf *config.Configuration, w io.Writer) error {
// declar tabwriter
tw := tabwriter.NewWriter(w, 0, 0, 3, ' ', 0)
fmt.Fprint(tw, "name\tlocation\ttype\twire-id\ti2c-bus\ti2c-address\tgpio\ttick-duration\tenabled\n")
for _, sensor := range cnf.Sensors {
fmt.Fprintf(tw, "%v\t%v\t%v\t", sensor.Name, sensor.Location, sensor.Model)
if sensor.WireID != nil {
fmt.Fprintf(tw, "%v\t", *sensor.WireID)
} else {
fmt.Fprintf(tw, "\t")
}
if sensor.I2CBus != nil {
fmt.Fprintf(tw, "%v\t", *sensor.I2CBus)
} else {
fmt.Fprintf(tw, "\t")
}
if sensor.I2CAddress != nil {
fmt.Fprintf(tw, "%#v\t", *sensor.I2CAddress)
} else {
fmt.Fprintf(tw, "\t")
}
if sensor.GPIONumber != nil {
fmt.Fprintf(tw, "%v\t", *sensor.GPIONumber)
} else {
fmt.Fprintf(tw, "\t")
}
fmt.Fprintf(tw, "%v\t%v\n", sensor.TickDuration, sensor.Enabled)
}
tw.Flush()
return nil
}
// PrintMeasuredValues displays a list of measured values
func PrintMeasuredValues(measuredValues []*types.MeasuredValue, cnf *config.Configuration, w io.Writer) error {
sensors, err := GetSensorIDsByMeasuredValues(measuredValues, cnf)
if err != nil {
return err
}
// sort measured values for every sensor
orderedMeasuredValues := make(map[string][]*types.MeasuredValue)
for _, measuredValue := range measuredValues {
orderedMeasuredValues[measuredValue.SensorID] = append(orderedMeasuredValues[measuredValue.SensorID], measuredValue)
}
// declare tabwriter
tw := tabwriter.NewWriter(w, 0, 0, 3, ' ', 0)
// headlines
i := 0
for _, sensor := range sensors {
fmt.Fprintf(tw, "%v\t", sensor.FullName())
if i == len(sensors)-1 {
fmt.Fprintf(tw, "\n")
}
i++
}
// find sensor with the most measured values
maxLength := 0
for _, orderedMeasuredValue := range orderedMeasuredValues {
if len(orderedMeasuredValue) > maxLength {
maxLength = len(orderedMeasuredValue)
}
}
// body
for i := 0; i < maxLength; i++ {
for _, sensor := range sensors {
if len(orderedMeasuredValues[sensor.ID]) > i {
fmt.Fprintf(tw, "%3.3f\t", orderedMeasuredValues[sensor.ID][i].Value)
} else {
fmt.Fprint(tw, "\t")
}
}
fmt.Fprint(tw, "\n")
}
tw.Flush()
return err
}

View File

@ -1,92 +0,0 @@
package cli
import (
"testing"
"github.com/volker-raschek/flucky/pkg/config"
"github.com/volker-raschek/flucky/pkg/types"
"github.com/stretchr/testify/require"
)
type testCase struct {
goldenConfig *config.Configuration
goldenMeasuredValues []*types.MeasuredValue
expectedSensors map[string]*types.Sensor
expectedError error
}
var (
goldenSensors = []*types.Sensor{
&types.Sensor{
ID: "5b7c08c8-ee2a-4f88-b796-83658caae09d",
},
}
goldenSensorMap = map[string]*types.Sensor{
"5b7c08c8-ee2a-4f88-b796-83658caae09d": goldenSensors[0],
}
goldenConfig = &config.Configuration{
Sensors: goldenSensors,
}
goldenMeasuredValues = []*types.MeasuredValue{
&types.MeasuredValue{
ID: "4887b7cc-6b40-4293-bcc1-db1b5035a711",
SensorID: "5b7c08c8-ee2a-4f88-b796-83658caae09d",
},
}
measuredValuesWrongID = []*types.MeasuredValue{
&types.MeasuredValue{
ID: "b2784dfb-c1a4-428d-8897-e09a18e7d94d",
SensorID: "5b7c08c8-ee2a-4f88-b796-83658caae09d",
},
&types.MeasuredValue{
ID: "81ca1a60-dfea-42f3-a7",
SensorID: "5b7c08c8-ee2a-4f88-b796-83658caae09d",
},
}
measuredValuesWrongSensorID = []*types.MeasuredValue{
&types.MeasuredValue{
ID: "b2784dfb-c1a4-428d-8897-e09a18e7d94d",
SensorID: "5b7c08c8-ee2a-4f88-b796-83658caae09d",
},
&types.MeasuredValue{
ID: "b2784dfb-c1a4-428d-8897-e09a18e7d94d",
SensorID: "5b7c08c8-ee2a-4f88-b79",
},
}
)
func TestGetSensorIDsByMeasuredValues(t *testing.T) {
require := require.New(t)
testCases := []*testCase{
&testCase{
goldenConfig: goldenConfig,
goldenMeasuredValues: goldenMeasuredValues,
expectedSensors: goldenSensorMap,
expectedError: nil,
},
&testCase{
goldenConfig: goldenConfig,
goldenMeasuredValues: measuredValuesWrongID,
expectedSensors: nil,
expectedError: errorMeasuredValueIDNotValid,
},
&testCase{
goldenConfig: goldenConfig,
goldenMeasuredValues: measuredValuesWrongSensorID,
expectedSensors: nil,
expectedError: errorSensorIDNotValid,
},
}
for _, testCase := range testCases {
actualSensors, err := GetSensorIDsByMeasuredValues(testCase.goldenMeasuredValues, testCase.goldenConfig)
require.EqualValues(testCase.expectedError, err, "Not the expected error")
require.EqualValues(testCase.expectedSensors, actualSensors, "Returned sensor list not expected")
}
}

View File

@ -2,141 +2,92 @@ package config
import (
"fmt"
"net/url"
"os"
"path/filepath"
"regexp"
"time"
"github.com/volker-raschek/flucky/pkg/internal/format"
"github.com/volker-raschek/flucky/pkg/rgbled"
"github.com/volker-raschek/flucky/pkg/sensor"
"github.com/volker-raschek/flucky/pkg/types"
uuid "github.com/satori/go.uuid"
"github.com/volker-raschek/flucky/pkg/types"
)
// Configuration of flucky
type Configuration struct {
var (
validUUID = regexp.MustCompile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
)
// Config represent the configuration
type Config struct {
Device *types.Device `json:"device"`
StorageEndpoint string `json:"storage_endpoint"`
RGBLEDs []*types.RGBLED `json:"rgb_leds"`
Sensors []*types.Sensor `json:"sensors"`
}
// AddRGBLED add a new RGBLED
func (c *Configuration) AddRGBLED(rgbLED *types.RGBLED) error {
// check if RGBLEDID is a valid UUID string
if !validUUID.MatchString(rgbLED.RGBLEDID) {
rgbLED.RGBLEDID = uuid.NewV4().String()
}
// check if sensor name and sensor uuid already exists
for _, l := range c.RGBLEDs {
if l.RGBLEDName == rgbLED.RGBLEDName {
return fmt.Errorf("RGBLED %v already exists", rgbLED.RGBLEDName)
}
if l.RGBLEDID == rgbLED.RGBLEDID {
return fmt.Errorf("RGBLED %v with UUID %v already exists", rgbLED.RGBLEDName, rgbLED.RGBLEDID)
}
}
// check if sensor has a valid device id
if rgbLED.DeviceID != c.Device.ID {
rgbLED.DeviceID = c.Device.ID
}
// overwrite creation date
rgbLED.CreationDate = time.Now()
// check
c.RGBLEDs = append(c.RGBLEDs, rgbLED)
return nil
StorageEndpoint string `json:"storage_endpoint"`
}
// AddSensor add a new sensor
func (c *Configuration) AddSensor(sensor *types.Sensor) error {
func (cnf *Config) AddSensor(sensor *types.Sensor) error {
// check if ID is a valid UUID string
// Verify that a device is configured
if cnf.Device == nil {
return fmt.Errorf("No device configured")
}
// Define a new UUID if the current UUID is invalid
if !validUUID.MatchString(sensor.ID) {
sensor.ID = uuid.NewV4().String()
}
// Verify that the sensor has a valid name
if len(sensor.Name) <= 0 {
return fmt.Errorf("No sensor name defined")
}
// check if sensor name and sensor uuid already exists
for _, s := range c.Sensors {
if s.Name == sensor.Name {
return fmt.Errorf("Sensor %v already exists", s.Name)
for _, cnfSensor := range cnf.Sensors {
if cnfSensor.Name == sensor.Name ||
cnfSensor.ID == sensor.ID {
return fmt.Errorf("Sensor with same name or id already exist")
}
if s.ID == sensor.ID {
return fmt.Errorf("Sensor %v with UUID %v already exists", s.Name, s.ID)
}
if s.WireID != nil && sensor.WireID != nil {
if *s.WireID == *sensor.WireID {
return fmt.Errorf("Sensor with 1wire-id %v already exists as %v", *s.WireID, s.Name)
}
if cnfSensor.WireID != nil &&
sensor.WireID != nil &&
*cnfSensor.WireID == *sensor.WireID {
return fmt.Errorf("Sensor with same wire id already exist")
}
}
// check if sensor has a valid tick time
if _, err := time.ParseDuration(sensor.TickDuration); err != nil {
return fmt.Errorf("Can not parse tick duration: %v", sensor.TickDuration)
return fmt.Errorf("Failed to parse tick duration: %v", err)
}
// check if sensor has a valid device id
if sensor.DeviceID != c.Device.ID {
sensor.DeviceID = c.Device.ID
if sensor.DeviceID != cnf.Device.ID {
sensor.DeviceID = cnf.Device.ID
}
// overwrite creation date
sensor.CreationDate = format.FormatedTime()
//TODO: check if wire sensor exists in /dev/bus/w1/devices
// check
c.Sensors = append(c.Sensors, sensor)
return nil
}
// DisableRGBLED enables a rgb led by its name or its unique UUID
func (c *Configuration) DisableRGBLED(name string) error {
found := false
for _, rgbled := range c.RGBLEDs {
// disable sensor matched after name
if !validUUID.MatchString(name) &&
rgbled.RGBLEDName == name {
rgbled.RGBLEDEnabled = false
found = true
break
}
// disable sensor matched by uuid
if validUUID.MatchString(name) &&
rgbled.RGBLEDID == name {
rgbled.RGBLEDEnabled = false
found = true
break
// check if wire socket is available
if sensor.WireID != nil {
socketPath := filepath.Join("/sys/bus/w1/devices", *sensor.WireID, "/w1_slave")
if _, err := os.Stat(socketPath); os.IsNotExist(err) {
return fmt.Errorf("Wire socket not found: %v", socketPath)
}
}
if !found {
return fmt.Errorf("Can not found RGB-LED %v", name)
}
cnf.Sensors = append(cnf.Sensors, sensor)
return nil
}
// DisableSensor disables a sensor by its name or its unique UUID
func (c *Configuration) DisableSensor(name string) error {
func (cnf *Config) DisableSensor(name string) error {
found := false
for _, sensor := range c.Sensors {
for _, sensor := range cnf.Sensors {
// disable sensor matched after name
if !validUUID.MatchString(name) &&
@ -162,41 +113,11 @@ func (c *Configuration) DisableSensor(name string) error {
return nil
}
// EnableRGBLED enables a rgb led by its name or its unique UUID
func (c *Configuration) EnableRGBLED(name string) error {
found := false
for _, rgbled := range c.RGBLEDs {
// disable sensor matched after name
if !validUUID.MatchString(name) &&
rgbled.RGBLEDName == name {
rgbled.RGBLEDEnabled = true
found = true
break
}
// disable sensor matched by uuid
if validUUID.MatchString(name) &&
rgbled.RGBLEDID == name {
rgbled.RGBLEDEnabled = true
found = true
break
}
}
if !found {
return fmt.Errorf("Can not found RGB-LED %v", name)
}
return nil
}
// EnableSensor enables a sensor by its name or its unique UUID
func (c *Configuration) EnableSensor(name string) error {
func (cnf *Config) EnableSensor(name string) error {
found := false
for _, sensor := range c.Sensors {
for _, sensor := range cnf.Sensors {
// disable sensor matched after name
if !validUUID.MatchString(name) &&
@ -222,155 +143,10 @@ func (c *Configuration) EnableSensor(name string) error {
return nil
}
// GetHumiditySensors returns a list of humidity sensors
func (c *Configuration) GetHumiditySensors(option Option) []sensor.Sensor {
sensors := c.getHumiditySensors()
cachedSensors := make([]*types.Sensor, 0)
switch option {
case ENABLED:
for _, sensor := range sensors {
if sensor.Enabled {
cachedSensors = append(cachedSensors, sensor)
}
}
return c.convertSensors(cachedSensors)
case DISABLED:
for _, sensor := range sensors {
if !sensor.Enabled {
cachedSensors = append(cachedSensors, sensor)
}
}
return c.convertSensors(cachedSensors)
default:
return c.convertSensors(cachedSensors)
}
}
// GetHumiditySensorsByName returns a list of humidity sensors by name,
// uuid or wire-id
func (c *Configuration) GetHumiditySensorsByName(names []string) []sensor.Sensor {
configHumiditySensors := make(map[string]*types.Sensor, 0)
for _, name := range names {
for _, s := range c.getHumiditySensors() {
switch name {
case s.ID:
configHumiditySensors[s.ID] = s
case s.Name:
configHumiditySensors[s.ID] = s
}
}
}
humiditySensors := make([]*types.Sensor, 0)
for _, cs := range configHumiditySensors {
humiditySensors = append(humiditySensors, cs)
}
return c.convertSensors(humiditySensors)
}
// GetPressureSensors returns a list of pressure sensors
func (c *Configuration) GetPressureSensors(option Option) []sensor.Sensor {
sensors := c.getPressureSensors()
cachedSensors := make([]*types.Sensor, 0)
switch option {
case ENABLED:
for _, sensor := range sensors {
if sensor.Enabled {
cachedSensors = append(cachedSensors, sensor)
}
}
return c.convertSensors(cachedSensors)
case DISABLED:
for _, sensor := range sensors {
if !sensor.Enabled {
cachedSensors = append(cachedSensors, sensor)
}
}
return c.convertSensors(cachedSensors)
default:
return c.convertSensors(cachedSensors)
}
}
// GetPressureSensorsByName returns a list of pressure sensors by name,
// uuid or wire-id
func (c *Configuration) GetPressureSensorsByName(names []string) []sensor.Sensor {
configPressureSensors := make(map[string]*types.Sensor, 0)
for _, name := range names {
for _, s := range c.getPressureSensors() {
switch name {
case s.ID:
configPressureSensors[s.ID] = s
case s.Name:
configPressureSensors[s.ID] = s
}
}
}
pressureSensors := make([]*types.Sensor, 0)
for _, cs := range configPressureSensors {
pressureSensors = append(pressureSensors, cs)
}
return c.convertSensors(pressureSensors)
}
func (c *Configuration) GetRGBLEDs(option Option) []rgbled.RGBLED {
rgbLEDs := c.RGBLEDs
switch option {
case ENABLED:
for i, rgbLED := range c.RGBLEDs {
if !rgbLED.RGBLEDEnabled {
rgbLEDs = append(rgbLEDs[:i], rgbLEDs[i+1:]...)
}
}
return c.convertRGBLEDs(rgbLEDs)
case DISABLED:
for i, rgbLED := range c.RGBLEDs {
if rgbLED.RGBLEDEnabled {
rgbLEDs = append(rgbLEDs[:i], rgbLEDs[i+1:]...)
}
}
return c.convertRGBLEDs(rgbLEDs)
default:
return c.convertRGBLEDs(rgbLEDs)
}
}
func (c *Configuration) GetRGBLEDsByName(names []string) []rgbled.RGBLED {
configRGBLEDs := make(map[string]*types.RGBLED, 0)
for _, name := range names {
for _, led := range c.RGBLEDs {
switch name {
case led.RGBLEDID:
configRGBLEDs[led.RGBLEDID] = led
case led.RGBLEDName:
configRGBLEDs[led.RGBLEDID] = led
}
}
}
rgbLEDs := make([]*types.RGBLED, 0)
for _, rgbLED := range configRGBLEDs {
rgbLEDs = append(rgbLEDs, rgbLED)
}
return c.convertRGBLEDs(rgbLEDs)
}
// GetSensorByID returns a sensor matched by his id. If no sensor has this id,
// the function returns nil
func (c *Configuration) GetSensorByID(id string) *types.Sensor {
for _, sensor := range c.Sensors {
func (cnf *Config) GetSensorByID(id string) *types.Sensor {
for _, sensor := range cnf.Sensors {
if sensor.ID == id {
return sensor
}
@ -378,239 +154,21 @@ func (c *Configuration) GetSensorByID(id string) *types.Sensor {
return nil
}
// GetSensors returns a list of humidity sensors
func (c *Configuration) GetSensors(option Option) []sensor.Sensor {
cachedSensors := make([]*types.Sensor, 0)
switch option {
case ENABLED:
for _, sensor := range c.Sensors {
if sensor.Enabled {
cachedSensors = append(cachedSensors, sensor)
}
}
return c.convertSensors(cachedSensors)
case DISABLED:
for _, sensor := range c.Sensors {
if !sensor.Enabled {
cachedSensors = append(cachedSensors, sensor)
}
}
return c.convertSensors(cachedSensors)
default:
return c.convertSensors(cachedSensors)
}
}
// GetStorageEndpointURL returns a parsed storage endpoint url
func (c *Configuration) GetStorageEndpointURL() (*url.URL, error) {
storageEndpointURL, err := url.Parse(c.StorageEndpoint)
if err != nil {
return nil, fmt.Errorf("Can not parse storage endpoint URL")
}
return storageEndpointURL, nil
}
// GetTemperatureSensors returns a list of temperature sensors
func (c *Configuration) GetTemperatureSensors(option Option) []sensor.Sensor {
sensors := c.getTemperatureSensors()
cachedSensors := make([]*types.Sensor, 0)
switch option {
case ENABLED:
for _, sensor := range sensors {
if sensor.Enabled {
cachedSensors = append(cachedSensors, sensor)
}
}
return c.convertSensors(cachedSensors)
case DISABLED:
for _, sensor := range sensors {
if !sensor.Enabled {
cachedSensors = append(cachedSensors, sensor)
}
}
return c.convertSensors(cachedSensors)
default:
return c.convertSensors(cachedSensors)
}
}
// GetTemperatureSensorsByName returns a list of temperature sensors by name,
// uuid or wire-id
func (c *Configuration) GetTemperatureSensorsByName(names []string) []sensor.Sensor {
configTemperatureSensors := make(map[string]*types.Sensor, 0)
for _, name := range names {
for _, s := range c.getTemperatureSensors() {
switch name {
case s.ID:
configTemperatureSensors[s.ID] = s
case s.Name:
configTemperatureSensors[s.ID] = s
}
}
}
temperatureSensors := make([]*types.Sensor, 0)
for _, cs := range configTemperatureSensors {
temperatureSensors = append(temperatureSensors, cs)
}
return c.convertSensors(temperatureSensors)
}
// RemoveRGBLED deletes a LED by its name or its unique UUID
func (c *Configuration) RemoveRGBLED(name string) error {
for i, rgbLED := range c.RGBLEDs {
// remove machted name
if !validUUID.MatchString(name) &&
rgbLED.RGBLEDName == name {
c.RGBLEDs = append(c.RGBLEDs[:i], c.RGBLEDs[i+1:]...)
return nil
}
// remove machted uuid
if validUUID.MatchString(name) &&
rgbLED.RGBLEDID == name {
c.RGBLEDs = append(c.RGBLEDs[:i], c.RGBLEDs[i+1:]...)
return nil
}
}
return fmt.Errorf("Can not find RGBLED %v", name)
}
// RemoveSensor deletes a sensor by its name or its unique UUID
func (c *Configuration) RemoveSensor(name string) error {
for i, sensor := range c.Sensors {
func (cnf *Config) RemoveSensor(name string) error {
for i, sensor := range cnf.Sensors {
// remove machted name
if !validUUID.MatchString(name) &&
sensor.Name == name {
c.Sensors = append(c.Sensors[:i], c.Sensors[i+1:]...)
cnf.Sensors = append(cnf.Sensors[:i], cnf.Sensors[i+1:]...)
return nil
}
// remove machted uuid
if validUUID.MatchString(name) &&
sensor.ID == name {
c.Sensors = append(c.Sensors[:i], c.Sensors[i+1:]...)
cnf.Sensors = append(cnf.Sensors[:i], cnf.Sensors[i+1:]...)
return nil
}
}
return fmt.Errorf("Can not find sensor %v", name)
}
// RenameRGBLED renames a sensor identified by the name or the UUID
func (c *Configuration) RenameRGBLED(oldName, newName string) error {
for _, rgbled := range c.RGBLEDs {
if rgbled.RGBLEDName == oldName ||
rgbled.RGBLEDID == oldName {
rgbled.RGBLEDName = newName
return nil
}
}
return fmt.Errorf("Could not find rgb-led %v to replace into with %v", oldName, newName)
}
// RenameSensor renames a sensor identified by the name or the UUID
func (c *Configuration) RenameSensor(oldName, newName string) error {
for _, sensor := range c.Sensors {
if sensor.Name == oldName ||
sensor.ID == oldName {
sensor.Name = newName
return nil
}
}
return fmt.Errorf("Could not find remote %v to replace into with %v", oldName, newName)
}
func (c *Configuration) SetStorageEndpoint(storageEndpoint string) error {
storageEndpointURL, err := url.Parse(storageEndpoint)
if err != nil {
return fmt.Errorf("Can not prase sorage endpoint url: %v", err)
}
supportedStorageEndpoints := []string{"file", "postgres"}
found := false
for _, supportedStorageEndpoint := range supportedStorageEndpoints {
if supportedStorageEndpoint == storageEndpointURL.Scheme {
found = true
break
}
}
if !found {
return fmt.Errorf("Storage endpoint scheme not supported")
}
c.StorageEndpoint = storageEndpointURL.String()
return nil
}
func (c *Configuration) convertSensors(sensors []*types.Sensor) []sensor.Sensor {
cachedSensors := make([]sensor.Sensor, 0)
for _, s := range sensors {
switch s.Model {
case types.BME280:
cachedSensors = append(cachedSensors, &sensor.BME280{
Sensor: s,
})
case types.DHT11:
cachedSensors = append(cachedSensors, &sensor.DHT11{
Sensor: s,
})
case types.DHT22:
cachedSensors = append(cachedSensors, &sensor.DHT22{
Sensor: s,
})
case types.DS18B20:
cachedSensors = append(cachedSensors, &sensor.DS18B20{
Sensor: s,
})
}
}
return cachedSensors
}
func (c *Configuration) convertRGBLEDs(rgbLEDs []*types.RGBLED) []rgbled.RGBLED {
leds := make([]rgbled.RGBLED, 0)
for _, rgbLED := range rgbLEDs {
leds = append(leds, &rgbled.DefaultRGBLED{
RGBLED: rgbLED,
})
}
return leds
}
func (c *Configuration) getHumiditySensors() []*types.Sensor {
humiditySensors := make([]*types.Sensor, 0)
for _, s := range c.Sensors {
if _, ok := humiditySensorModels[s.Model]; ok {
humiditySensors = append(humiditySensors, s)
}
}
return humiditySensors
}
func (c *Configuration) getPressureSensors() []*types.Sensor {
pressureSensors := make([]*types.Sensor, 0)
for _, s := range c.Sensors {
if _, ok := pressureSensorModels[s.Model]; ok {
pressureSensors = append(pressureSensors, s)
}
}
return pressureSensors
}
func (c *Configuration) getTemperatureSensors() []*types.Sensor {
temperatureSensors := make([]*types.Sensor, 0)
for _, s := range c.Sensors {
if _, ok := temperatureSensorModels[s.Model]; ok {
temperatureSensors = append(temperatureSensors, s)
}
}
return temperatureSensors
}

40
pkg/config/config_test.go Normal file
View File

@ -0,0 +1,40 @@
package config_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/volker-raschek/flucky/pkg/config"
"github.com/volker-raschek/flucky/pkg/types"
)
func TestAddRemoveSensor(t *testing.T) {
require := require.New(t)
// Test: No device configured
cnf := new(config.Config)
err := cnf.AddSensor(&types.Sensor{ID: "1aa32c9a-b505-456d-868b-0403344f4cdf"})
require.Error(err)
// wireID := "sdfsdff"
// i2cBus := 1
// i2cAddress := 78
cnf.Device = &types.Device{ID: "d6176a06-2b0b-41af-a85c-913e8f61c35d"}
testCases := map[*types.Sensor]error{
{ID: "1aa32c9a-b505-456d-868b-0403344f4cdf", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d"}: fmt.Errorf("No sensor name defined"),
{ID: "1aa32c9a-b505-456d-868b-0403344f4cdf", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test01"}: fmt.Errorf("Failed to parse tick duration: time: invalid duration "),
{ID: "1aa32c9a-b505-456d-868b-0403344f4cdf", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test01", TickDuration: "5s"}: nil,
{ID: "1aa32c9a-b505-456d-868b-0403344f4cdf", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test01", TickDuration: "5s"}: fmt.Errorf("Sensor with same name or id already exist"),
// {ID: "f90cfc18-f141-4cfd-a8d2-fb40082de5cc", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test01", TickDuration: "5s"}: fmt.Errorf("Sensor with same name or id already exist"),
// {ID: "860a9922-62cb-4c9b-b5af-5fa783cebe9d", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test02", TickDuration: "5s", WireID: &wireID}: fmt.Errorf("Wire socket not found: /sys/bus/w1/devices/sdfsdff/w1_slave"),
// {ID: "9be8989c-b2a1-4401-a82f-d6989ec226fe", DeviceID: "d6176a06-2b0b-41af-a85c-913e8f61c35d", Name: "Test02", TickDuration: "5s"}: nil,
}
for sensor, expectedErr := range testCases {
err := cnf.AddSensor(sensor)
require.Equal(expectedErr, err)
}
}

View File

@ -3,57 +3,59 @@ package config
import (
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
)
var validUUID = regexp.MustCompile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
// Decode a configuration from a reader
func Decode(r io.Reader) (*Config, error) {
cnf := new(Config)
jsonDecoder := json.NewDecoder(r)
if err := jsonDecoder.Decode(&cnf); err != nil {
return nil, fmt.Errorf("Can not unmarshal JSON: %v", err)
}
return cnf, nil
}
// Encode a configuration to a writer
func Encode(cnf *Config, w io.Writer) error {
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
err := encoder.Encode(cnf)
if err != nil {
return fmt.Errorf("Error encoding config to json: %v", err)
}
return nil
}
// Read the configuration file
func Read(configFile string) (*Configuration, error) {
fc := &Configuration{}
func Read(configFile string) (*Config, error) {
f, err := os.Open(configFile)
if err != nil {
return nil, fmt.Errorf("Can not open file %v: %v", configFile, err)
}
defer f.Close()
jsonDecoder := json.NewDecoder(f)
if err := jsonDecoder.Decode(&fc); err != nil {
return nil, fmt.Errorf("Can not unmarshal JSON: %v", err)
}
return fc, nil
return Decode(f)
}
// Write the configuration into a file, specified by the configuration filepath
func Write(cfg *Configuration, configFile string) error {
func Write(cnf *Config, configFile string) error {
if _, err := os.Stat(configFile); os.IsNotExist(err) {
configDir := filepath.Dir(configFile)
err := os.MkdirAll(configDir, os.ModeDir)
if err != nil {
return fmt.Errorf("Can not create config directory %v: %v", configDir, err)
return fmt.Errorf("Failed to create config directory %v: %v", configDir, err)
}
}
f, err := os.Create(configFile)
if err != nil {
return fmt.Errorf("Can not write config file: %v", err)
return fmt.Errorf("Failed not create config file %v: %v", configFile, err)
}
defer f.Close()
encoder := json.NewEncoder(f)
encoder.SetIndent("", " ")
err = encoder.Encode(cfg)
if err != nil {
return fmt.Errorf("Error in encoding struct to json: %v", err)
}
return nil
return Encode(cnf, f)
}

View File

@ -1,14 +0,0 @@
package config
type Option int
const (
// ALL specified enabled and disabled items
ALL Option = iota + 1
// ENABLED items
ENABLED
// DISABLED items
DISABLED
)

View File

@ -1,22 +0,0 @@
package config
import "github.com/volker-raschek/flucky/pkg/types"
var (
humiditySensorModels = map[types.SensorModel]types.SensorModel{
types.BME280: types.BME280,
types.DHT11: types.DHT11,
types.DHT22: types.DHT22,
}
pressureSensorModels = map[types.SensorModel]types.SensorModel{
types.BME280: types.BME280,
}
temperatureSensorModels = map[types.SensorModel]types.SensorModel{
types.BME280: types.BME280,
types.DHT11: types.DHT11,
types.DHT22: types.DHT22,
types.DS18B20: types.DS18B20,
}
)

View File

@ -1,98 +0,0 @@
package daemon
import (
"context"
"net/url"
"sync"
"github.com/volker-raschek/flucky/pkg/storage"
"github.com/volker-raschek/flucky/pkg/storage/logfile"
"github.com/volker-raschek/flucky/pkg/types"
)
type cacheStore struct {
compression bool
cache []*types.MeasuredValue
mux *sync.Mutex
round float64
URL *url.URL
}
func (cs *cacheStore) Add(measuredValue *types.MeasuredValue) {
cs.mux.Lock()
defer cs.mux.Unlock()
cs.cache = append(cs.cache, measuredValue)
}
func (cs *cacheStore) Flush(measuredValue *types.MeasuredValue) *cacheStore {
cs.mux.Lock()
defer cs.mux.Unlock()
cs.cache = make([]*types.MeasuredValue, 0)
return cs
}
func (cs *cacheStore) Get(id string) *types.MeasuredValue {
cs.mux.Lock()
defer cs.mux.Unlock()
for _, measuredValue := range cs.cache {
if measuredValue.ID == id {
return measuredValue
}
}
return nil
}
func (cs *cacheStore) Size() int {
cs.mux.Lock()
defer cs.mux.Unlock()
return len(cs.cache)
}
func (cs *cacheStore) WriteToEndpoint() error {
cs.mux.Lock()
defer cs.mux.Unlock()
defer func() { cs.cache = make([]*types.MeasuredValue, 0) }()
switch cs.URL.Scheme {
case "file":
return cs.logfile()
case "postgres":
return cs.postgres()
}
return nil
}
func (cs *cacheStore) logfile() error {
newMeasuredValues := make([]*types.MeasuredValue, 0)
for _, measuredValue := range cs.cache {
newMeasuredValues = append(newMeasuredValues, measuredValue)
}
if cs.round != 0 {
storage.Round(newMeasuredValues, cs.round)
}
measuredLogfile := logfile.New(cs.URL.Path)
measuredValues, err := measuredLogfile.Read()
if err != nil {
return err
}
measuredValues = append(measuredValues, newMeasuredValues...)
if cs.compression {
measuredValues = storage.Compression(measuredValues)
}
err = measuredLogfile.Write(measuredValues)
if err != nil {
return err
}
return nil
}
func (cs *cacheStore) postgres() error {
ctx := context.Background()
return storage.Write(ctx, cs.cache, cs.URL)
}

View File

@ -2,81 +2,77 @@ package daemon
import (
"context"
"fmt"
"os"
"os/signal"
"sync"
"syscall"
"github.com/volker-raschek/flucky/pkg/config"
"github.com/volker-raschek/flucky/pkg/sensor"
"github.com/volker-raschek/flucky/pkg/storage"
"github.com/volker-raschek/flucky/pkg/types"
"github.com/volker-raschek/go-logger/pkg/logger"
)
var (
flogger = logger.NewSilentLogger()
)
func Start(cnf *config.Config, flogger logger.Logger) error {
func SetLogger(logger logger.Logger) {
flogger = logger
}
sensors := make([]sensor.Sensor, 0)
for _, cnfSensor := range cnf.Sensors {
if !cnfSensor.Enabled {
continue
}
// Start the daemon
func Start(cnf *config.Configuration, cacheSize uint, compression bool, round float64) error {
sensor, err := sensor.New(cnfSensor)
if err != nil {
return err
}
sensors = append(sensors, sensor)
}
storageEndpointURL, err := cnf.GetStorageEndpointURL()
measuredValueChannel := make(chan *types.MeasuredValue, 0)
// load storage endpoint
storageEndpoint, err := storage.New(cnf.StorageEndpoint, flogger)
if err != nil {
return err
}
cache := &cacheStore{
compression: compression,
cache: make([]*types.MeasuredValue, 0),
mux: new(sync.Mutex),
round: round,
URL: storageEndpointURL,
interruptChannel := make(chan os.Signal, 1)
signal.Notify(interruptChannel, os.Kill, syscall.SIGTERM)
// Collection
parentCtx := context.Background()
// Insert device if not exist
device, _ := storageEndpoint.SelectDevice(parentCtx, cnf.Device.ID)
if device == nil {
if err := storageEndpoint.InsertDevice(parentCtx, cnf.Device); err != nil {
return err
}
}
// Insert sensors if not exist
for _, cnfSensor := range cnf.Sensors {
sensor, _ := storageEndpoint.SelectSensor(parentCtx, cnfSensor.ID)
if sensor == nil {
if err := storageEndpoint.InsertSensor(parentCtx, cnfSensor); err != nil {
return err
}
}
}
// Context
parentCtx := context.Background()
ctx, cancel := context.WithCancel(parentCtx)
// channels
debugChannel := make(chan string, 0)
infoChannel := make(chan string, 0)
warnChannel := make(chan string, 0)
errorChannel := make(chan error, 0)
fatalChannel := make(chan error, 1)
interruptChannel := make(chan os.Signal, 1)
signal.Notify(interruptChannel, os.Interrupt, os.Kill, syscall.SIGTERM)
measuredValueChannel := make(chan *types.MeasuredValue, 0)
// Info
flogger.Debug("Use compression: %v", compression)
flogger.Debug("Round values to: %v", round)
// Init semaphoreChannel
semaphoreChannels := make(map[string]chan struct{})
for _, sensor := range cnf.GetSensors(config.ENABLED) {
semaphoreChannels[sensor.GetID()] = make(chan struct{}, 1)
}
// Start producers
for _, s := range cnf.GetSensors(config.ENABLED) {
// start go routine for each sensor
for _, s := range sensors {
go func(sensor sensor.Sensor) {
// run forever
for {
select {
case <-ctx.Done():
return
case <-semaphoreChannels[sensor.GetID()]:
case <-sensor.GetTicker().C:
measuredValues, err := sensor.Read()
if err != nil {
errorChannel <- err
return
flogger.Error("%v", err)
continue
}
for _, measuredValue := range measuredValues {
measuredValueChannel <- measuredValue
@ -84,98 +80,36 @@ func Start(cnf *config.Configuration, cacheSize uint, compression bool, round fl
}
}
}(s)
// start ticker for each sensor
go func(sensor sensor.Sensor) {
for {
select {
case <-ctx.Done():
return
case <-sensor.GetTicker().C:
semaphoreChannels[sensor.GetID()] <- struct{}{}
}
}
}(s)
}
go func() {
for {
select {
case <-ctx.Done():
debugChannel <- fmt.Sprintf("Stop consumer of measured values: Closed context: %v", ctx.Err().Error())
return
case measuredValue := <-measuredValueChannel:
cache.Add(measuredValue)
debugChannel <- fmt.Sprintf("CacheStore ID: %v, Sensor ID: %v, Type: %v, Value: %v", cache.Size(), measuredValue.SensorID, measuredValue.ValueType, measuredValue.Value)
if cache.Size() >= int(cacheSize) {
debugChannel <- fmt.Sprint("Write cache into storage endpoint")
err := cache.WriteToEndpoint()
if err != nil {
errorChannel <- err
}
}
}
}
}()
measuredValues := make([]*types.MeasuredValue, 0, 10)
for {
select {
case debug, _ := <-debugChannel:
flogger.Debug("%v", debug)
case info, _ := <-infoChannel:
flogger.Info("%v", info)
case warn, _ := <-warnChannel:
flogger.Warn("%v", warn)
case err, _ := <-errorChannel:
flogger.Error("%v", err)
case fatal, _ := <-fatalChannel:
flogger.Fatal("Received a fatal error: %v", fatal)
case measuredValue := <-measuredValueChannel:
flogger.Debug("%v\t%v\t%v", measuredValue.ID, measuredValue.ValueType, measuredValue.Value)
measuredValues = append(measuredValues, measuredValue)
if cap(measuredValues) == len(measuredValues) {
flogger.Debug("Flush cache")
err := storageEndpoint.InsertMeasuredValues(ctx, measuredValues)
if err != nil {
flogger.Error("%v", err)
}
measuredValues = make([]*types.MeasuredValue, 0, 10)
}
case <-interruptChannel:
flogger.Debug("Write %v cached values into storage endpoint", cache.Size())
err := cache.WriteToEndpoint()
cancel()
close(measuredValueChannel)
err := storageEndpoint.InsertMeasuredValues(ctx, measuredValues)
if err != nil {
flogger.Error("%v", err)
}
flogger.Debug("Close context")
cancel()
flogger.Debug("Close channels")
close(debugChannel)
close(infoChannel)
close(warnChannel)
close(errorChannel)
close(interruptChannel)
return nil
break
}
}
return nil
}
// func checkDeviceInDatabase(ctx context.Context, device *types.Device, database db.Database) {
// _, err := database.SelectDeviceByID(ctx, device.ID)
// if err != nil {
// flogger.Debug("It's seems the current device is not registered in the database. Register the device now")
// err2 := database.InsertDevices(ctx, []*types.Device{device})
// if err2 != nil {
// flogger.Fatal("Can not register device into database: %v", err2)
// }
// flogger.Debug("Device successfully registered into the database")
// return
// }
// flogger.Debug("Device already registered into the database")
// }
// func checkSensorsInDatabase(ctx context.Context, sensors []*types.Sensor, database db.Database) {
// for _, sensor := range sensors {
// _, err := database.SelectSensorByID(ctx, sensor.ID)
// if err != nil {
// flogger.Debug("It's seems the sensor %v is not registered in the database. Register the sensor now", sensor.Name)
// err2 := database.InsertSensors(ctx, []*types.Sensor{sensor})
// if err2 != nil {
// flogger.Fatal("Can not register sensor %v into database: %v", sensor.Name, err2)
// }
// flogger.Debug("Sensor %v successfully registered into the database", sensor.Name)
// continue
// }
// flogger.Debug("Sensor %v is already registered into the database", sensor.Name)
// }
// }

View File

@ -1,16 +0,0 @@
package collect
func Errors(errorChannel <-chan error) []error {
errorList := make([]error, 0)
for {
select {
case err, more := <-errorChannel:
if more {
errorList = append(errorList, err)
continue
}
default:
return errorList
}
}
}

View File

@ -1,20 +0,0 @@
package collect
import (
"github.com/go-flucky/flucky/pkg/types"
)
func MeasuredValues(measuredValueChannel <-chan *types.MeasuredValue) []*types.MeasuredValue {
cachedMeasuredValues := make([]*types.MeasuredValue, 0)
for {
select {
case measuredValue, more := <-measuredValueChannel:
if more {
cachedMeasuredValues = append(cachedMeasuredValues, measuredValue)
continue
}
default:
return cachedMeasuredValues
}
}
}

View File

@ -1,30 +0,0 @@
package distribute
import (
"context"
"github.com/volker-raschek/flucky/pkg/types"
)
func MeasuredValues(ctx context.Context, channels int, inputChannel <-chan *types.MeasuredValue) []chan *types.MeasuredValue {
outputChannels := make([]chan *types.MeasuredValue, channels)
for i := 0; i <= channels; i++ {
outputChannel := make(chan *types.MeasuredValue)
outputChannels = append(outputChannels, outputChannel)
}
go func(ctx context.Context, inputChannel <-chan *types.MeasuredValue, outputChannels []chan *types.MeasuredValue) {
for {
select {
case <-ctx.Done():
return
case measuredValue, _ := <-inputChannel:
for _, outputChannel := range outputChannels {
outputChannel <- measuredValue
}
}
}
}(ctx, inputChannel, outputChannels)
return outputChannels
}

View File

@ -1,18 +0,0 @@
package prittyprint
import "fmt"
func FormatErrors(errors []error) error {
if len(errors) > 0 {
errMsg := ""
for i, err := range errors {
if i == 0 {
errMsg = fmt.Sprintf("%v", err.Error())
} else {
errMsg = fmt.Sprintf("%v\n%v", errMsg, err.Error())
}
}
return fmt.Errorf(errMsg)
}
return nil
}

View File

@ -1,239 +0,0 @@
package rgbled
import (
"fmt"
"github.com/volker-raschek/flucky/pkg/types"
"github.com/stianeikeland/go-rpio"
)
// DefaultRGBLED is a RGBLED which implement all functions of the interface
// RGBLED
type DefaultRGBLED struct {
*types.RGBLED
}
// Blue makes the RGBLED shine in blue
func (rgbled *DefaultRGBLED) Blue() error {
gpios := []*types.GPIO{
rgbled.BaseColorsToGPIO[types.BaseColorBlue],
}
if err := rgbled.Off(); err != nil {
return err
}
if err := rgbled.switchColors(gpios, true); err != nil {
return fmt.Errorf("Can not operate with GPIOs: %v", err)
}
return nil
}
// Green makes the RGBLED shine in green
func (rgbled *DefaultRGBLED) Green() error {
gpios := []*types.GPIO{
rgbled.BaseColorsToGPIO[types.BaseColorGreen],
}
if err := rgbled.Off(); err != nil {
return err
}
if err := rgbled.switchColors(gpios, true); err != nil {
return fmt.Errorf("Can not operate with GPIOs: %v", err)
}
return nil
}
// Off turns on the RGBLED off
func (rgbled *DefaultRGBLED) Off() error {
gpios := []*types.GPIO{
rgbled.BaseColorsToGPIO[types.BaseColorBlue],
rgbled.BaseColorsToGPIO[types.BaseColorGreen],
rgbled.BaseColorsToGPIO[types.BaseColorRed],
}
if err := rgbled.switchColors(gpios, false); err != nil {
return fmt.Errorf("Can not turn GPIOs off: %v", err)
}
return nil
}
// On turns on the RGBLED
func (rgbled *DefaultRGBLED) On() error {
return rgbled.White()
}
// Purple makes the RGBLED shine in purple
func (rgbled *DefaultRGBLED) Purple() error {
gpios := []*types.GPIO{
rgbled.BaseColorsToGPIO[types.BaseColorBlue],
rgbled.BaseColorsToGPIO[types.BaseColorRed],
}
if err := rgbled.Off(); err != nil {
return err
}
if err := rgbled.switchColors(gpios, true); err != nil {
return fmt.Errorf("Can not operate with GPIOs: %v", err)
}
return nil
}
// Red makes the RGBLED shine in red
func (rgbled *DefaultRGBLED) Red() error {
gpios := []*types.GPIO{
rgbled.BaseColorsToGPIO[types.BaseColorRed],
}
if err := rgbled.Off(); err != nil {
return err
}
if err := rgbled.switchColors(gpios, true); err != nil {
return fmt.Errorf("Can not operate with GPIOs: %v", err)
}
return nil
}
// Turquoise makes the RGBLED shine in turquoise
func (rgbled *DefaultRGBLED) Turquoise() error {
gpios := []*types.GPIO{
rgbled.BaseColorsToGPIO[types.BaseColorBlue],
rgbled.BaseColorsToGPIO[types.BaseColorGreen],
}
if err := rgbled.Off(); err != nil {
return err
}
if err := rgbled.switchColors(gpios, true); err != nil {
return fmt.Errorf("Can not operate with GPIOs: %v", err)
}
return nil
}
// White makes the RGBLED shine in white
func (rgbled *DefaultRGBLED) White() error {
gpios := []*types.GPIO{
rgbled.BaseColorsToGPIO[types.BaseColorBlue],
rgbled.BaseColorsToGPIO[types.BaseColorGreen],
rgbled.BaseColorsToGPIO[types.BaseColorRed],
}
if err := rgbled.Off(); err != nil {
return err
}
if err := rgbled.switchColors(gpios, true); err != nil {
return fmt.Errorf("Can not operate with GPIOs: %v", err)
}
return nil
}
// Yellow makes the RGBLED shine in yellow
func (rgbled *DefaultRGBLED) Yellow() error {
gpios := []*types.GPIO{
rgbled.BaseColorsToGPIO[types.BaseColorGreen],
rgbled.BaseColorsToGPIO[types.BaseColorRed],
}
if err := rgbled.Off(); err != nil {
return err
}
if err := rgbled.switchColors(gpios, true); err != nil {
return fmt.Errorf("Can not operate with GPIOs: %v", err)
}
return nil
}
// Error makes the RGBLED shine in the error specified color
func (rgbled *DefaultRGBLED) Error() error {
return rgbled.switchColorBasedOnAction(rgbled.ActionMapping[types.LEDActionError])
}
// Logfile makes the RGBLED shine in the logfile specified color
func (rgbled *DefaultRGBLED) Logfile() error {
return rgbled.switchColorBasedOnAction(rgbled.ActionMapping[types.LEDActionLogfile])
}
// Sync makes the RGBLED shine in the sync specified color
func (rgbled *DefaultRGBLED) Sync() error {
return rgbled.switchColorBasedOnAction(rgbled.ActionMapping[types.LEDActionSync])
}
// Warn makes the RGBLED shine in the warn specified color
func (rgbled *DefaultRGBLED) Warn() error {
return rgbled.switchColorBasedOnAction(rgbled.ActionMapping[types.LEDActionWarn])
}
// Run makes the RGBLED shine in the run specified color
func (rgbled *DefaultRGBLED) Run() error {
return rgbled.switchColorBasedOnAction(rgbled.ActionMapping[types.LEDActionRun])
}
func (rgbled *DefaultRGBLED) switchColors(gpios []*types.GPIO, on bool) error {
if err := rpio.Open(); err != nil {
return fmt.Errorf("Cam not open rpio connection: %v", err)
}
defer rpio.Close()
for _, gpio := range gpios {
gpioInt, err := types.GPIOToInt(*gpio)
if err != nil {
return fmt.Errorf("Can not determine %v into integer: %v", gpio, err)
}
pin := rpio.Pin(gpioInt)
// if rpio.DetectEdge(rpio.P rpio.AnyEdge) {
// log.Println("Test")
// }
pin.Pull(rpio.PullOff)
pin.Output()
if on {
pin.High()
} else {
pin.Low()
}
}
return nil
}
func (rgbled *DefaultRGBLED) switchColorBasedOnAction(action types.LEDColor) error {
switch action {
case types.LEDColorBlue:
return rgbled.Blue()
case types.LEDColorGreen:
return rgbled.Green()
case types.LEDColorNone:
return rgbled.Off()
case types.LEDColorPurple:
return rgbled.Purple()
case types.LEDColorRed:
return rgbled.Red()
case types.LEDColorTurquoise:
return rgbled.Turquoise()
case types.LEDColorWhite:
return rgbled.White()
case types.LEDColorYellow:
return rgbled.Yellow()
default:
return rgbled.Off()
}
}

View File

@ -1,21 +0,0 @@
package rgbled
// RGBLED is an interface that discribes all needed functions for a RGBLED
type RGBLED interface {
Blue() error
Green() error
Purple() error
Red() error
Turquoise() error
White() error
Yellow() error
Error() error
Logfile() error
Run() error
Sync() error
Warn() error
On() error
Off() error
}

View File

@ -1,275 +0,0 @@
package rgbled
import (
"sync"
"github.com/volker-raschek/flucky/pkg/internal/collect"
"github.com/volker-raschek/flucky/pkg/internal/prittyprint"
"github.com/volker-raschek/flucky/pkg/types"
)
// Blue makes all RGB-LEDs which are passed as parameters light up blue.
func Blue(rgbLEDs []RGBLED) error {
color := types.LEDColorBlue
if err := operate(rgbLEDs, color); err != nil {
return err
}
return nil
}
// CustomColor makes all RGB-LEDs which are passed as parameters light up in
// custom color.
func CustomColor(rgbLEDs []RGBLED, color types.LEDColor) error {
if err := operate(rgbLEDs, color); err != nil {
return err
}
return nil
}
// Green makes all RGB-LEDs which are passed as parameters light up in green.
func Green(rgbLEDs []RGBLED) error {
color := types.LEDColorGreen
if err := operate(rgbLEDs, color); err != nil {
return err
}
return nil
}
// Off turns all RGB-LEDs which are passes as parameters off.
func Off(rgbLEDs []RGBLED) error {
color := types.LEDColorNone
if err := operate(rgbLEDs, color); err != nil {
return err
}
return nil
}
// Purple makes all RGB-LEDs which are passed as parameters light up in purple.
func Purple(rgbLEDs []RGBLED) error {
color := types.LEDColorPurple
if err := operate(rgbLEDs, color); err != nil {
return err
}
return nil
}
// Red makes all RGB-LEDs which are passed as parameters light up in red.
func Red(rgbLEDs []RGBLED) error {
color := types.LEDColorRed
if err := operate(rgbLEDs, color); err != nil {
return err
}
return nil
}
// Turquoise makes all RGB-LEDs which are passed as parameters light up in
// turquoise.
func Turquoise(rgbLEDs []RGBLED) error {
color := types.LEDColorTurquoise
if err := operate(rgbLEDs, color); err != nil {
return err
}
return nil
}
// White makes all RGB-LEDs which are passed as parameters light up in white.
func White(rgbLEDs []RGBLED) error {
color := types.LEDColorWhite
if err := operate(rgbLEDs, color); err != nil {
return err
}
return nil
}
// Yellow makes all RGB-LEDs which are passed as parameters light up in yellow.
func Yellow(rgbLEDs []RGBLED) error {
color := types.LEDColorYellow
if err := operate(rgbLEDs, color); err != nil {
return err
}
return nil
}
func operate(rgbLEDs []RGBLED, color types.LEDColor) error {
errorChannel := make(chan error, len(rgbLEDs))
wg := new(sync.WaitGroup)
wg.Add(len(rgbLEDs))
for _, rgbLED := range rgbLEDs {
go func(rgbLED RGBLED, color types.LEDColor, errorChannel chan<- error, wg *sync.WaitGroup) {
defer wg.Done()
var err error
switch color {
case types.LEDColorBlue:
err = rgbLED.Blue()
case types.LEDColorGreen:
err = rgbLED.Green()
case types.LEDColorPurple:
err = rgbLED.Purple()
case types.LEDColorNone:
err = rgbLED.Off()
case types.LEDColorRed:
err = rgbLED.Red()
case types.LEDColorTurquoise:
err = rgbLED.Turquoise()
case types.LEDColorWhite:
err = rgbLED.White()
case types.LEDColorYellow:
err = rgbLED.Yellow()
default:
err = rgbLED.Off()
}
if err != nil {
errorChannel <- err
}
}(rgbLED, color, errorChannel, wg)
}
wg.Wait()
errors := collect.Errors(errorChannel)
if len(errors) > 0 {
return prittyprint.FormatErrors(errors)
}
return nil
}
// Error makes all RGB-LEDs which are passed as parameters light up in their
// error specified color.
func Error(rgbLEDs []RGBLED) error {
errorChannel := make(chan error, len(rgbLEDs))
wg := new(sync.WaitGroup)
wg.Add(len(rgbLEDs))
for _, rgbLED := range rgbLEDs {
go func(rgbLED RGBLED, errorChannel chan<- error, wg *sync.WaitGroup) {
defer wg.Done()
err := rgbLED.Error()
if err != nil {
errorChannel <- err
}
}(rgbLED, errorChannel, wg)
}
wg.Wait()
errors := collect.Errors(errorChannel)
if len(errors) > 0 {
return prittyprint.FormatErrors(errors)
}
return nil
}
// Logfile makes all RGB-LEDs which are passed as parameters light up in their
// logfile specified color.
func Logfile(rgbLEDs []RGBLED) error {
errorChannel := make(chan error, len(rgbLEDs))
wg := new(sync.WaitGroup)
wg.Add(len(rgbLEDs))
for _, rgbLED := range rgbLEDs {
go func(rgbLED RGBLED, errorChannel chan<- error, wg *sync.WaitGroup) {
defer wg.Done()
err := rgbLED.Logfile()
if err != nil {
errorChannel <- err
}
}(rgbLED, errorChannel, wg)
}
wg.Wait()
errors := collect.Errors(errorChannel)
if len(errors) > 0 {
return prittyprint.FormatErrors(errors)
}
return nil
}
// Run makes all RGB-LEDs which are passed as parameters light up in their run
// specified color.
func Run(rgbLEDs []RGBLED) error {
errorChannel := make(chan error, len(rgbLEDs))
wg := new(sync.WaitGroup)
wg.Add(len(rgbLEDs))
for _, rgbLED := range rgbLEDs {
go func(rgbLED RGBLED, errorChannel chan<- error, wg *sync.WaitGroup) {
defer wg.Done()
err := rgbLED.Run()
if err != nil {
errorChannel <- err
}
}(rgbLED, errorChannel, wg)
}
wg.Wait()
errors := collect.Errors(errorChannel)
if len(errors) > 0 {
return prittyprint.FormatErrors(errors)
}
return nil
}
// Sync makes all RGB-LEDs which are passed as parameters light up in their sync
// specified color.
func Sync(rgbLEDs []RGBLED) error {
errorChannel := make(chan error, len(rgbLEDs))
wg := new(sync.WaitGroup)
wg.Add(len(rgbLEDs))
for _, rgbLED := range rgbLEDs {
go func(rgbLED RGBLED, errorChannel chan<- error, wg *sync.WaitGroup) {
defer wg.Done()
err := rgbLED.Sync()
if err != nil {
errorChannel <- err
}
}(rgbLED, errorChannel, wg)
}
wg.Wait()
errors := collect.Errors(errorChannel)
if len(errors) > 0 {
return prittyprint.FormatErrors(errors)
}
return nil
}
// Warn makes all RGB-LEDs which are passed as parameters light up in their
// warn specified color.
func Warn(rgbLEDs []RGBLED) error {
errorChannel := make(chan error, len(rgbLEDs))
wg := new(sync.WaitGroup)
wg.Add(len(rgbLEDs))
for _, rgbLED := range rgbLEDs {
go func(rgbLED RGBLED, errorChannel chan<- error, wg *sync.WaitGroup) {
defer wg.Done()
err := rgbLED.Warn()
if err != nil {
errorChannel <- err
}
}(rgbLED, errorChannel, wg)
}
wg.Wait()
errors := collect.Errors(errorChannel)
if len(errors) > 0 {
return prittyprint.FormatErrors(errors)
}
return nil
}

View File

@ -1,98 +1,91 @@
package sensor
import (
"log"
"time"
"fmt"
"sync"
"github.com/d2r2/go-bsbmp"
"github.com/d2r2/go-i2c"
"github.com/d2r2/go-logger"
uuid "github.com/satori/go.uuid"
"github.com/volker-raschek/flucky/pkg/internal/format"
"github.com/volker-raschek/flucky/pkg/types"
uuid "github.com/satori/go.uuid"
)
// BME280 is a sensor to measure humidity and temperature.
type BME280 struct {
*types.Sensor
}
// GetID returns the sensor id
func (s *BME280) GetID() string {
return s.ID
}
// GetTicker returns a new ticker, which tick every when the sensor should be read
func (s *BME280) GetTicker() *time.Ticker {
duration, err := time.ParseDuration(s.TickDuration)
if err != nil {
duration = time.Minute
}
return time.NewTicker(duration)
mutex *sync.Mutex
}
// Read measured values
func (s *BME280) Read() ([]*types.MeasuredValue, error) {
func (bme280 *BME280) Read() ([]*types.MeasuredValue, error) {
// Lock multiple access
bme280.mutex.Lock()
defer bme280.mutex.Unlock()
// Create new connection to i2c-bus on 1 line with address 0x76.
// Use i2cdetect utility to find device address over the i2c-bus
i2c, err := i2c.NewI2C(*s.I2CAddress, *s.I2CBus)
i2c, err := i2c.NewI2C(*bme280.I2CAddress, *bme280.I2CBus)
if err != nil {
log.Fatal(err)
return nil, err
}
defer i2c.Close()
logger.ChangePackageLogLevel("i2c", logger.InfoLevel)
// Reduce loglevel
for _, pkg := range []string{"bsbmp", "i2c"} {
err = logger.ChangePackageLogLevel(pkg, logger.InfoLevel)
if err != nil {
return nil, fmt.Errorf("Failed to change package log level: %v", err)
}
}
sensor, err := bsbmp.NewBMP(bsbmp.BME280, i2c)
if err != nil {
log.Fatal(err)
return nil, err
}
logger.ChangePackageLogLevel("bsbmp", logger.InfoLevel)
temperatureValue, err := sensor.ReadTemperatureC(bsbmp.ACCURACY_STANDARD)
if err != nil {
log.Fatal(err)
return nil, err
}
pressureValue, err := sensor.ReadPressurePa(bsbmp.ACCURACY_STANDARD)
if err != nil {
log.Fatal(err)
return nil, err
}
// pressureValueRound := math.Round(float64(pressureValue)/10*0.25) * 10 / 0.25
_, humidityValue, err := sensor.ReadHumidityRH(bsbmp.ACCURACY_STANDARD)
if err != nil {
log.Fatal(err)
return nil, err
}
measuredValues := []*types.MeasuredValue{
&types.MeasuredValue{
{
ID: uuid.NewV4().String(),
Value: float64(humidityValue),
ValueType: types.MeasuredValueTypeHumidity,
ValueType: "humidity",
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.ID,
SensorID: bme280.ID,
},
&types.MeasuredValue{
{
ID: uuid.NewV4().String(),
Value: float64(pressureValue),
ValueType: types.MeasuredValueTypePressure,
ValueType: "pressure",
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.ID,
SensorID: bme280.ID,
},
&types.MeasuredValue{
{
ID: uuid.NewV4().String(),
Value: float64(temperatureValue),
ValueType: types.MeasuredValueTypeTemperature,
ValueType: "temperature",
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.ID,
SensorID: bme280.ID,
},
}

View File

@ -2,49 +2,35 @@ package sensor
import (
"fmt"
"time"
"sync"
"github.com/volker-raschek/flucky/pkg/internal/format"
"github.com/volker-raschek/flucky/pkg/types"
"github.com/go-flucky/go-dht"
uuid "github.com/satori/go.uuid"
"github.com/volker-raschek/flucky/pkg/internal/format"
"github.com/volker-raschek/flucky/pkg/types"
)
// DHT11 is a sensor to measure humidity and temperature.
type DHT11 struct {
*types.Sensor
}
// GetID returns the sensor id
func (s *DHT11) GetID() string {
return s.ID
}
// GetTicker returns a new ticker, which tick every when the sensor should be read
func (s *DHT11) GetTicker() *time.Ticker {
duration, err := time.ParseDuration(s.TickDuration)
if err != nil {
duration = time.Minute
}
return time.NewTicker(duration)
mutex *sync.Mutex
}
// Read measured values
func (s *DHT11) Read() ([]*types.MeasuredValue, error) {
func (dht11 *DHT11) Read() ([]*types.MeasuredValue, error) {
// Lock multiple access
dht11.mutex.Lock()
defer dht11.mutex.Unlock()
err := dht.HostInit()
if err != nil {
return nil, fmt.Errorf("HostInit error: %v", err)
return nil, fmt.Errorf("Failed to initialize periph: %v", err)
}
gpio, err := types.GPIOToString(*s.GPIONumber)
dht, err := dht.NewDHT(dht11.GPIONumber, dht.Celsius, "")
if err != nil {
return nil, err
}
dht, err := dht.NewDHT(gpio, dht.Celsius, "")
if err != nil {
return nil, fmt.Errorf("NewDHT error: %v", err)
return nil, fmt.Errorf("Failed to initialize new DHT11 sensor: %v", err)
}
humidityValue, temperatureValue, err := dht.Read()
@ -53,21 +39,21 @@ func (s *DHT11) Read() ([]*types.MeasuredValue, error) {
}
measuredValues := []*types.MeasuredValue{
&types.MeasuredValue{
{
ID: uuid.NewV4().String(),
Value: float64(humidityValue),
ValueType: types.MeasuredValueTypeHumidity,
ValueType: "humidity",
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.ID,
SensorID: dht11.ID,
},
&types.MeasuredValue{
{
ID: uuid.NewV4().String(),
Value: float64(temperatureValue),
ValueType: types.MeasuredValueTypeTemperature,
ValueType: "temperature",
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.ID,
SensorID: dht11.ID,
},
}

View File

@ -2,49 +2,35 @@ package sensor
import (
"fmt"
"time"
"sync"
"github.com/volker-raschek/flucky/pkg/internal/format"
"github.com/volker-raschek/flucky/pkg/types"
"github.com/go-flucky/go-dht"
uuid "github.com/satori/go.uuid"
"github.com/volker-raschek/flucky/pkg/internal/format"
"github.com/volker-raschek/flucky/pkg/types"
)
// DHT22 is a sensor to measure humidity and temperature.
type DHT22 struct {
*types.Sensor
}
// GetID returns the sensor id
func (s *DHT22) GetID() string {
return s.ID
}
// GetTicker returns a new ticker, which tick every when the sensor should be read
func (s *DHT22) GetTicker() *time.Ticker {
duration, err := time.ParseDuration(s.TickDuration)
if err != nil {
duration = time.Minute
}
return time.NewTicker(duration)
mutex *sync.Mutex
}
// Read measured values
func (s *DHT22) Read() ([]*types.MeasuredValue, error) {
func (dht22 *DHT22) Read() ([]*types.MeasuredValue, error) {
// Lock multiple access
dht22.mutex.Lock()
defer dht22.mutex.Unlock()
err := dht.HostInit()
if err != nil {
return nil, fmt.Errorf("HostInit error: %v", err)
return nil, fmt.Errorf("Failed to initialize periph: %v", err)
}
gpio, err := types.GPIOToString(*s.GPIONumber)
dht, err := dht.NewDHT(dht22.GPIONumber, dht.Celsius, "")
if err != nil {
return nil, err
}
dht, err := dht.NewDHT(gpio, dht.Celsius, "")
if err != nil {
return nil, fmt.Errorf("NewDHT error: %v", err)
return nil, fmt.Errorf("Failed to initialize new DHT22 sensor: %v", err)
}
humidityValue, temperatureValue, err := dht.Read()
@ -53,21 +39,21 @@ func (s *DHT22) Read() ([]*types.MeasuredValue, error) {
}
measuredValues := []*types.MeasuredValue{
&types.MeasuredValue{
{
ID: uuid.NewV4().String(),
Value: float64(humidityValue),
ValueType: types.MeasuredValueTypeHumidity,
ValueType: "humidity",
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.ID,
SensorID: dht22.ID,
},
&types.MeasuredValue{
{
ID: uuid.NewV4().String(),
Value: float64(temperatureValue),
ValueType: types.MeasuredValueTypeTemperature,
ValueType: "temperature",
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.ID,
SensorID: dht22.ID,
},
}

View File

@ -3,45 +3,42 @@ package sensor
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"sync"
uuid "github.com/satori/go.uuid"
"github.com/volker-raschek/flucky/pkg/internal/format"
"github.com/volker-raschek/flucky/pkg/types"
uuid "github.com/satori/go.uuid"
)
// DS18B20 is a sensor to measure humidity and temperature.
type DS18B20 struct {
*types.Sensor
}
// GetID returns the sensor id
func (s *DS18B20) GetID() string {
return s.ID
}
// GetTicker returns a new ticker, which tick every when the sensor should be read
func (s *DS18B20) GetTicker() *time.Ticker {
duration, err := time.ParseDuration(s.TickDuration)
if err != nil {
duration = time.Minute
}
return time.NewTicker(duration)
mutex *sync.Mutex
}
// Read measured values
func (s *DS18B20) Read() ([]*types.MeasuredValue, error) {
func (ds18b20 *DS18B20) Read() ([]*types.MeasuredValue, error) {
if s.WireID == nil {
return nil, fmt.Errorf("WireID is not specified for sensor %v", s.Name)
// Lock multiple access
ds18b20.mutex.Lock()
defer ds18b20.mutex.Unlock()
if ds18b20.WireID == nil {
return nil, fmt.Errorf("WireID is not specified")
}
data, err := ioutil.ReadFile(filepath.Join("/sys/bus/w1/devices", *s.WireID, "/w1_slave"))
socketPath := filepath.Join("/sys/bus/w1/devices", *ds18b20.WireID, "/w1_slave")
if _, err := os.Stat(socketPath); os.IsNotExist(err) {
return nil, fmt.Errorf("Socket path not found: %v", socketPath)
}
data, err := ioutil.ReadFile(socketPath)
if err != nil {
return nil, fmt.Errorf("Can not read data from sensor %v", s.Name)
return nil, fmt.Errorf("Can not read data from sensor %v", ds18b20.Name)
}
raw := string(data)
@ -59,13 +56,13 @@ func (s *DS18B20) Read() ([]*types.MeasuredValue, error) {
temperatureValue := c / 1000
measuredValues := []*types.MeasuredValue{
&types.MeasuredValue{
{
ID: uuid.NewV4().String(),
Value: float64(temperatureValue),
ValueType: types.MeasuredValueTypeTemperature,
ValueType: "temperature",
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.ID,
SensorID: ds18b20.ID,
},
}

View File

@ -1,46 +1,40 @@
package sensor
import (
"errors"
"sync"
"github.com/volker-raschek/flucky/pkg/types"
)
func Read(sensors []Sensor, measuredValueType types.MeasuredValueType) ([]*types.MeasuredValue, error) {
var (
ErrSensorModelNotMatched = errors.New("Sensor model not matched")
)
type result struct {
measuredValues []*types.MeasuredValue
err error
// New returns a new sensor
func New(sensor *types.Sensor) (Sensor, error) {
switch sensor.Model {
case "BME280":
return &BME280{
Sensor: sensor,
mutex: new(sync.Mutex),
}, nil
case "DHT11":
return &DHT11{
Sensor: sensor,
mutex: new(sync.Mutex),
}, nil
case "DHT22":
return &DHT22{
Sensor: sensor,
mutex: new(sync.Mutex),
}, nil
case "DS18B20":
return &DS18B20{
Sensor: sensor,
mutex: new(sync.Mutex),
}, nil
default:
return nil, ErrSensorModelNotMatched
}
resultChannel := make(chan *result, len(sensors))
// producers
// read measured values
for _, s := range sensors {
go func(s Sensor) {
measuredValues, err := s.Read()
resultChannel <- &result{
measuredValues: measuredValues,
err: err,
}
}(s)
}
// consumer
measuredValues := make([]*types.MeasuredValue, 0)
counter := len(sensors)
for {
if counter == 0 {
break
}
select {
case result := <-resultChannel:
counter--
if result.err != nil {
return nil, result.err
}
measuredValues = append(measuredValues, result.measuredValues...)
}
}
return types.SelectMeasuredValues(measuredValueType, measuredValues), nil
}

View File

@ -1,35 +0,0 @@
package db
import (
"database/sql"
"errors"
"net/url"
_ "github.com/lib/pq"
"github.com/volker-raschek/go-logger/pkg/logger"
)
var (
errorUnsupportedDatabase = errors.New("Unsupported database scheme")
flogger = logger.NewSilentLogger()
)
func New(storageEndpoint *url.URL) (Database, error) {
switch storageEndpoint.Scheme {
case "postgres":
newDBO, err := sql.Open(storageEndpoint.Scheme, storageEndpoint.String())
if err != nil {
return nil, err
}
return &Postgres{
dbo: newDBO,
}, nil
default:
return nil, errorUnsupportedDatabase
}
}
func SetLogger(logger logger.Logger) {
flogger = logger
}

View File

@ -1,38 +0,0 @@
package db
import (
"net/url"
"testing"
"github.com/stretchr/testify/require"
)
func TestNew(t *testing.T) {
require := require.New(t)
validStorageEndpoints := []string{
"postgres://flucky:flucky@markus-pc.trier.cryptic.systems/postgres?sslmode=disable",
}
unsupportedStorageEndpoints := []string{
"html://flucky.cryptic.systems",
"oracle://flucky:flucky@example.com/postgres",
}
for _, validStorageEndpoint := range validStorageEndpoints {
storageEndpointURL, err := url.Parse(validStorageEndpoint)
require.Nil(err)
dbo, err := New(storageEndpointURL)
require.Nil(err)
err = dbo.Close()
require.Nil(err)
}
for _, unsupportedStorageEndpoint := range unsupportedStorageEndpoints {
storageEndpointURL, err := url.Parse(unsupportedStorageEndpoint)
require.Nil(err)
_, err = New(storageEndpointURL)
require.Equal(errorUnsupportedDatabase, err)
}
}

View File

@ -1,18 +0,0 @@
package db
import (
"errors"
)
var (
errorBeginTransaction = errors.New("Failed to start new transaction")
errorGetAsset = errors.New("Failed to get asset from go-bindata")
errorNoRowsAffected = errors.New("No rows affected")
errorRowNotFound = errors.New("Failed to find row by given ID")
errorPrepareStatement = errors.New("Failed to prepare sql statement")
errorRollbackTransaction = errors.New("Failed to rollback transaction")
errorScanRow = errors.New("Failed to scan row")
errorStatementExecute = errors.New("Failed to execute statement")
errorStatementQuery = errors.New("Failed to query statement")
errorUnknownMeasuredValueType = errors.New("Unknown measured value type")
)

View File

@ -1,48 +0,0 @@
package db
import (
"context"
"github.com/Masterminds/semver"
"github.com/volker-raschek/flucky/pkg/types"
)
type Database interface {
// Close DB Connction
Close() error
// Schema
Schema(ctx context.Context, version *semver.Version) error
// Delete
DeleteDevices(ctx context.Context, devices []*types.Device) error
DeleteInfo(ctx context.Context, key string) 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
InsertInfo(ctx context.Context, key string, value string) 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)
SelectInfo(ctx context.Context, key string) (string, error)
SelectMeasuredValues(ctx context.Context) ([]*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
UpdateInfo(ctx context.Context, key string, value string) error
UpdateMeasuredValues(ctx context.Context, measuredValues []*types.MeasuredValue) error
UpdateSensors(ctx context.Context, sensots []*types.Sensor) error
}

View File

@ -1,713 +0,0 @@
package db
import (
"context"
"database/sql"
"fmt"
"path/filepath"
"sort"
"strings"
"github.com/Masterminds/semver"
"github.com/volker-raschek/flucky/pkg/types"
// PostgreSQL lib
_ "github.com/lib/pq"
)
var (
postgresAssetPath = "pkg/storage/db/sql/psql"
)
// Postgres provide functions to interact with a postgres database
type Postgres struct {
dbo *sql.DB
}
// Close the database connection
func (p *Postgres) Close() error {
return p.dbo.Close()
}
// Schema create or updates the database schema to a given version. Normally the
// version is the same as the flucky binary version.
func (p *Postgres) Schema(ctx context.Context, version *semver.Version) error {
schemaFunc := func(ctx context.Context, fromVersion *semver.Version, toVersion *semver.Version) error {
assetPath := fmt.Sprintf("%v/schema", postgresAssetPath)
sqlAssetFiles, err := AssetDir(assetPath)
if err != nil {
return fmt.Errorf("Can not restore asset directory %v: %v", assetPath, err)
}
postgreSQLVersionChanges := make(map[*semver.Version]string, 0)
postgreSQLVersions := make([]*semver.Version, len(sqlAssetFiles))
for i, sqlAssetFile := range sqlAssetFiles {
fileSemVersion, err := semver.NewVersion(strings.ReplaceAll(sqlAssetFile, ".sql", ""))
if err != nil {
return fmt.Errorf("Can not create semantic version from file asset %v: %v", sqlAssetFile, err)
}
postgreSQLVersionChanges[fileSemVersion] = sqlAssetFile
postgreSQLVersions[i] = fileSemVersion
}
sort.Sort(semver.Collection(postgreSQLVersions))
for i, postgreSQLVersion := range postgreSQLVersions {
if fromVersion != nil {
if postgreSQLVersion.LessThan(fromVersion) || postgreSQLVersion.Equal(fromVersion) {
flogger.Debug("SKIP: PostgreSQL schema version '%v' is less or eqal then the local version changes '%v'", postgreSQLVersion.String(), fromVersion.String())
continue
}
}
asset := postgreSQLVersionChanges[postgreSQLVersion]
queryBytes, err := Asset(filepath.Join(assetPath, asset))
if err != nil {
return fmt.Errorf("Can not restore asset %v, %v", asset, err)
}
query := string(queryBytes)
if _, err := p.dbo.ExecContext(ctx, query); err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
if i == 0 {
if err := p.InsertInfo(ctx, "version", postgreSQLVersion.String()); err != nil {
return fmt.Errorf("Can not insert version %v into info table: %v", postgreSQLVersion.String(), err)
}
} else {
if err := p.UpdateInfo(ctx, "version", postgreSQLVersion.String()); err != nil {
return fmt.Errorf("Can not update version %v into info table: %v", postgreSQLVersion.String(), err)
}
}
}
return nil
}
dbVersion, err := p.SelectInfo(ctx, "version")
if err != nil {
// can not select version from database, maybe the schema is not initialize
// create db schema for the current flucky version
return schemaFunc(ctx, nil, version)
} else {
fromVersion, err := semver.NewVersion(dbVersion)
if err != nil {
return fmt.Errorf("Can not create semantic version from database entry %v: %v", dbVersion, err)
}
return schemaFunc(ctx, fromVersion, version)
}
}
// DeleteDevices delete recursively all spicified devices, including sensors and
// all measured values
func (p *Postgres) DeleteDevices(ctx context.Context, devices []*types.Device) error {
asset := fmt.Sprintf("%v/deleteDevice.sql", postgresAssetPath)
queryBytes, err := Asset(asset)
if err != nil {
return fmt.Errorf("%v: %v", errorGetAsset, err)
}
query := string(queryBytes)
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.ID)
if err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
}
return nil
}
// DeleteSensors delete recusively all spicified sensors, including all measured
// values
func (p *Postgres) DeleteSensors(ctx context.Context, sensors []*types.Sensor) error {
asset := fmt.Sprintf("%v/deleteSensor.sql", postgresAssetPath)
queryBytes, err := Asset(asset)
if err != nil {
return fmt.Errorf("%v: %v", errorGetAsset, err)
}
query := string(queryBytes)
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.ID)
if err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
}
return nil
}
// DeleteInfo delete a key with his value
func (p *Postgres) DeleteInfo(ctx context.Context, key string) error {
asset := fmt.Sprintf("%v/deleteInfo.sql", postgresAssetPath)
queryBytes, err := Asset(asset)
if err != nil {
return fmt.Errorf("%v: %v", errorGetAsset, err)
}
query := string(queryBytes)
stmt, err := p.dbo.PrepareContext(ctx, query)
if err != nil {
return fmt.Errorf("%v: %v", errorPrepareStatement, err)
}
defer stmt.Close()
_, err = stmt.ExecContext(ctx, &key)
if err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
return nil
}
// DeleteMeasuredValues delete all spicified measured values
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)
}
assetFunc := func(queryFile string) (string, error) {
queryBytes, err := Asset(queryFile)
if err != nil {
return "", fmt.Errorf("%v: %v", errorGetAsset, err)
}
return string(queryBytes), nil
}
for measuredValueType, sortedMeasuredValues := range sortedMeasuredValueTypes {
switch measuredValueType {
case types.MeasuredValueTypeHumidity:
query, err := assetFunc(fmt.Sprintf("%v/deleteHumidity.sql", postgresAssetPath))
if err != nil {
return err
}
if err := deleteMeasuredValue(ctx, query, sortedMeasuredValues); err != nil {
return err
}
case types.MeasuredValueTypePressure:
query, err := assetFunc(fmt.Sprintf("%v/deletePressure.sql", postgresAssetPath))
if err != nil {
return err
}
if err := deleteMeasuredValue(ctx, query, sortedMeasuredValues); err != nil {
return err
}
case types.MeasuredValueTypeTemperature:
query, err := assetFunc(fmt.Sprintf("%v/deleteTemperature.sql", postgresAssetPath))
if err != nil {
return err
}
if err := deleteMeasuredValue(ctx, query, sortedMeasuredValues); err != nil {
return err
}
}
}
return nil
}
// InsertDevices insert all specified devices into the database
func (p *Postgres) InsertDevices(ctx context.Context, devices []*types.Device) error {
asset := fmt.Sprintf("%v/insertDevice.sql", postgresAssetPath)
queryBytes, err := Asset(asset)
if err != nil {
return fmt.Errorf("%v: %v", errorGetAsset, err)
}
query := string(queryBytes)
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.ID, &device.Name, &device.Location, &device.CreationDate)
if err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
}
return nil
}
// InsertInfo insert into the database additional informations, based on a key value syntax
func (p *Postgres) InsertInfo(ctx context.Context, key string, value string) error {
asset := fmt.Sprintf("%v/insertInfo.sql", postgresAssetPath)
queryBytes, err := Asset(asset)
if err != nil {
return fmt.Errorf("%v: %v", errorGetAsset, err)
}
query := string(queryBytes)
stmt, err := p.dbo.PrepareContext(ctx, query)
if err != nil {
return fmt.Errorf("%v: %v", errorPrepareStatement, err)
}
defer stmt.Close()
_, err = stmt.ExecContext(ctx, key, value)
if err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
return nil
}
// InsertMeasuredValues insert all specified measured values into the database
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 {
asset := fmt.Sprintf("%v/insertHumidity.sql", postgresAssetPath)
queryBytes, err := Asset(asset)
if err != nil {
return fmt.Errorf("%v: %v", errorGetAsset, err)
}
query := string(queryBytes)
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 {
asset := fmt.Sprintf("%v/insertPressure.sql", postgresAssetPath)
queryBytes, err := Asset(asset)
if err != nil {
return fmt.Errorf("%v: %v", errorGetAsset, err)
}
query := string(queryBytes)
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: Measured value id %v: %v", errorStatementExecute, measuredValue.ID, err)
}
}
return nil
}
func (p *Postgres) insertTemperature(ctx context.Context, measuredValues []*types.MeasuredValue) error {
asset := fmt.Sprintf("%v/insertTemperature.sql", postgresAssetPath)
queryBytes, err := Asset(asset)
if err != nil {
return fmt.Errorf("%v: %v", errorGetAsset, err)
}
query := string(queryBytes)
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: Measured value id %v: %v", errorStatementExecute, measuredValue.ID, err)
}
}
return nil
}
// InsertSensors insert all specified sensors into the database
func (p *Postgres) InsertSensors(ctx context.Context, sensors []*types.Sensor) error {
asset := fmt.Sprintf("%v/insertSensor.sql", postgresAssetPath)
queryBytes, err := Asset(asset)
if err != nil {
return fmt.Errorf("%v: %v", errorGetAsset, err)
}
query := string(queryBytes)
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.ID, &sensor.Name, &sensor.Location, &sensor.WireID, &sensor.I2CBus, &sensor.I2CAddress, &sensor.GPIONumber, &sensor.Model, &sensor.Enabled, &sensor.DeviceID, &sensor.CreationDate)
if err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
}
return nil
}
// SelectDeviceByID returns a device by his ID
func (p *Postgres) SelectDeviceByID(ctx context.Context, id string) (*types.Device, error) {
asset := fmt.Sprintf("%v/selectDeviceByID.sql", postgresAssetPath)
queryBytes, err := Asset(asset)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorGetAsset, err)
}
query := string(queryBytes)
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.ID, &device.Name, &device.Location, &device.CreationDate)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorScanRow, err)
}
return device, nil
}
// SelectInfo returns the value of a key stored in the database
func (p *Postgres) SelectInfo(ctx context.Context, key string) (string, error) {
asset := fmt.Sprintf("%v/selectInfo.sql", postgresAssetPath)
queryBytes, err := Asset(asset)
if err != nil {
return "", fmt.Errorf("%v: %v", errorGetAsset, err)
}
query := string(queryBytes)
stmt, err := p.dbo.PrepareContext(ctx, query)
if err != nil {
return "", fmt.Errorf("%v: %v", errorPrepareStatement, err)
}
row := stmt.QueryRowContext(ctx, key)
if row == nil {
return "", errorRowNotFound
}
value := ""
err = row.Scan(&value)
if err != nil {
return "", fmt.Errorf("%v: %v", errorScanRow, err)
}
return value, nil
}
// SelectHumidities returns humidity values
func (p *Postgres) SelectHumidities(ctx context.Context) ([]*types.MeasuredValue, error) {
queryFile := fmt.Sprintf("%v/selectHumidities.sql", postgresAssetPath)
measuredValues, err := p.selectMeasuredValues(ctx, types.MeasuredValueTypeHumidity, queryFile, nil)
if err != nil {
return nil, err
}
return measuredValues, nil
}
// SelectHumidityByID returns a humidity value by his ID
func (p *Postgres) SelectHumidityByID(ctx context.Context, id string) (*types.MeasuredValue, error) {
queryFile := fmt.Sprintf("%v/selectHumidityByID.sql", postgresAssetPath)
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
}
// SelectMeasuredValues returns all measured values about all diffferent value
// types
func (p *Postgres) SelectMeasuredValues(ctx context.Context) ([]*types.MeasuredValue, error) {
measuredValues := make([]*types.MeasuredValue, 0)
// MeasuredValue query functions
queryFunctions := []func(ctx context.Context) ([]*types.MeasuredValue, error){
p.SelectHumidities,
p.SelectPressures,
p.SelectTemperatures,
}
// Execute query functions
for _, queryFunction := range queryFunctions {
queriedMeasuredValues, err := queryFunction(ctx)
if err != nil {
return nil, err
}
measuredValues = append(measuredValues, queriedMeasuredValues...)
}
return measuredValues, nil
}
// SelectMeasuredValuesByIDAndType returns a measured value by his ID and type
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)
}
}
// SelectPressures returns pressure values
func (p *Postgres) SelectPressures(ctx context.Context) ([]*types.MeasuredValue, error) {
queryFile := fmt.Sprintf("%v/selectPressures.sql", postgresAssetPath)
measuredValues, err := p.selectMeasuredValues(ctx, types.MeasuredValueTypePressure, queryFile, nil)
if err != nil {
return nil, err
}
return measuredValues, nil
}
// SelectPressureByID returns a pressure value by his ID
func (p *Postgres) SelectPressureByID(ctx context.Context, id string) (*types.MeasuredValue, error) {
queryFile := fmt.Sprintf("%v/selectPressureByID.sql", postgresAssetPath)
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
}
// SelectSensorByID returns a sensor by his ID
func (p *Postgres) SelectSensorByID(ctx context.Context, id string) (*types.Sensor, error) {
asset := fmt.Sprintf("%v/selectSensorByID.sql", postgresAssetPath)
queryBytes, err := Asset(asset)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorGetAsset, err)
}
query := string(queryBytes)
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.ID, &sensor.Name, &sensor.Location, &sensor.WireID, &sensor.I2CBus, &sensor.I2CAddress, &sensor.GPIONumber, &sensor.Model, &sensor.Enabled, &sensor.DeviceID, &sensor.CreationDate)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorScanRow, err)
}
return sensor, nil
}
// SelectTemperatures returns temperature values
func (p *Postgres) SelectTemperatures(ctx context.Context) ([]*types.MeasuredValue, error) {
queryFile := fmt.Sprintf("%v/selectTemperatures.sql", postgresAssetPath)
measuredValues, err := p.selectMeasuredValues(ctx, types.MeasuredValueTypeTemperature, queryFile, nil)
if err != nil {
return nil, err
}
return measuredValues, nil
}
// SelectTemperatureByID returns a temperature value by his ID
func (p *Postgres) SelectTemperatureByID(ctx context.Context, id string) (*types.MeasuredValue, error) {
queryFile := fmt.Sprintf("%v/selectTemperatureByID.sql", postgresAssetPath)
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) {
queryBytes, err := Asset(queryFile)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorGetAsset, err)
}
query := string(queryBytes)
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
}
// UpdateDevices updates all specified devices into the database
func (p *Postgres) UpdateDevices(ctx context.Context, devices []*types.Device) error {
return nil
}
// UpdateInfo updates the value which is stored to a key in the database
func (p *Postgres) UpdateInfo(ctx context.Context, key string, value string) error {
asset := fmt.Sprintf("%v/updateInfo.sql", postgresAssetPath)
queryBytes, err := Asset(asset)
if err != nil {
return fmt.Errorf("%v: %v", errorGetAsset, err)
}
query := string(queryBytes)
stmt, err := p.dbo.PrepareContext(ctx, query)
if err != nil {
return fmt.Errorf("%v: %v", errorPrepareStatement, err)
}
defer stmt.Close()
res, err := stmt.ExecContext(ctx, key, value)
if err != nil {
return fmt.Errorf("%v: %v", errorStatementExecute, err)
}
affected, err := res.RowsAffected()
if err != nil {
return err
}
if affected == 0 {
return errorNoRowsAffected
}
return nil
}
// UpdateMeasuredValues updates the measured values which are stored in the database
func (p *Postgres) UpdateMeasuredValues(ctx context.Context, measuredValues []*types.MeasuredValue) error {
return nil
}
// UpdateSensors updates the sensors which are stored in the database
func (p *Postgres) UpdateSensors(ctx context.Context, sensots []*types.Sensor) error {
return nil
}

View File

@ -1,397 +0,0 @@
package db_test
import (
"context"
"net/url"
"strings"
"testing"
"github.com/Masterminds/semver"
"github.com/stretchr/testify/require"
"github.com/volker-raschek/flucky/pkg/storage/db"
"github.com/volker-raschek/flucky/pkg/types"
"github.com/volker-raschek/flucky/test/goldenfiles"
)
type test struct {
Name string
Test func(*testing.T)
}
var (
database db.Database
postgresContainerImage string = "docker.io/postgres/postgres"
storageEndpointString string = "postgres://flucky:flucky@markus-pc.trier.cryptic.systems/postgres?sslmode=disable"
goldenDevicesFilePath string = "test/goldenfiles/json/goldenDevices.json"
goldenSensorsFilePath string = "test/goldenfiles/json/goldenSensors.json"
goldenMeasuredValuesFilePath string = "test/goldenfiles/json/goldenMeasuredValuesUncompressedRounded.json"
goldenPressuresFilePath string = "test/goldenfiles/json/goldenPressuresUncompressedRounded.json"
goldenHumiditiesFilePath string = "test/goldenfiles/json/goldenHumiditiesUncompressedRounded.json"
goldenTemperaturesFilePath string = "test/goldenfiles/json/goldenTemperaturesUncompressedRounded.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)
storageEndpoint, err := url.Parse(storageEndpointString)
require.Nil(err)
db, err := db.New(storageEndpoint)
database = db
require.Nil(err)
tests := []*test{
&test{
Name: "schema",
Test: testSchemaCreate,
},
&test{
Name: "insertInfo",
Test: testInsertInfo,
},
&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: "selectHumidities",
Test: testSelectHumidities,
},
&test{
Name: "selectPressures",
Test: testSelectPressures,
},
&test{
Name: "selectTemperatures",
Test: testSelectTemperatures,
},
// &test{
// Name: "selectMeasuredValues",
// Test: testSelectMeasuredValues,
// },
&test{
Name: "deleteHumidities",
Test: testDeleteHumidity,
},
&test{
Name: "deleteInfo",
Test: testDeleteInfo,
},
&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,
},
&test{
Name: "updateInfo",
Test: testUpdateInfo,
},
}
for _, test := range tests {
t.Run(test.Name, test.Test)
}
}
func testSchemaCreate(t *testing.T) {
require := require.New(t)
homePath := "pkg/storage/db/sql/psql/schema"
sqlAssetFiles, err := db.AssetDir(homePath)
require.NoError(err)
ctx := context.Background()
for _, sqlAssetFile := range sqlAssetFiles {
fromVersion, err := semver.NewVersion(strings.ReplaceAll(sqlAssetFile, ".sql", ""))
require.NoError(err)
err = database.Schema(ctx, fromVersion)
require.NoError(err)
}
}
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.ID)
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.ID)
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 testInsertInfo(t *testing.T) {
require := require.New(t)
ctx := context.Background()
err := database.InsertInfo(ctx, "test", "value")
require.NoError(err)
value, err := database.SelectInfo(ctx, "test")
require.NoError(err)
require.Equal("value", value)
}
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 testSelectHumidities(t *testing.T) {
require := require.New(t)
ctx := context.Background()
humidities, err := database.SelectHumidities(ctx)
require.NoError(err)
goldenfiles.CompareMeasuredValues(t, goldenHumidites, humidities)
}
func testSelectMeasuredValues(t *testing.T) {
require := require.New(t)
ctx := context.Background()
measuredValues, err := database.SelectMeasuredValues(ctx)
require.NoError(err)
goldenfiles.CompareMeasuredValues(t, goldenMeasuredValues, measuredValues)
}
func testSelectPressures(t *testing.T) {
require := require.New(t)
ctx := context.Background()
pressures, err := database.SelectPressures(ctx)
require.NoError(err)
goldenfiles.CompareMeasuredValues(t, goldenPressures, pressures)
}
func testSelectTemperatures(t *testing.T) {
require := require.New(t)
ctx := context.Background()
temperatures, err := database.SelectTemperatures(ctx)
require.NoError(err)
goldenfiles.CompareMeasuredValues(t, goldenTemperatures, temperatures)
}
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.ID)
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.ID)
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 testDeleteInfo(t *testing.T) {
require := require.New(t)
ctx := context.Background()
err := database.DeleteInfo(ctx, "test")
require.NoError(err)
_, err = database.SelectInfo(ctx, "test")
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)
}
}
func testUpdateInfo(t *testing.T) {
require := require.New(t)
ctx := context.Background()
// VALID
err := database.InsertInfo(ctx, "key", "value")
require.NoError(err)
err = database.UpdateInfo(ctx, "key", "value2")
require.NoError(err)
value, err := database.SelectInfo(ctx, "key")
require.NoError(err)
require.Equal("value2", value)
err = database.DeleteInfo(ctx, "key")
require.NoError(err)
// INVALID
err = database.UpdateInfo(ctx, "key2", "value")
require.Error(err)
}

View File

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

View File

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

View File

@ -1,2 +0,0 @@
DELETE FROM info
WHERE key = $1;

View File

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

View File

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

View File

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

View File

@ -1,2 +0,0 @@
INSERT INTO info (key, value)
VALUES ($1, $2);

View File

@ -1,152 +0,0 @@
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;
DROP TABLE IF EXISTS info 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
);
CREATE TABLE IF NOT EXISTS info (
key VARCHAR(32) CONSTRAINT pk_info PRIMARY KEY,
value VARCHAR(32) NOT NULL
);
-- +----------------------------------------+
-- | 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

@ -1,3 +0,0 @@
ALTER TABLE humidities ALTER COLUMN creation_date DROP NOT NULL;
ALTER TABLE pressures ALTER COLUMN creation_date DROP NOT NULL;
ALTER TABLE temperatures ALTER COLUMN creation_date DROP NOT NULL;

View File

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

View File

@ -1,12 +0,0 @@
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

@ -1,3 +0,0 @@
SELECT value
FROM info
WHERE key = $1;

View File

@ -1,12 +0,0 @@
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

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

View File

@ -1,12 +0,0 @@
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

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

View File

@ -1,3 +0,0 @@
UPDATE info
SET value = $2
WHERE key = $1;

View File

@ -1,142 +0,0 @@
package logfile
import (
"encoding/csv"
"fmt"
"os"
"path/filepath"
"strconv"
"time"
"github.com/volker-raschek/flucky/pkg/internal/format"
"github.com/volker-raschek/flucky/pkg/types"
)
type csvLogfile struct {
logfile string
}
func (cl *csvLogfile) Read() ([]*types.MeasuredValue, error) {
if _, err := os.Stat(cl.logfile); os.IsNotExist(err) {
if _, err := os.Stat(filepath.Dir(cl.logfile)); os.IsNotExist(err) {
if err := os.MkdirAll(filepath.Dir(cl.logfile), 0755); err != nil {
return nil, fmt.Errorf("%v: %v", errorDirectoryCreate, filepath.Dir(cl.logfile))
}
}
f, err := os.Create(cl.logfile)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorLogfileCreate, cl.logfile)
}
f.Close()
}
f, err := os.Open(cl.logfile)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorLogfileOpen, cl.logfile)
}
defer f.Close()
r := csv.NewReader(f)
records, err := r.ReadAll()
if err != nil {
return nil, fmt.Errorf("%v %v: %v", errorLogfileDecode, cl.logfile, err)
}
measuredValues := make([]*types.MeasuredValue, 0)
for _, record := range records {
// ValueType
valueType, err := types.SelectMeasuredValueType(record[1])
if err != nil {
return nil, fmt.Errorf("%v %v: %v", errorParseFloat, record[1], err)
}
// Value
value, err := strconv.ParseFloat(record[2], 64)
if err != nil {
return nil, fmt.Errorf("%v %v: %v", errorParseFloat, record[2], err)
}
// Times
times := make([]time.Time, 0)
for _, i := range []int{3, 4} {
time, err := time.Parse(format.TimeFormat, record[i])
if err != nil {
return nil, fmt.Errorf("%v %v: %v", errorParseTime, record[i], err)
}
times = append(times, time)
}
measuredValue := &types.MeasuredValue{
ID: record[0],
ValueType: *valueType,
Value: value,
FromDate: times[0],
TillDate: times[1],
SensorID: record[5],
}
// Creation date
if record[6] != "null" {
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
}
// Update date
if record[7] != "null" {
updateDate, err := time.Parse(format.TimeFormat, record[7])
if err != nil {
return nil, fmt.Errorf("%v %v: %v", errorParseTime, record[7], err)
}
measuredValue.UpdateDate = &updateDate
}
measuredValues = append(measuredValues, measuredValue)
}
return measuredValues, nil
}
func (cl *csvLogfile) Write(measuredValues []*types.MeasuredValue) error {
f, err := os.Create(cl.logfile)
if err != nil {
return fmt.Errorf("%v: %v", errorLogfileCreate, cl.logfile)
}
defer f.Close()
w := csv.NewWriter(f)
for _, measuredValue := range measuredValues {
record := []string{
measuredValue.ID,
fmt.Sprintf("%v", measuredValue.ValueType),
fmt.Sprintf("%v", measuredValue.Value),
measuredValue.FromDate.Format(format.TimeFormat),
measuredValue.TillDate.Format(format.TimeFormat),
measuredValue.SensorID,
}
record = append(record, measuredValue.CreationDate.Format(format.TimeFormat))
if measuredValue.UpdateDate != nil {
record = append(record, measuredValue.UpdateDate.Format(format.TimeFormat))
} else {
record = append(record, "null")
}
w.Write(record)
}
w.Flush()
return nil
}

View File

@ -1,30 +0,0 @@
package logfile
import "errors"
var (
errorDirectoryCreate = errors.New("Can not create directory")
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")
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")
errorParseFloat = errors.New("Can not parse float")
errorParseMeasurementUnit = errors.New("Can not parse mesaurement unit")
errorParseTime = errors.New("Can not parse time")
errorTypeSwitch = errors.New("Can not detect type via type switch")
)

View File

@ -1,11 +0,0 @@
package logfile
import (
"github.com/volker-raschek/flucky/pkg/types"
)
// Logfile is an interface for various logfiles
type Logfile interface {
Read() ([]*types.MeasuredValue, error)
Write(measuredValues []*types.MeasuredValue) error
}

View File

@ -1,69 +0,0 @@
package logfile
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/volker-raschek/flucky/pkg/types"
)
type jsonLogfile struct {
logfile string
}
func (jl *jsonLogfile) Read() ([]*types.MeasuredValue, error) {
if _, err := os.Stat(jl.logfile); os.IsNotExist(err) {
if _, err := os.Stat(filepath.Dir(jl.logfile)); os.IsNotExist(err) {
if err := os.MkdirAll(filepath.Dir(jl.logfile), 0755); err != nil {
return nil, fmt.Errorf("%v: %v", errorDirectoryCreate, filepath.Dir(jl.logfile))
}
}
f, err := os.Create(jl.logfile)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorLogfileCreate, jl.logfile)
}
f.WriteString("{}")
f.Close()
}
f, err := os.Open(jl.logfile)
if err != nil {
return nil, fmt.Errorf("%v %v: %v", errorLogfileOpen, jl.logfile, err)
}
measuredValues := make([]*types.MeasuredValue, 0)
if err := json.NewDecoder(f).Decode(&measuredValues); err != nil {
return nil, fmt.Errorf("%v %v: %v", errorLogfileDecode, jl.logfile, err)
}
return measuredValues, nil
}
func (jl *jsonLogfile) Write(measuredValues []*types.MeasuredValue) error {
if _, err := os.Stat(filepath.Dir(jl.logfile)); os.IsNotExist(err) {
if err := os.MkdirAll(filepath.Dir(jl.logfile), 755); err != nil {
return fmt.Errorf("Directory for the logfile can not be created: %v", err)
}
}
f, err := os.Create(jl.logfile)
if err != nil {
return fmt.Errorf("%v %v: %v", errorLogfileCreate, jl.logfile, err)
}
jsonEncoder := json.NewEncoder(f)
jsonEncoder.SetIndent("", " ")
err = jsonEncoder.Encode(measuredValues)
if err != nil {
return fmt.Errorf("%v %v: %v", errorLogfileEncode, jl.logfile, err)
}
return nil
}

View File

@ -1,30 +0,0 @@
package logfile
import (
"path/filepath"
)
// New returns a log file with basic functions for reading and writing data. The
// file extension of the logfile is taken into account to format the logfile
// into the correct format.
func New(logfile string) Logfile {
ext := filepath.Ext(logfile)
switch ext {
case ".csv":
return &csvLogfile{
logfile: logfile,
}
case ".json":
return &jsonLogfile{
logfile: logfile,
}
case ".xml":
return &xmlLogfile{
logfile: logfile,
}
default:
return &jsonLogfile{
logfile: logfile,
}
}
}

View File

@ -1,19 +0,0 @@
package logfile
import (
"encoding/xml"
"github.com/volker-raschek/flucky/pkg/types"
)
// MeasuredValues is an XML Wrapper for an array of measured values
type MeasuredValues struct {
XMLName xml.Name `xml:"measured_values"`
MeasuredValues []*MeasuredValue `xml:"measured_value"`
}
// MeasuredValue is an XML Wrapper for the original measured value struct
type MeasuredValue struct {
XMLName xml.Name `xml:"measured_value"`
*types.MeasuredValue
}

View File

@ -1,84 +0,0 @@
package logfile
import (
"encoding/xml"
"fmt"
"os"
"path/filepath"
"github.com/volker-raschek/flucky/pkg/types"
)
type xmlLogfile struct {
logfile string
}
func (xl *xmlLogfile) GetLogfile() string {
return xl.logfile
}
func (xl *xmlLogfile) Read() ([]*types.MeasuredValue, error) {
if _, err := os.Stat(xl.logfile); os.IsNotExist(err) {
if _, err := os.Stat(filepath.Dir(xl.logfile)); os.IsNotExist(err) {
if err := os.MkdirAll(filepath.Dir(xl.logfile), 0755); err != nil {
return nil, fmt.Errorf("%v: %v", errorDirectoryCreate, filepath.Dir(xl.logfile))
}
}
f, err := os.Create(xl.logfile)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorLogfileCreate, xl.logfile)
}
f.Close()
}
f, err := os.Open(xl.logfile)
if err != nil {
return nil, fmt.Errorf("%v: %v", errorLogfileOpen, xl.logfile)
}
defer f.Close()
measuredValues := new(MeasuredValues)
if err := xml.NewDecoder(f).Decode(&measuredValues); err != nil {
return nil, fmt.Errorf("%v: %v", errorLogfileDecode, err)
}
cachedMeasuredValues := make([]*types.MeasuredValue, 0)
for _, measuredValue := range measuredValues.MeasuredValues {
cachedMeasuredValues = append(cachedMeasuredValues, measuredValue.MeasuredValue)
}
return cachedMeasuredValues, nil
}
func (xl *xmlLogfile) Write(measuredValues []*types.MeasuredValue) error {
f, err := os.Create(xl.logfile)
if err != nil {
return fmt.Errorf("%v: %v", errorLogfileCreate, xl.logfile)
}
defer f.Close()
cachedMeasuredValues := new(MeasuredValues)
for _, measuredValue := range measuredValues {
cachedMeasuredValue := &MeasuredValue{
MeasuredValue: measuredValue,
}
cachedMeasuredValues.MeasuredValues = append(cachedMeasuredValues.MeasuredValues, cachedMeasuredValue)
}
bytes, err := xml.MarshalIndent(cachedMeasuredValues, "", " ")
if err != nil {
return fmt.Errorf("%v: %v", errorLogfileMarshal, err)
}
_, err = f.Write(bytes)
if err != nil {
return fmt.Errorf("%v: %v", errorLogfileWrite, err)
}
return nil
}

View File

@ -11,4 +11,4 @@ INSERT INTO sensors (
device_id,
creation_date
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11);
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11);

View File

@ -2,132 +2,239 @@ package storage
import (
"context"
"database/sql"
"fmt"
"math"
"net/url"
"sort"
"path/filepath"
"github.com/volker-raschek/flucky/pkg/internal/format"
"github.com/volker-raschek/flucky/pkg/storage/db"
"github.com/volker-raschek/flucky/pkg/storage/logfile"
_ "github.com/lib/pq"
"github.com/volker-raschek/flucky/pkg/types"
"github.com/volker-raschek/go-logger/pkg/logger"
)
// Compression the measured values. The system checks whether the measured values
// of the same type correspond to those of the predecessor. If this is the case,
// the current value is discarded and the validity date of the previous value is
// set to that of the current value. This means that no information is lost.
// Only the validity period of the measured value is increased.
func Compression(measuredValues []*types.MeasuredValue) []*types.MeasuredValue {
compressedMeasuredValues := make([]*types.MeasuredValue, 0)
lastMeasuredValuesBySensors := make(map[string]map[types.MeasuredValueType]*types.MeasuredValue, 0)
// Sort all measured values according to the start time of the validity date
// in order to successfully implement the subsequent compression.
sort.SliceStable(measuredValues, func(i int, j int) bool {
return measuredValues[i].FromDate.Before(measuredValues[j].TillDate)
})
now := format.FormatedTime()
for _, measuredValue := range measuredValues {
// If the sensor id does not exist in the map, a new map is initialized,
// which can assume measured value types as the key. Behind this key there
// is a pointer which refers to a measured value in the memory. This new map
// is added to the map "lastMeasuredValuesBySensors" under the sensor ID.
// This makes it possible to store one measured value per measured value
// type per sensor.
if _, ok := lastMeasuredValuesBySensors[measuredValue.SensorID]; !ok {
lastMeasuredValuesBySensors[measuredValue.SensorID] = make(map[types.MeasuredValueType]*types.MeasuredValue, 0)
}
if _, ok := lastMeasuredValuesBySensors[measuredValue.SensorID][measuredValue.ValueType]; !ok {
lastMeasuredValuesBySensors[measuredValue.SensorID][measuredValue.ValueType] = measuredValue
continue
}
if lastMeasuredValuesBySensors[measuredValue.SensorID][measuredValue.ValueType].Value == measuredValue.Value {
lastMeasuredValuesBySensors[measuredValue.SensorID][measuredValue.ValueType].TillDate = measuredValue.TillDate
lastMeasuredValuesBySensors[measuredValue.SensorID][measuredValue.ValueType].UpdateDate = &now
} else if lastMeasuredValuesBySensors[measuredValue.SensorID][measuredValue.ValueType].Value != measuredValue.Value {
compressedMeasuredValues = append(compressedMeasuredValues, lastMeasuredValuesBySensors[measuredValue.SensorID][measuredValue.ValueType])
delete(lastMeasuredValuesBySensors[measuredValue.SensorID], measuredValue.ValueType)
lastMeasuredValuesBySensors[measuredValue.SensorID][measuredValue.ValueType] = measuredValue
}
}
// Copy all remaining entries from the map into the cache array
for _, lastMeasuredValuesBySensor := range lastMeasuredValuesBySensors {
for _, measuredValueType := range types.MeasuredValueTypes {
if measuredValue, ok := lastMeasuredValuesBySensor[measuredValueType]; ok {
compressedMeasuredValues = append(compressedMeasuredValues, measuredValue)
}
}
}
// Sort all measured values again to include the measured values from the
// cache.
sort.SliceStable(compressedMeasuredValues, func(i int, j int) bool {
return compressedMeasuredValues[i].FromDate.Before(compressedMeasuredValues[j].FromDate)
})
return compressedMeasuredValues
// Storage is a general interface for a storage endpoint
type Storage interface {
InsertDevice(ctx context.Context, device *types.Device) error
InsertMeasuredValues(ctx context.Context, measuredValues []*types.MeasuredValue) error
InsertSensor(ctx context.Context, sensor *types.Sensor) error
SelectDevice(ctx context.Context, id string) (*types.Device, error)
SelectSensor(ctx context.Context, id string) (*types.Sensor, error)
}
// Read measured values from the given storage endpoint url. The scheme must be
// matched to a provider, if the scheme is not implemented, the function
// returns an error
func Read(ctx context.Context, storageEndpoint *url.URL) ([]*types.MeasuredValue, error) {
switch storageEndpoint.Scheme {
case "file":
measuredValueLogfile := logfile.New(storageEndpoint.Path)
return measuredValueLogfile.Read()
var (
postgresAssetPath = "pkg/storage/postgres"
)
// Postgres implementation
type Postgres struct {
dbo *sql.DB
flogger logger.Logger
}
// InsertDevice into the database
func (postgres *Postgres) InsertDevice(ctx context.Context, device *types.Device) error {
asset := filepath.Join(postgresAssetPath, "insertDevice.sql")
queryBytes, err := Asset(asset)
if err != nil {
return fmt.Errorf("Failed to load asset %v: %v", asset, err)
}
query := string(queryBytes)
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err)
}
stmt, err := tx.Prepare(query)
if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err)
}
defer stmt.Close()
_, err = stmt.Exec(&device.ID, &device.Name, &device.Location, &device.CreationDate)
if err != nil {
tx.Rollback()
return fmt.Errorf("Failed to execute statement: %v", err)
}
return tx.Commit()
}
// InsertMeasuredValues into the database
func (postgres *Postgres) InsertMeasuredValues(ctx context.Context, measuredValues []*types.MeasuredValue) error {
splittedMeasuredValues := make(map[string][]*types.MeasuredValue, 0)
for _, measuredValue := range measuredValues {
if _, ok := splittedMeasuredValues[measuredValue.ValueType]; !ok {
splittedMeasuredValues[measuredValue.ValueType] = make([]*types.MeasuredValue, 0)
}
splittedMeasuredValues[measuredValue.ValueType] = append(splittedMeasuredValues[measuredValue.ValueType], measuredValue)
}
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err)
}
// General insert function
insert := func(tx *sql.Tx, asset string, measuredValues []*types.MeasuredValue) error {
queryBytes, err := Asset(asset)
if err != nil {
return fmt.Errorf("Failed to load asset %v: %v", asset, err)
}
query := string(queryBytes)
stmt, err := tx.Prepare(query)
if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err)
}
defer stmt.Close()
for _, measuredValue := range measuredValues {
_, err := stmt.Exec(&measuredValue.ID, &measuredValue.Value, &measuredValue.FromDate, &measuredValue.TillDate, &measuredValue.SensorID, &measuredValue.CreationDate, &measuredValue.UpdateDate)
if err != nil {
return fmt.Errorf("Failed to execute statement: %v", err)
}
}
return nil
}
for measuredValueType, measuredValues := range splittedMeasuredValues {
var asset string
switch measuredValueType {
case "humidity":
asset = filepath.Join(postgresAssetPath, "insertHumidity.sql")
case "pressure":
asset = filepath.Join(postgresAssetPath, "insertPressure.sql")
case "temperature":
asset = filepath.Join(postgresAssetPath, "insertTemperature.sql")
default:
tx.Rollback()
return fmt.Errorf("Measured value type %v not supported", measuredValueType)
}
err := insert(tx, asset, measuredValues)
if err != nil {
tx.Rollback()
return err
}
}
return tx.Commit()
}
// InsertSensor into the database
func (postgres *Postgres) InsertSensor(ctx context.Context, sensor *types.Sensor) error {
asset := filepath.Join(postgresAssetPath, "insertSensor.sql")
queryBytes, err := Asset(asset)
if err != nil {
return fmt.Errorf("Failed to load asset %v: %v", asset, err)
}
query := string(queryBytes)
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err)
}
stmt, err := tx.Prepare(query)
if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err)
}
defer stmt.Close()
_, err = stmt.Exec(&sensor.ID, &sensor.Name, &sensor.Location, &sensor.WireID, &sensor.I2CBus, &sensor.I2CAddress, &sensor.GPIONumber, &sensor.Model, &sensor.Enabled, &sensor.DeviceID, &sensor.CreationDate)
if err != nil {
tx.Rollback()
return fmt.Errorf("Failed to execute statement: %v", err)
}
return tx.Commit()
}
// SelectDevice from database
func (postgres *Postgres) SelectDevice(ctx context.Context, id string) (*types.Device, error) {
asset := filepath.Join(postgresAssetPath, "selectDeviceByID.sql")
queryBytes, err := Asset(asset)
if err != nil {
return nil, fmt.Errorf("Failed to load asset %v: %v", asset, err)
}
query := string(queryBytes)
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
}
stmt, err := tx.Prepare(query)
if err != nil {
return nil, fmt.Errorf("Failed to prepare statement: %v", err)
}
defer stmt.Close()
row := stmt.QueryRow(id)
device := new(types.Device)
err = row.Scan(&device.ID, &device.Name, &device.Location, &device.CreationDate)
if err != nil {
return nil, fmt.Errorf("Failed to scan row: %v", err)
}
return device, nil
}
// SelectSensor from database
func (postgres *Postgres) SelectSensor(ctx context.Context, id string) (*types.Sensor, error) {
asset := filepath.Join(postgresAssetPath, "selectSensorByID.sql")
queryBytes, err := Asset(asset)
if err != nil {
return nil, fmt.Errorf("Failed to load asset %v: %v", asset, err)
}
query := string(queryBytes)
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
}
stmt, err := tx.Prepare(query)
if err != nil {
return nil, fmt.Errorf("Failed to prepare statement: %v", err)
}
defer stmt.Close()
row := stmt.QueryRow(id)
sensor := new(types.Sensor)
err = row.Scan(&sensor.ID, &sensor.Name, &sensor.Location, &sensor.WireID, &sensor.I2CBus, &sensor.I2CAddress, &sensor.GPIONumber, &sensor.Model, &sensor.Enabled, &sensor.DeviceID, &sensor.CreationDate)
if err != nil {
return nil, fmt.Errorf("Failed to scan row: %v", err)
}
return sensor, nil
}
// New returns a new storage provider
func New(storageEndpoint string, flogger logger.Logger) (Storage, error) {
storageEndpointURL, err := url.Parse(storageEndpoint)
if err != nil {
return nil, err
}
switch storageEndpointURL.Scheme {
case "postgres":
database, err := db.New(storageEndpoint)
newDBO, err := sql.Open(storageEndpointURL.Scheme, storageEndpointURL.String())
if err != nil {
return nil, err
}
defer database.Close()
return database.SelectMeasuredValues(ctx)
}
return nil, fmt.Errorf("No supported scheme")
}
func Round(measuredValues []*types.MeasuredValue, round float64) {
for _, measuredValue := range measuredValues {
measuredValue.Value = math.Round(measuredValue.Value/round) * round
}
}
// Write measured values to the given storage endpoint url. If the storage
// provider defined to a file, the data will be overwritten. If a database
// provider is used, the data is simply added without deleting the existing
// data. The scheme must be matched to a storage provider, if the scheme is not
// implemented, the function returns an error
func Write(ctx context.Context, measuredValues []*types.MeasuredValue, storageEndpoint *url.URL) error {
writeCreationDate(measuredValues)
switch storageEndpoint.Scheme {
case "file":
measuredValueLogfile := logfile.New(storageEndpoint.Path)
return measuredValueLogfile.Write(measuredValues)
case "postgres":
database, err := db.New(storageEndpoint)
if err != nil {
return err
}
defer database.Close()
return database.InsertMeasuredValues(ctx, measuredValues)
return &Postgres{
dbo: newDBO,
flogger: flogger,
}, nil
default:
return fmt.Errorf("No supported scheme")
}
}
func writeCreationDate(measuredValues []*types.MeasuredValue) {
now := format.FormatedTime()
for _, measuredValue := range measuredValues {
if measuredValue.CreationDate == nil {
measuredValue.CreationDate = &now
}
return nil, fmt.Errorf("Unsupported database scheme: %v", storageEndpointURL.Scheme)
}
}

View File

@ -1,286 +0,0 @@
package storage_test
import (
"testing"
"github.com/volker-raschek/flucky/pkg/storage"
"github.com/volker-raschek/flucky/test/goldenfiles"
"github.com/stretchr/testify/require"
)
type testCase struct {
source string
expected string
compression bool
round float64
}
var (
testCases = []*testCase{
// CSV - Uncompressed Not Rounded -> Uncompressed Rounded 0.5
&testCase{
source: "test/goldenfiles/csv/goldenMeasuredValuesUncompressedNotRounded.csv",
expected: "test/goldenfiles/csv/goldenMeasuredValuesUncompressedRounded.csv",
compression: false,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/csv/goldenHumiditiesUncompressedNotRounded.csv",
expected: "test/goldenfiles/csv/goldenHumiditiesUncompressedRounded.csv",
compression: false,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/csv/goldenPressuresUncompressedNotRounded.csv",
expected: "test/goldenfiles/csv/goldenPressuresUncompressedRounded.csv",
compression: false,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/csv/goldenTemperaturesUncompressedNotRounded.csv",
expected: "test/goldenfiles/csv/goldenTemperaturesUncompressedRounded.csv",
compression: false,
round: 0.5,
},
// CSV - Uncompressed Not Rounded -> Compressed Not Rounded
&testCase{
source: "test/goldenfiles/csv/goldenMeasuredValuesUncompressedNotRounded.csv",
expected: "test/goldenfiles/csv/goldenMeasuredValuesCompressedNotRounded.csv",
compression: true,
round: 0,
},
&testCase{
source: "test/goldenfiles/csv/goldenHumiditiesUncompressedNotRounded.csv",
expected: "test/goldenfiles/csv/goldenHumiditiesCompressedNotRounded.csv",
compression: true,
round: 0,
},
&testCase{
source: "test/goldenfiles/csv/goldenPressuresUncompressedNotRounded.csv",
expected: "test/goldenfiles/csv/goldenPressuresCompressedNotRounded.csv",
compression: true,
round: 0,
},
&testCase{
source: "test/goldenfiles/csv/goldenTemperaturesUncompressedNotRounded.csv",
expected: "test/goldenfiles/csv/goldenTemperaturesCompressedNotRounded.csv",
compression: true,
round: 0,
},
// CSV - Uncompressed Not Rounded -> Compressed Rounded 0.5
&testCase{
source: "test/goldenfiles/csv/goldenMeasuredValuesUncompressedNotRounded.csv",
expected: "test/goldenfiles/csv/goldenMeasuredValuesCompressedRounded.csv",
compression: true,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/csv/goldenHumiditiesUncompressedNotRounded.csv",
expected: "test/goldenfiles/csv/goldenHumiditiesCompressedRounded.csv",
compression: true,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/csv/goldenPressuresUncompressedNotRounded.csv",
expected: "test/goldenfiles/csv/goldenPressuresCompressedRounded.csv",
compression: true,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/csv/goldenTemperaturesUncompressedNotRounded.csv",
expected: "test/goldenfiles/csv/goldenTemperaturesCompressedRounded.csv",
compression: true,
round: 0.5,
},
// JSON - Uncompressed Not Rounded -> Uncompressed Rounded 0.5
&testCase{
source: "test/goldenfiles/json/goldenMeasuredValuesUncompressedNotRounded.json",
expected: "test/goldenfiles/json/goldenMeasuredValuesUncompressedRounded.json",
compression: false,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/json/goldenHumiditiesUncompressedNotRounded.json",
expected: "test/goldenfiles/json/goldenHumiditiesUncompressedRounded.json",
compression: false,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/json/goldenPressuresUncompressedNotRounded.json",
expected: "test/goldenfiles/json/goldenPressuresUncompressedRounded.json",
compression: false,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/json/goldenTemperaturesUncompressedNotRounded.json",
expected: "test/goldenfiles/json/goldenTemperaturesUncompressedRounded.json",
compression: false,
round: 0.5,
},
// JSON - Uncompressed Not Rounded -> Compressed Not Rounded
&testCase{
source: "test/goldenfiles/json/goldenMeasuredValuesUncompressedNotRounded.json",
expected: "test/goldenfiles/json/goldenMeasuredValuesCompressedNotRounded.json",
compression: true,
round: 0,
},
&testCase{
source: "test/goldenfiles/json/goldenHumiditiesUncompressedNotRounded.json",
expected: "test/goldenfiles/json/goldenHumiditiesCompressedNotRounded.json",
compression: true,
round: 0,
},
&testCase{
source: "test/goldenfiles/json/goldenPressuresUncompressedNotRounded.json",
expected: "test/goldenfiles/json/goldenPressuresCompressedNotRounded.json",
compression: true,
round: 0,
},
&testCase{
source: "test/goldenfiles/json/goldenTemperaturesUncompressedNotRounded.json",
expected: "test/goldenfiles/json/goldenTemperaturesCompressedNotRounded.json",
compression: true,
round: 0,
},
// JSON - Uncompressed Not Rounded -> Compressed Rounded 0.5
&testCase{
source: "test/goldenfiles/json/goldenMeasuredValuesUncompressedNotRounded.json",
expected: "test/goldenfiles/json/goldenMeasuredValuesCompressedRounded.json",
compression: true,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/json/goldenHumiditiesUncompressedNotRounded.json",
expected: "test/goldenfiles/json/goldenHumiditiesCompressedRounded.json",
compression: true,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/json/goldenPressuresUncompressedNotRounded.json",
expected: "test/goldenfiles/json/goldenPressuresCompressedRounded.json",
compression: true,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/json/goldenTemperaturesUncompressedNotRounded.json",
expected: "test/goldenfiles/json/goldenTemperaturesCompressedRounded.json",
compression: true,
round: 0.5,
},
// XML - Uncompressed Not Rounded -> Uncompressed Rounded 0.5
&testCase{
source: "test/goldenfiles/xml/goldenMeasuredValuesUncompressedNotRounded.xml",
expected: "test/goldenfiles/xml/goldenMeasuredValuesUncompressedRounded.xml",
compression: false,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/xml/goldenHumiditiesUncompressedNotRounded.xml",
expected: "test/goldenfiles/xml/goldenHumiditiesUncompressedRounded.xml",
compression: false,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/xml/goldenPressuresUncompressedNotRounded.xml",
expected: "test/goldenfiles/xml/goldenPressuresUncompressedRounded.xml",
compression: false,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/xml/goldenTemperaturesUncompressedNotRounded.xml",
expected: "test/goldenfiles/xml/goldenTemperaturesUncompressedRounded.xml",
compression: false,
round: 0.5,
},
// XML - Uncompressed Not Rounded -> Compressed Not Rounded
&testCase{
source: "test/goldenfiles/xml/goldenMeasuredValuesUncompressedNotRounded.xml",
expected: "test/goldenfiles/xml/goldenMeasuredValuesCompressedNotRounded.xml",
compression: true,
round: 0,
},
&testCase{
source: "test/goldenfiles/xml/goldenHumiditiesUncompressedNotRounded.xml",
expected: "test/goldenfiles/xml/goldenHumiditiesCompressedNotRounded.xml",
compression: true,
round: 0,
},
&testCase{
source: "test/goldenfiles/xml/goldenPressuresUncompressedNotRounded.xml",
expected: "test/goldenfiles/xml/goldenPressuresCompressedNotRounded.xml",
compression: true,
round: 0,
},
&testCase{
source: "test/goldenfiles/xml/goldenTemperaturesUncompressedNotRounded.xml",
expected: "test/goldenfiles/xml/goldenTemperaturesCompressedNotRounded.xml",
compression: true,
round: 0,
},
// XML - Uncompressed Not Rounded -> Compressed Rounded 0.5
&testCase{
source: "test/goldenfiles/xml/goldenMeasuredValuesUncompressedNotRounded.xml",
expected: "test/goldenfiles/xml/goldenMeasuredValuesCompressedRounded.xml",
compression: true,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/xml/goldenHumiditiesUncompressedNotRounded.xml",
expected: "test/goldenfiles/xml/goldenHumiditiesCompressedRounded.xml",
compression: true,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/xml/goldenPressuresUncompressedNotRounded.xml",
expected: "test/goldenfiles/xml/goldenPressuresCompressedRounded.xml",
compression: true,
round: 0.5,
},
&testCase{
source: "test/goldenfiles/xml/goldenTemperaturesUncompressedNotRounded.xml",
expected: "test/goldenfiles/xml/goldenTemperaturesCompressedRounded.xml",
compression: true,
round: 0.5,
},
}
)
func TestCompressionAndRounding(t *testing.T) {
require := require.New(t)
for _, testCase := range testCases {
measuredValues, err := goldenfiles.GetGoldenMeasuredValues(testCase.source)
require.NoError(err)
expectedMeasuredValues, err := goldenfiles.GetGoldenMeasuredValues(testCase.expected)
require.NoError(err)
actualMeasuredValues := measuredValues
if testCase.round != 0 {
storage.Round(actualMeasuredValues, testCase.round)
}
if testCase.compression {
actualMeasuredValues = storage.Compression(actualMeasuredValues)
}
for i, _ := range expectedMeasuredValues {
require.Equal(expectedMeasuredValues[i].ID, actualMeasuredValues[i].ID, "ID of element %v is not equal between expected and actual", i)
require.Equal(expectedMeasuredValues[i].Value, actualMeasuredValues[i].Value, "Value of element %v is not equal between expected and actual", i)
require.Equal(expectedMeasuredValues[i].ValueType, actualMeasuredValues[i].ValueType, "ValueType of element %v is not equal between expected and actual", i)
require.Equal(expectedMeasuredValues[i].FromDate, actualMeasuredValues[i].FromDate, "FromDate of element %v is not equal between expected and actual", i)
require.Equal(expectedMeasuredValues[i].TillDate, actualMeasuredValues[i].TillDate, "TillDate of element %v is not equal between expected and actual", i)
require.Equal(expectedMeasuredValues[i].SensorID, actualMeasuredValues[i].SensorID, "SensorID of element %v is not equal between expected and actual", i)
}
}
}

View File

@ -2,7 +2,7 @@ package types
import "time"
// Device ...
// Device represent a device with all his settings.
type Device struct {
ID string `json:"id" xml:"id"`
Name string `json:"name" xml:"name"`

View File

@ -1,206 +0,0 @@
package types
import (
"fmt"
)
type GPIO string
const (
GPIO02 GPIO = "GPIO02"
GPIO03 = "GPIO03"
GPIO04 = "GPIO04"
GPIO05 = "GPIO05"
GPIO06 = "GPIO06"
GPIO07 = "GPIO07"
GPIO08 = "GPIO08"
GPIO10 = "GPIO10"
GPIO11 = "GPIO11"
GPIO12 = "GPIO12"
GPIO13 = "GPIO13"
GPIO14 = "GPIO14"
GPIO15 = "GPIO15"
GPIO16 = "GPIO16"
GPIO17 = "GPIO17"
GPIO18 = "GPIO18"
GPIO19 = "GPIO19"
GPIO20 = "GPIO20"
GPIO21 = "GPIO21"
GPIO22 = "GPIO22"
GPIO23 = "GPIO23"
GPIO24 = "GPIO24"
GPIO25 = "GPIO25"
GPIO26 = "GPIO26"
GPIO27 = "GPIO27"
)
func GPIOToString(gpio GPIO) (string, error) {
switch gpio {
case GPIO02:
return "GPIO02", nil
case GPIO03:
return "GPIO03", nil
case GPIO04:
return "GPIO04", nil
case GPIO05:
return "GPIO05", nil
case GPIO06:
return "GPIO06", nil
case GPIO07:
return "GPIO07", nil
case GPIO08:
return "GPIO08", nil
case GPIO10:
return "GPIO10", nil
case GPIO11:
return "GPIO11", nil
case GPIO12:
return "GPIO12", nil
case GPIO13:
return "GPIO13", nil
case GPIO14:
return "GPIO14", nil
case GPIO15:
return "GPIO15", nil
case GPIO16:
return "GPIO16", nil
case GPIO17:
return "GPIO17", nil
case GPIO18:
return "GPIO18", nil
case GPIO19:
return "GPIO19", nil
case GPIO20:
return "GPIO20", nil
case GPIO21:
return "GPIO21", nil
case GPIO22:
return "GPIO22", nil
case GPIO23:
return "GPIO23", nil
case GPIO24:
return "GPIO24", nil
case GPIO25:
return "GPIO25", nil
case GPIO26:
return "GPIO26", nil
case GPIO27:
return "GPIO27", nil
default:
return "", fmt.Errorf("Can not determine gpio %v", gpio)
}
}
func GPIOToInt(gpio GPIO) (int, error) {
switch gpio {
case GPIO02:
return 2, nil
case GPIO03:
return 3, nil
case GPIO04:
return 4, nil
case GPIO05:
return 5, nil
case GPIO06:
return 6, nil
case GPIO07:
return 7, nil
case GPIO08:
return 8, nil
case GPIO10:
return 10, nil
case GPIO11:
return 11, nil
case GPIO12:
return 12, nil
case GPIO13:
return 13, nil
case GPIO14:
return 14, nil
case GPIO15:
return 15, nil
case GPIO16:
return 16, nil
case GPIO17:
return 17, nil
case GPIO18:
return 18, nil
case GPIO19:
return 19, nil
case GPIO20:
return 20, nil
case GPIO21:
return 21, nil
case GPIO22:
return 22, nil
case GPIO23:
return 23, nil
case GPIO24:
return 24, nil
case GPIO25:
return 25, nil
case GPIO26:
return 26, nil
case GPIO27:
return 27, nil
default:
return 0, fmt.Errorf("Can not determine gpio %v", gpio)
}
}
func StringToGPIO(gpio string) (GPIO, error) {
switch gpio {
case "GPIO02":
return GPIO02, nil
case "GPIO03":
return GPIO03, nil
case "GPIO04":
return GPIO04, nil
case "GPIO05":
return GPIO05, nil
case "GPIO06":
return GPIO06, nil
case "GPIO07":
return GPIO07, nil
case "GPIO08":
return GPIO08, nil
case "GPIO10":
return GPIO10, nil
case "GPIO11":
return GPIO11, nil
case "GPIO12":
return GPIO12, nil
case "GPIO13":
return GPIO13, nil
case "GPIO14":
return GPIO14, nil
case "GPIO15":
return GPIO15, nil
case "GPIO16":
return GPIO16, nil
case "GPIO17":
return GPIO17, nil
case "GPIO18":
return GPIO18, nil
case "GPIO19":
return GPIO19, nil
case "GPIO20":
return GPIO20, nil
case "GPIO21":
return GPIO21, nil
case "GPIO22":
return GPIO22, nil
case "GPIO23":
return GPIO23, nil
case "GPIO24":
return GPIO24, nil
case "GPIO25":
return GPIO25, nil
case "GPIO26":
return GPIO26, nil
case "GPIO27":
return GPIO27, nil
default:
return "", fmt.Errorf("Can not determine gpio %v", gpio)
}
}

View File

@ -1,79 +0,0 @@
package types
import (
"fmt"
"time"
)
type RGBLED struct {
RGBLEDID string `json:"rgbled_id" xml:"rgbled_id"`
RGBLEDName string `json:"rgbled_name" xml:"rgbled_name"`
RGBLEDLocation string `json:"rgbled_location" xml:"rgb_location"`
BaseColorsToGPIO map[BaseColor]*GPIO `json:"base_colors_to_gpio" xml:"base_colors_to_gpio"`
ActionMapping map[LEDAction]LEDColor `json:"action_mapping" xml:"action_mapping"`
RGBLEDEnabled bool `json:"rgbled_enabled" xml:"rgb_enabled"`
DeviceID string `json:"device_id" xml:"device_id"`
CreationDate time.Time `json:"creation_date" xml:"creation_date"`
}
type BaseColor string
const (
BaseColorBlue BaseColor = "blue"
BaseColorRed BaseColor = "red"
BaseColorGreen BaseColor = "green"
)
type LEDColor string
const (
LEDColorBlue LEDColor = "blue"
LEDColorGreen LEDColor = "green"
LEDColorNone LEDColor = "none"
LEDColorPurple LEDColor = "purple"
LEDColorRed LEDColor = "red"
LEDColorTurquoise LEDColor = "turquoise"
LEDColorYellow LEDColor = "yellow"
LEDColorWhite LEDColor = "white"
)
func StringToLEDColor(color string) (LEDColor, error) {
switch color {
case "blue":
return LEDColorBlue, nil
case "red":
return LEDColorRed, nil
case "green":
return LEDColorGreen, nil
case "none":
return LEDColorNone, nil
case "purple":
return LEDColorPurple, nil
case "turquoise":
return LEDColorTurquoise, nil
case "yellow":
return LEDColorYellow, nil
case "white":
return LEDColorWhite, nil
default:
return LEDColorNone, fmt.Errorf("Can not convert color to const")
}
}
type LEDAction string
const (
LEDActionError LEDAction = "error"
LEDActionWarn = "warn"
LEDActionRun = "run"
LEDActionSync = "sync"
LEDActionLogfile = "logfile"
)
var DefaultActionMapping = map[LEDAction]LEDColor{
LEDActionError: LEDColorRed,
LEDActionWarn: LEDColorYellow,
LEDActionRun: LEDColorGreen,
LEDActionSync: LEDColorTurquoise,
LEDActionLogfile: LEDColorBlue,
}

View File

@ -1,51 +1,19 @@
package types
import (
"fmt"
"time"
)
// MeasuredValue represent a value provided by a measuring instrument. For
// example from a sensor. It can contains different types, for example humidity
// or temperature.
type MeasuredValue struct {
ID string `json:"id" xml:"id"`
Value float64 `json:"value,string" xml:"value,string"`
ValueType MeasuredValueType `json:"value_type" xml:"value_type"`
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"`
UpdateDate *time.Time `json:"update_date" xml:"update_date"`
}
type MeasuredValueType string
const (
MeasuredValueTypeHumidity MeasuredValueType = "humidity"
MeasuredValueTypePressure MeasuredValueType = "pressure"
MeasuredValueTypeTemperature MeasuredValueType = "temperature"
)
var MeasuredValueTypes = []MeasuredValueType{
MeasuredValueTypeHumidity,
MeasuredValueTypePressure,
MeasuredValueTypeTemperature,
}
func SelectMeasuredValues(measuredValueType MeasuredValueType, measuredValues []*MeasuredValue) []*MeasuredValue {
cachedMeasuredValues := make([]*MeasuredValue, 0)
for _, measuredValue := range measuredValues {
if measuredValue.ValueType == measuredValueType {
cachedMeasuredValues = append(cachedMeasuredValues, measuredValue)
}
}
return cachedMeasuredValues
}
func SelectMeasuredValueType(valueType string) (*MeasuredValueType, error) {
for _, measuredValueType := range MeasuredValueTypes {
if fmt.Sprint(measuredValueType) == valueType {
return &measuredValueType, nil
}
}
return nil, fmt.Errorf("Can not determine value type: %v", valueType)
ID string `json:"id" xml:"id"`
Value float64 `json:"value,string" xml:"value,string"`
ValueType string `json:"value_type" xml:"value_type"`
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"`
UpdateDate *time.Time `json:"update_date" xml:"update_date"`
}

View File

@ -1,57 +1,48 @@
package types
import (
"encoding/json"
"fmt"
"io"
"time"
)
// Sensor ...
// Sensor represents a sensor with all his settings. The struct does not
// contains any read method.
type Sensor struct {
ID string `json:"id" xml:"id"`
Name string `json:"name" xml:"name"`
Location string `json:"location" xml:"location"`
WireID *string `json:"wire_id" xml:"wire_id"`
I2CBus *int `json:"i2c_bus" xml:"i2c_bus"`
I2CAddress *uint8 `json:"i2c_address" xml:"i2c_address"`
GPIONumber *GPIO `json:"gpio_number" xml:"gpio_number"`
Model SensorModel `json:"model" xml:"model"`
Enabled bool `json:"enabled" xml:"enabled"`
LastContact *time.Time `json:"last_contact" xml:"last_contact"`
TickDuration string `json:"tick_duration" xml:"tick_duration"`
DeviceID string `json:"device_id" xml:"device_id"`
CreationDate time.Time `json:"creation_date" xml:"creation_date"`
ID string `json:"id" xml:"id"`
Name string `json:"name" xml:"name"`
Location string `json:"location" xml:"location"`
WireID *string `json:"wire_id" xml:"wire_id"`
I2CBus *int `json:"i2c_bus" xml:"i2c_bus"`
I2CAddress *uint8 `json:"i2c_address" xml:"i2c_address"`
GPIONumber string `json:"gpio_number" xml:"gpio_number"`
Model string `json:"model" xml:"model"`
Enabled bool `json:"enabled" xml:"enabled"`
LastContact *time.Time `json:"last_contact" xml:"last_contact"`
TickDuration string `json:"tick_duration" xml:"tick_duration"`
DeviceID string `json:"device_id" xml:"device_id"`
CreationDate time.Time `json:"creation_date" xml:"creation_date"`
}
// JSONDecoder decodes a json into a sensor
func (s *Sensor) JSONDecoder(r io.Reader) error {
decoder := json.NewDecoder(r)
err := decoder.Decode(s)
if err != nil {
return fmt.Errorf("Can not decode sensor from json: %v", err)
}
return nil
}
func (s *Sensor) JSONEncoder(w io.Writer) error {
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
err := encoder.Encode(s)
if err != nil {
return fmt.Errorf("Can not encode sensor to json: %v", err)
}
return nil
}
func (s *Sensor) FullName() string {
if s.Name != "" {
return s.Name
} else if s.WireID != nil {
return *s.WireID
} else if s.I2CAddress != nil &&
s.I2CBus != nil {
return fmt.Sprintf("%v/%v", *s.I2CBus, *s.I2CAddress)
}
// GetID returns the UUID of the sensor.
func (s *Sensor) GetID() string {
return s.ID
}
// GetDeviceID returns the UUID of the configured device.
func (s *Sensor) GetDeviceID() string {
return s.DeviceID
}
// GetName returns the name of the sensor.
func (s *Sensor) GetName() string {
return s.Name
}
// GetTicker returns a new ticker, which tick every when the sensor should be
// read
func (s *Sensor) GetTicker() *time.Ticker {
duration, err := time.ParseDuration(s.TickDuration)
if err != nil {
duration = time.Minute
}
return time.NewTicker(duration)
}

View File

@ -1,28 +0,0 @@
package types
import "fmt"
type SensorModel string
const (
BME280 SensorModel = "BME280"
DHT11 = "DHT11"
DHT22 = "DHT22"
DS18B20 = "DS18B20"
)
// SelectSensorModel converts a string into a constant
func SelectSensorModel(model string) (SensorModel, error) {
switch model {
case "BME280":
return BME280, nil
case "DHT11":
return DHT11, nil
case "DHT22":
return DHT22, nil
case "DS18B20":
return DS18B20, nil
default:
return "", fmt.Errorf("Sensor Model %v currently not supported", model)
}
}

View File

@ -1,5 +0,0 @@
#!/bin/bash
scp -r ${FLUCKY_REMOTE}:/var/log/flucky/*.csv csv
scp -r ${FLUCKY_REMOTE}:/var/log/flucky/*.json json
scp -r ${FLUCKY_REMOTE}:/var/log/flucky/*.xml xml

View File

@ -1,18 +0,0 @@
0fc9c4bf-7e86-4cee-b99c-7642984b5fcd,humidity,50.8740234375,2019-10-11T11:51:01.969+02:00,2019-10-11T11:51:03.109+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:01.971+02:00,2019-10-11T11:51:20.729+02:00
7b4f0fc3-ecec-4226-b621-49dd01cf9eb7,humidity,50.884765625,2019-10-11T11:51:04.251+02:00,2019-10-11T11:51:04.252+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:04.254+02:00,null
72ccf0a7-e680-460c-a3b8-7d14f46b5b0c,humidity,50.861328125,2019-10-11T11:51:05.402+02:00,2019-10-11T11:51:05.403+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:05.405+02:00,null
6f5277d9-abe3-4e87-b0f1-e4320756bbac,humidity,50.8505859375,2019-10-11T11:51:06.551+02:00,2019-10-11T11:51:06.552+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:06.565+02:00,null
026370e8-2011-4bf0-8da9-ec4a9fe1ee19,humidity,50.7734375,2019-10-11T11:51:07.7+02:00,2019-10-11T11:51:07.7+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:07.702+02:00,null
e96eff46-8322-424d-9a79-51c746124354,humidity,50.728515625,2019-10-11T11:51:08.843+02:00,2019-10-11T11:51:08.843+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:08.846+02:00,null
6beb6aee-6888-4f4a-82ff-5bf700a22b0a,humidity,50.67578125,2019-10-11T11:51:09.986+02:00,2019-10-11T11:51:09.986+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:09.989+02:00,null
62a8f169-344d-4f9c-828a-7b4ea4261fad,humidity,50.6650390625,2019-10-11T11:51:11.129+02:00,2019-10-11T11:51:11.13+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:11.133+02:00,null
b1d64d40-59d7-4093-ba1d-208d869c9aae,humidity,50.6884765625,2019-10-11T11:51:11.264+02:00,2019-10-11T11:51:11.265+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:11.267+02:00,null
f98b69ee-bef9-4609-8bfa-82f11d7f51b6,humidity,50.76171875,2019-10-11T11:51:12.401+02:00,2019-10-11T11:51:12.402+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:12.41+02:00,null
eab34e23-2b43-40c2-9d6e-90e72fa13db5,humidity,50.7373046875,2019-10-11T11:51:13.551+02:00,2019-10-11T11:51:13.551+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:13.554+02:00,null
4659b946-8a40-4ba4-9888-6997bd1795f2,humidity,50.73828125,2019-10-11T11:51:14.695+02:00,2019-10-11T11:51:14.695+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:14.698+02:00,null
a6552aff-724c-4339-810a-36d911ddaa58,humidity,50.6845703125,2019-10-11T11:51:15.839+02:00,2019-10-11T11:51:15.839+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:15.842+02:00,null
c311cacd-e346-4b68-902b-400be0f8e02e,humidity,50.6767578125,2019-10-11T11:51:15.976+02:00,2019-10-11T11:51:15.977+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:15.98+02:00,null
2d4db614-17f8-4ddd-ba08-e2a5f77d74ea,humidity,50.68359375,2019-10-11T11:51:17.118+02:00,2019-10-11T11:51:17.118+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:17.121+02:00,null
936d61e4-494e-4959-a39a-c1dbc4a7d162,humidity,50.685546875,2019-10-11T11:51:17.243+02:00,2019-10-11T11:51:17.244+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:17.247+02:00,null
b913c9ca-4b5e-4c01-9aea-ee31d0d97977,humidity,50.6943359375,2019-10-11T11:51:18.381+02:00,2019-10-11T11:51:19.529+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:18.385+02:00,2019-10-11T11:51:20.729+02:00
d1a5e29c-d7e7-4ccc-9ff7-80ba1f797312,humidity,50.7080078125,2019-10-11T11:51:19.664+02:00,2019-10-11T11:51:19.664+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:19.667+02:00,null
1 0fc9c4bf-7e86-4cee-b99c-7642984b5fcd humidity 50.8740234375 2019-10-11T11:51:01.969+02:00 2019-10-11T11:51:03.109+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:01.971+02:00 2019-10-11T11:51:20.729+02:00
2 7b4f0fc3-ecec-4226-b621-49dd01cf9eb7 humidity 50.884765625 2019-10-11T11:51:04.251+02:00 2019-10-11T11:51:04.252+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:04.254+02:00 null
3 72ccf0a7-e680-460c-a3b8-7d14f46b5b0c humidity 50.861328125 2019-10-11T11:51:05.402+02:00 2019-10-11T11:51:05.403+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:05.405+02:00 null
4 6f5277d9-abe3-4e87-b0f1-e4320756bbac humidity 50.8505859375 2019-10-11T11:51:06.551+02:00 2019-10-11T11:51:06.552+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:06.565+02:00 null
5 026370e8-2011-4bf0-8da9-ec4a9fe1ee19 humidity 50.7734375 2019-10-11T11:51:07.7+02:00 2019-10-11T11:51:07.7+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:07.702+02:00 null
6 e96eff46-8322-424d-9a79-51c746124354 humidity 50.728515625 2019-10-11T11:51:08.843+02:00 2019-10-11T11:51:08.843+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:08.846+02:00 null
7 6beb6aee-6888-4f4a-82ff-5bf700a22b0a humidity 50.67578125 2019-10-11T11:51:09.986+02:00 2019-10-11T11:51:09.986+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:09.989+02:00 null
8 62a8f169-344d-4f9c-828a-7b4ea4261fad humidity 50.6650390625 2019-10-11T11:51:11.129+02:00 2019-10-11T11:51:11.13+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:11.133+02:00 null
9 b1d64d40-59d7-4093-ba1d-208d869c9aae humidity 50.6884765625 2019-10-11T11:51:11.264+02:00 2019-10-11T11:51:11.265+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:11.267+02:00 null
10 f98b69ee-bef9-4609-8bfa-82f11d7f51b6 humidity 50.76171875 2019-10-11T11:51:12.401+02:00 2019-10-11T11:51:12.402+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:12.41+02:00 null
11 eab34e23-2b43-40c2-9d6e-90e72fa13db5 humidity 50.7373046875 2019-10-11T11:51:13.551+02:00 2019-10-11T11:51:13.551+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:13.554+02:00 null
12 4659b946-8a40-4ba4-9888-6997bd1795f2 humidity 50.73828125 2019-10-11T11:51:14.695+02:00 2019-10-11T11:51:14.695+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:14.698+02:00 null
13 a6552aff-724c-4339-810a-36d911ddaa58 humidity 50.6845703125 2019-10-11T11:51:15.839+02:00 2019-10-11T11:51:15.839+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:15.842+02:00 null
14 c311cacd-e346-4b68-902b-400be0f8e02e humidity 50.6767578125 2019-10-11T11:51:15.976+02:00 2019-10-11T11:51:15.977+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:15.98+02:00 null
15 2d4db614-17f8-4ddd-ba08-e2a5f77d74ea humidity 50.68359375 2019-10-11T11:51:17.118+02:00 2019-10-11T11:51:17.118+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:17.121+02:00 null
16 936d61e4-494e-4959-a39a-c1dbc4a7d162 humidity 50.685546875 2019-10-11T11:51:17.243+02:00 2019-10-11T11:51:17.244+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:17.247+02:00 null
17 b913c9ca-4b5e-4c01-9aea-ee31d0d97977 humidity 50.6943359375 2019-10-11T11:51:18.381+02:00 2019-10-11T11:51:19.529+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:18.385+02:00 2019-10-11T11:51:20.729+02:00
18 d1a5e29c-d7e7-4ccc-9ff7-80ba1f797312 humidity 50.7080078125 2019-10-11T11:51:19.664+02:00 2019-10-11T11:51:19.664+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:19.667+02:00 null

View File

@ -1,4 +0,0 @@
0fc9c4bf-7e86-4cee-b99c-7642984b5fcd,humidity,51,2019-10-11T11:51:01.969+02:00,2019-10-11T11:51:07.7+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:01.971+02:00,2019-10-11T11:51:20.754+02:00
e96eff46-8322-424d-9a79-51c746124354,humidity,50.5,2019-10-11T11:51:08.843+02:00,2019-10-11T11:51:11.265+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:08.846+02:00,2019-10-11T11:51:20.754+02:00
f98b69ee-bef9-4609-8bfa-82f11d7f51b6,humidity,51,2019-10-11T11:51:12.401+02:00,2019-10-11T11:51:12.402+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:12.41+02:00,null
eab34e23-2b43-40c2-9d6e-90e72fa13db5,humidity,50.5,2019-10-11T11:51:13.551+02:00,2019-10-11T11:51:19.664+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:13.554+02:00,2019-10-11T11:51:20.754+02:00
1 0fc9c4bf-7e86-4cee-b99c-7642984b5fcd humidity 51 2019-10-11T11:51:01.969+02:00 2019-10-11T11:51:07.7+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:01.971+02:00 2019-10-11T11:51:20.754+02:00
2 e96eff46-8322-424d-9a79-51c746124354 humidity 50.5 2019-10-11T11:51:08.843+02:00 2019-10-11T11:51:11.265+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:08.846+02:00 2019-10-11T11:51:20.754+02:00
3 f98b69ee-bef9-4609-8bfa-82f11d7f51b6 humidity 51 2019-10-11T11:51:12.401+02:00 2019-10-11T11:51:12.402+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:12.41+02:00 null
4 eab34e23-2b43-40c2-9d6e-90e72fa13db5 humidity 50.5 2019-10-11T11:51:13.551+02:00 2019-10-11T11:51:19.664+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:13.554+02:00 2019-10-11T11:51:20.754+02:00

View File

@ -1,20 +0,0 @@
0fc9c4bf-7e86-4cee-b99c-7642984b5fcd,humidity,50.8740234375,2019-10-11T11:51:01.969+02:00,2019-10-11T11:51:01.969+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:01.971+02:00,null
45e8b20c-1ab0-4cf1-9ef1-aa82537557f6,humidity,50.8740234375,2019-10-11T11:51:03.109+02:00,2019-10-11T11:51:03.109+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:03.111+02:00,null
7b4f0fc3-ecec-4226-b621-49dd01cf9eb7,humidity,50.884765625,2019-10-11T11:51:04.251+02:00,2019-10-11T11:51:04.252+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:04.254+02:00,null
72ccf0a7-e680-460c-a3b8-7d14f46b5b0c,humidity,50.861328125,2019-10-11T11:51:05.402+02:00,2019-10-11T11:51:05.403+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:05.405+02:00,null
6f5277d9-abe3-4e87-b0f1-e4320756bbac,humidity,50.8505859375,2019-10-11T11:51:06.551+02:00,2019-10-11T11:51:06.552+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:06.565+02:00,null
026370e8-2011-4bf0-8da9-ec4a9fe1ee19,humidity,50.7734375,2019-10-11T11:51:07.7+02:00,2019-10-11T11:51:07.7+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:07.702+02:00,null
e96eff46-8322-424d-9a79-51c746124354,humidity,50.728515625,2019-10-11T11:51:08.843+02:00,2019-10-11T11:51:08.843+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:08.846+02:00,null
6beb6aee-6888-4f4a-82ff-5bf700a22b0a,humidity,50.67578125,2019-10-11T11:51:09.986+02:00,2019-10-11T11:51:09.986+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:09.989+02:00,null
62a8f169-344d-4f9c-828a-7b4ea4261fad,humidity,50.6650390625,2019-10-11T11:51:11.129+02:00,2019-10-11T11:51:11.13+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:11.133+02:00,null
b1d64d40-59d7-4093-ba1d-208d869c9aae,humidity,50.6884765625,2019-10-11T11:51:11.264+02:00,2019-10-11T11:51:11.265+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:11.267+02:00,null
f98b69ee-bef9-4609-8bfa-82f11d7f51b6,humidity,50.76171875,2019-10-11T11:51:12.401+02:00,2019-10-11T11:51:12.402+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:12.41+02:00,null
eab34e23-2b43-40c2-9d6e-90e72fa13db5,humidity,50.7373046875,2019-10-11T11:51:13.551+02:00,2019-10-11T11:51:13.551+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:13.554+02:00,null
4659b946-8a40-4ba4-9888-6997bd1795f2,humidity,50.73828125,2019-10-11T11:51:14.695+02:00,2019-10-11T11:51:14.695+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:14.698+02:00,null
a6552aff-724c-4339-810a-36d911ddaa58,humidity,50.6845703125,2019-10-11T11:51:15.839+02:00,2019-10-11T11:51:15.839+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:15.842+02:00,null
c311cacd-e346-4b68-902b-400be0f8e02e,humidity,50.6767578125,2019-10-11T11:51:15.976+02:00,2019-10-11T11:51:15.977+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:15.98+02:00,null
2d4db614-17f8-4ddd-ba08-e2a5f77d74ea,humidity,50.68359375,2019-10-11T11:51:17.118+02:00,2019-10-11T11:51:17.118+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:17.121+02:00,null
936d61e4-494e-4959-a39a-c1dbc4a7d162,humidity,50.685546875,2019-10-11T11:51:17.243+02:00,2019-10-11T11:51:17.244+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:17.247+02:00,null
b913c9ca-4b5e-4c01-9aea-ee31d0d97977,humidity,50.6943359375,2019-10-11T11:51:18.381+02:00,2019-10-11T11:51:18.382+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:18.385+02:00,null
1171d0dd-2173-4da2-b2c6-7f7daaad7cd1,humidity,50.6943359375,2019-10-11T11:51:19.529+02:00,2019-10-11T11:51:19.529+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:19.532+02:00,null
d1a5e29c-d7e7-4ccc-9ff7-80ba1f797312,humidity,50.7080078125,2019-10-11T11:51:19.664+02:00,2019-10-11T11:51:19.664+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:19.667+02:00,null
1 0fc9c4bf-7e86-4cee-b99c-7642984b5fcd humidity 50.8740234375 2019-10-11T11:51:01.969+02:00 2019-10-11T11:51:01.969+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:01.971+02:00 null
2 45e8b20c-1ab0-4cf1-9ef1-aa82537557f6 humidity 50.8740234375 2019-10-11T11:51:03.109+02:00 2019-10-11T11:51:03.109+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:03.111+02:00 null
3 7b4f0fc3-ecec-4226-b621-49dd01cf9eb7 humidity 50.884765625 2019-10-11T11:51:04.251+02:00 2019-10-11T11:51:04.252+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:04.254+02:00 null
4 72ccf0a7-e680-460c-a3b8-7d14f46b5b0c humidity 50.861328125 2019-10-11T11:51:05.402+02:00 2019-10-11T11:51:05.403+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:05.405+02:00 null
5 6f5277d9-abe3-4e87-b0f1-e4320756bbac humidity 50.8505859375 2019-10-11T11:51:06.551+02:00 2019-10-11T11:51:06.552+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:06.565+02:00 null
6 026370e8-2011-4bf0-8da9-ec4a9fe1ee19 humidity 50.7734375 2019-10-11T11:51:07.7+02:00 2019-10-11T11:51:07.7+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:07.702+02:00 null
7 e96eff46-8322-424d-9a79-51c746124354 humidity 50.728515625 2019-10-11T11:51:08.843+02:00 2019-10-11T11:51:08.843+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:08.846+02:00 null
8 6beb6aee-6888-4f4a-82ff-5bf700a22b0a humidity 50.67578125 2019-10-11T11:51:09.986+02:00 2019-10-11T11:51:09.986+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:09.989+02:00 null
9 62a8f169-344d-4f9c-828a-7b4ea4261fad humidity 50.6650390625 2019-10-11T11:51:11.129+02:00 2019-10-11T11:51:11.13+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:11.133+02:00 null
10 b1d64d40-59d7-4093-ba1d-208d869c9aae humidity 50.6884765625 2019-10-11T11:51:11.264+02:00 2019-10-11T11:51:11.265+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:11.267+02:00 null
11 f98b69ee-bef9-4609-8bfa-82f11d7f51b6 humidity 50.76171875 2019-10-11T11:51:12.401+02:00 2019-10-11T11:51:12.402+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:12.41+02:00 null
12 eab34e23-2b43-40c2-9d6e-90e72fa13db5 humidity 50.7373046875 2019-10-11T11:51:13.551+02:00 2019-10-11T11:51:13.551+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:13.554+02:00 null
13 4659b946-8a40-4ba4-9888-6997bd1795f2 humidity 50.73828125 2019-10-11T11:51:14.695+02:00 2019-10-11T11:51:14.695+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:14.698+02:00 null
14 a6552aff-724c-4339-810a-36d911ddaa58 humidity 50.6845703125 2019-10-11T11:51:15.839+02:00 2019-10-11T11:51:15.839+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:15.842+02:00 null
15 c311cacd-e346-4b68-902b-400be0f8e02e humidity 50.6767578125 2019-10-11T11:51:15.976+02:00 2019-10-11T11:51:15.977+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:15.98+02:00 null
16 2d4db614-17f8-4ddd-ba08-e2a5f77d74ea humidity 50.68359375 2019-10-11T11:51:17.118+02:00 2019-10-11T11:51:17.118+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:17.121+02:00 null
17 936d61e4-494e-4959-a39a-c1dbc4a7d162 humidity 50.685546875 2019-10-11T11:51:17.243+02:00 2019-10-11T11:51:17.244+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:17.247+02:00 null
18 b913c9ca-4b5e-4c01-9aea-ee31d0d97977 humidity 50.6943359375 2019-10-11T11:51:18.381+02:00 2019-10-11T11:51:18.382+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:18.385+02:00 null
19 1171d0dd-2173-4da2-b2c6-7f7daaad7cd1 humidity 50.6943359375 2019-10-11T11:51:19.529+02:00 2019-10-11T11:51:19.529+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:19.532+02:00 null
20 d1a5e29c-d7e7-4ccc-9ff7-80ba1f797312 humidity 50.7080078125 2019-10-11T11:51:19.664+02:00 2019-10-11T11:51:19.664+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:19.667+02:00 null

View File

@ -1,20 +0,0 @@
0fc9c4bf-7e86-4cee-b99c-7642984b5fcd,humidity,51,2019-10-11T11:51:01.969+02:00,2019-10-11T11:51:01.969+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:01.971+02:00,null
45e8b20c-1ab0-4cf1-9ef1-aa82537557f6,humidity,51,2019-10-11T11:51:03.109+02:00,2019-10-11T11:51:03.109+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:03.111+02:00,null
7b4f0fc3-ecec-4226-b621-49dd01cf9eb7,humidity,51,2019-10-11T11:51:04.251+02:00,2019-10-11T11:51:04.252+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:04.254+02:00,null
72ccf0a7-e680-460c-a3b8-7d14f46b5b0c,humidity,51,2019-10-11T11:51:05.402+02:00,2019-10-11T11:51:05.403+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:05.405+02:00,null
6f5277d9-abe3-4e87-b0f1-e4320756bbac,humidity,51,2019-10-11T11:51:06.551+02:00,2019-10-11T11:51:06.552+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:06.565+02:00,null
026370e8-2011-4bf0-8da9-ec4a9fe1ee19,humidity,51,2019-10-11T11:51:07.7+02:00,2019-10-11T11:51:07.7+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:07.702+02:00,null
e96eff46-8322-424d-9a79-51c746124354,humidity,50.5,2019-10-11T11:51:08.843+02:00,2019-10-11T11:51:08.843+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:08.846+02:00,null
6beb6aee-6888-4f4a-82ff-5bf700a22b0a,humidity,50.5,2019-10-11T11:51:09.986+02:00,2019-10-11T11:51:09.986+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:09.989+02:00,null
62a8f169-344d-4f9c-828a-7b4ea4261fad,humidity,50.5,2019-10-11T11:51:11.129+02:00,2019-10-11T11:51:11.13+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:11.133+02:00,null
b1d64d40-59d7-4093-ba1d-208d869c9aae,humidity,50.5,2019-10-11T11:51:11.264+02:00,2019-10-11T11:51:11.265+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:11.267+02:00,null
f98b69ee-bef9-4609-8bfa-82f11d7f51b6,humidity,51,2019-10-11T11:51:12.401+02:00,2019-10-11T11:51:12.402+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:12.41+02:00,null
eab34e23-2b43-40c2-9d6e-90e72fa13db5,humidity,50.5,2019-10-11T11:51:13.551+02:00,2019-10-11T11:51:13.551+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:13.554+02:00,null
4659b946-8a40-4ba4-9888-6997bd1795f2,humidity,50.5,2019-10-11T11:51:14.695+02:00,2019-10-11T11:51:14.695+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:14.698+02:00,null
a6552aff-724c-4339-810a-36d911ddaa58,humidity,50.5,2019-10-11T11:51:15.839+02:00,2019-10-11T11:51:15.839+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:15.842+02:00,null
c311cacd-e346-4b68-902b-400be0f8e02e,humidity,50.5,2019-10-11T11:51:15.976+02:00,2019-10-11T11:51:15.977+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:15.98+02:00,null
2d4db614-17f8-4ddd-ba08-e2a5f77d74ea,humidity,50.5,2019-10-11T11:51:17.118+02:00,2019-10-11T11:51:17.118+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:17.121+02:00,null
936d61e4-494e-4959-a39a-c1dbc4a7d162,humidity,50.5,2019-10-11T11:51:17.243+02:00,2019-10-11T11:51:17.244+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:17.247+02:00,null
b913c9ca-4b5e-4c01-9aea-ee31d0d97977,humidity,50.5,2019-10-11T11:51:18.381+02:00,2019-10-11T11:51:18.382+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:18.385+02:00,null
1171d0dd-2173-4da2-b2c6-7f7daaad7cd1,humidity,50.5,2019-10-11T11:51:19.529+02:00,2019-10-11T11:51:19.529+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:19.532+02:00,null
d1a5e29c-d7e7-4ccc-9ff7-80ba1f797312,humidity,50.5,2019-10-11T11:51:19.664+02:00,2019-10-11T11:51:19.664+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:19.667+02:00,null
1 0fc9c4bf-7e86-4cee-b99c-7642984b5fcd humidity 51 2019-10-11T11:51:01.969+02:00 2019-10-11T11:51:01.969+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:01.971+02:00 null
2 45e8b20c-1ab0-4cf1-9ef1-aa82537557f6 humidity 51 2019-10-11T11:51:03.109+02:00 2019-10-11T11:51:03.109+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:03.111+02:00 null
3 7b4f0fc3-ecec-4226-b621-49dd01cf9eb7 humidity 51 2019-10-11T11:51:04.251+02:00 2019-10-11T11:51:04.252+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:04.254+02:00 null
4 72ccf0a7-e680-460c-a3b8-7d14f46b5b0c humidity 51 2019-10-11T11:51:05.402+02:00 2019-10-11T11:51:05.403+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:05.405+02:00 null
5 6f5277d9-abe3-4e87-b0f1-e4320756bbac humidity 51 2019-10-11T11:51:06.551+02:00 2019-10-11T11:51:06.552+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:06.565+02:00 null
6 026370e8-2011-4bf0-8da9-ec4a9fe1ee19 humidity 51 2019-10-11T11:51:07.7+02:00 2019-10-11T11:51:07.7+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:07.702+02:00 null
7 e96eff46-8322-424d-9a79-51c746124354 humidity 50.5 2019-10-11T11:51:08.843+02:00 2019-10-11T11:51:08.843+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:08.846+02:00 null
8 6beb6aee-6888-4f4a-82ff-5bf700a22b0a humidity 50.5 2019-10-11T11:51:09.986+02:00 2019-10-11T11:51:09.986+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:09.989+02:00 null
9 62a8f169-344d-4f9c-828a-7b4ea4261fad humidity 50.5 2019-10-11T11:51:11.129+02:00 2019-10-11T11:51:11.13+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:11.133+02:00 null
10 b1d64d40-59d7-4093-ba1d-208d869c9aae humidity 50.5 2019-10-11T11:51:11.264+02:00 2019-10-11T11:51:11.265+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:11.267+02:00 null
11 f98b69ee-bef9-4609-8bfa-82f11d7f51b6 humidity 51 2019-10-11T11:51:12.401+02:00 2019-10-11T11:51:12.402+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:12.41+02:00 null
12 eab34e23-2b43-40c2-9d6e-90e72fa13db5 humidity 50.5 2019-10-11T11:51:13.551+02:00 2019-10-11T11:51:13.551+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:13.554+02:00 null
13 4659b946-8a40-4ba4-9888-6997bd1795f2 humidity 50.5 2019-10-11T11:51:14.695+02:00 2019-10-11T11:51:14.695+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:14.698+02:00 null
14 a6552aff-724c-4339-810a-36d911ddaa58 humidity 50.5 2019-10-11T11:51:15.839+02:00 2019-10-11T11:51:15.839+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:15.842+02:00 null
15 c311cacd-e346-4b68-902b-400be0f8e02e humidity 50.5 2019-10-11T11:51:15.976+02:00 2019-10-11T11:51:15.977+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:15.98+02:00 null
16 2d4db614-17f8-4ddd-ba08-e2a5f77d74ea humidity 50.5 2019-10-11T11:51:17.118+02:00 2019-10-11T11:51:17.118+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:17.121+02:00 null
17 936d61e4-494e-4959-a39a-c1dbc4a7d162 humidity 50.5 2019-10-11T11:51:17.243+02:00 2019-10-11T11:51:17.244+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:17.247+02:00 null
18 b913c9ca-4b5e-4c01-9aea-ee31d0d97977 humidity 50.5 2019-10-11T11:51:18.381+02:00 2019-10-11T11:51:18.382+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:18.385+02:00 null
19 1171d0dd-2173-4da2-b2c6-7f7daaad7cd1 humidity 50.5 2019-10-11T11:51:19.529+02:00 2019-10-11T11:51:19.529+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:19.532+02:00 null
20 d1a5e29c-d7e7-4ccc-9ff7-80ba1f797312 humidity 50.5 2019-10-11T11:51:19.664+02:00 2019-10-11T11:51:19.664+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:19.667+02:00 null

View File

@ -1,50 +0,0 @@
2029cc8c-56cf-4cd1-adf8-06e768fbad29,temperature,20.90999984741211,2019-10-11T11:50:43.317+02:00,2019-10-11T11:50:43.317+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:43.319+02:00,null
a588cc90-4dca-4a4e-be81-2d20b9f64458,temperature,20.920000076293945,2019-10-11T11:50:44.459+02:00,2019-10-11T11:50:47.745+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:44.473+02:00,2019-10-11T11:51:01.71+02:00
88f5c095-8e88-4c80-b66a-cf154919afa8,temperature,20.93000030517578,2019-10-11T11:50:49.53+02:00,2019-10-11T11:50:49.53+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:50.018+02:00,null
7c3b7eac-3b43-4461-b204-d74c2bde3a08,temperature,20.920000076293945,2019-10-11T11:50:51.187+02:00,2019-10-11T11:50:53.652+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:51.189+02:00,2019-10-11T11:51:01.71+02:00
acb55d68-d909-41ba-a445-8ef9eab0c7aa,temperature,20.950000762939453,2019-10-11T11:50:53.788+02:00,2019-10-11T11:50:53.789+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:53.79+02:00,null
6563ff1e-371b-46a2-aca8-cc49b418bf61,temperature,20.93000030517578,2019-10-11T11:50:54.929+02:00,2019-10-11T11:50:55.536+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:55.405+02:00,2019-10-11T11:51:01.71+02:00
157eda79-3283-4cda-a21d-db0aa2da8548,temperature,20.959999084472656,2019-10-11T11:50:55.674+02:00,2019-10-11T11:50:55.674+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:55.677+02:00,null
c7d8d6ce-59d9-4448-9934-69cebc071cf6,temperature,20.940000534057617,2019-10-11T11:50:56.817+02:00,2019-10-11T11:50:56.817+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:56.82+02:00,null
07d08037-7267-44ab-8d90-878271809a4c,temperature,20.93000030517578,2019-10-11T11:50:57.959+02:00,2019-10-11T11:50:59.1+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:57.962+02:00,2019-10-11T11:51:01.71+02:00
0b865113-d0f4-42f4-af3d-50e60edcc523,temperature,20.959999084472656,2019-10-11T11:50:59.238+02:00,2019-10-11T11:50:59.238+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:59.241+02:00,null
c40ad502-b6f1-49d8-8b6d-e7d5e475a6e2,temperature,20.979999542236328,2019-10-11T11:50:59.368+02:00,2019-10-11T11:50:59.368+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:59.371+02:00,null
8d98e723-7a07-4ccf-b3bc-7a2653952b8b,temperature,20.920000076293945,2019-10-11T11:51:00.509+02:00,2019-10-11T11:51:01.658+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:00.516+02:00,2019-10-11T11:51:01.71+02:00
0fc9c4bf-7e86-4cee-b99c-7642984b5fcd,humidity,50.8740234375,2019-10-11T11:51:01.969+02:00,2019-10-11T11:51:03.109+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:01.971+02:00,2019-10-11T11:51:20.729+02:00
7b4f0fc3-ecec-4226-b621-49dd01cf9eb7,humidity,50.884765625,2019-10-11T11:51:04.251+02:00,2019-10-11T11:51:04.252+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:04.254+02:00,null
72ccf0a7-e680-460c-a3b8-7d14f46b5b0c,humidity,50.861328125,2019-10-11T11:51:05.402+02:00,2019-10-11T11:51:05.403+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:05.405+02:00,null
6f5277d9-abe3-4e87-b0f1-e4320756bbac,humidity,50.8505859375,2019-10-11T11:51:06.551+02:00,2019-10-11T11:51:06.552+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:06.565+02:00,null
026370e8-2011-4bf0-8da9-ec4a9fe1ee19,humidity,50.7734375,2019-10-11T11:51:07.7+02:00,2019-10-11T11:51:07.7+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:07.702+02:00,null
e96eff46-8322-424d-9a79-51c746124354,humidity,50.728515625,2019-10-11T11:51:08.843+02:00,2019-10-11T11:51:08.843+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:08.846+02:00,null
6beb6aee-6888-4f4a-82ff-5bf700a22b0a,humidity,50.67578125,2019-10-11T11:51:09.986+02:00,2019-10-11T11:51:09.986+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:09.989+02:00,null
62a8f169-344d-4f9c-828a-7b4ea4261fad,humidity,50.6650390625,2019-10-11T11:51:11.129+02:00,2019-10-11T11:51:11.13+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:11.133+02:00,null
b1d64d40-59d7-4093-ba1d-208d869c9aae,humidity,50.6884765625,2019-10-11T11:51:11.264+02:00,2019-10-11T11:51:11.265+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:11.267+02:00,null
f98b69ee-bef9-4609-8bfa-82f11d7f51b6,humidity,50.76171875,2019-10-11T11:51:12.401+02:00,2019-10-11T11:51:12.402+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:12.41+02:00,null
eab34e23-2b43-40c2-9d6e-90e72fa13db5,humidity,50.7373046875,2019-10-11T11:51:13.551+02:00,2019-10-11T11:51:13.551+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:13.554+02:00,null
4659b946-8a40-4ba4-9888-6997bd1795f2,humidity,50.73828125,2019-10-11T11:51:14.695+02:00,2019-10-11T11:51:14.695+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:14.698+02:00,null
a6552aff-724c-4339-810a-36d911ddaa58,humidity,50.6845703125,2019-10-11T11:51:15.839+02:00,2019-10-11T11:51:15.839+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:15.842+02:00,null
c311cacd-e346-4b68-902b-400be0f8e02e,humidity,50.6767578125,2019-10-11T11:51:15.976+02:00,2019-10-11T11:51:15.977+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:15.98+02:00,null
2d4db614-17f8-4ddd-ba08-e2a5f77d74ea,humidity,50.68359375,2019-10-11T11:51:17.118+02:00,2019-10-11T11:51:17.118+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:17.121+02:00,null
936d61e4-494e-4959-a39a-c1dbc4a7d162,humidity,50.685546875,2019-10-11T11:51:17.243+02:00,2019-10-11T11:51:17.244+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:17.247+02:00,null
b913c9ca-4b5e-4c01-9aea-ee31d0d97977,humidity,50.6943359375,2019-10-11T11:51:18.381+02:00,2019-10-11T11:51:19.529+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:18.385+02:00,2019-10-11T11:51:20.729+02:00
d1a5e29c-d7e7-4ccc-9ff7-80ba1f797312,humidity,50.7080078125,2019-10-11T11:51:19.664+02:00,2019-10-11T11:51:19.664+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:19.667+02:00,null
140d6183-36c8-4903-a798-def4fcb3bd74,pressure,100150.5,2019-10-11T11:51:20.98+02:00,2019-10-11T11:51:20.98+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:20.982+02:00,null
98102a36-a38e-4ccc-9886-c119a7971414,pressure,100151.703125,2019-10-11T11:51:22.123+02:00,2019-10-11T11:51:22.123+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:22.125+02:00,null
5ffe51ae-687c-47df-8936-7785de9beed7,pressure,100150.8984375,2019-10-11T11:51:23.261+02:00,2019-10-11T11:51:23.262+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:23.263+02:00,null
e534cb23-c725-403b-b5c9-5455256a8662,pressure,100153.203125,2019-10-11T11:51:23.739+02:00,2019-10-11T11:51:23.739+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.594+02:00,null
e3c2a10b-373f-43d5-b02c-b381d1ad203b,pressure,100149.296875,2019-10-11T11:51:24.721+02:00,2019-10-11T11:51:24.721+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.723+02:00,null
e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94,pressure,100150.796875,2019-10-11T11:51:24.849+02:00,2019-10-11T11:51:24.849+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:25.212+02:00,null
70f6de4d-3a17-4688-b635-b8965e85be43,pressure,100149.6015625,2019-10-11T11:51:26.352+02:00,2019-10-11T11:51:26.352+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.354+02:00,null
0e42478b-da36-4096-afad-d8c2a7685ccd,pressure,100149.5,2019-10-11T11:51:26.488+02:00,2019-10-11T11:51:26.488+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.49+02:00,null
0313994c-db52-42d6-b059-2ca13afdd5d8,pressure,100151.3984375,2019-10-11T11:51:26.622+02:00,2019-10-11T11:51:26.622+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.624+02:00,null
9cb66b4c-31d8-40dc-8c5b-9d243c2bb974,pressure,100150.203125,2019-10-11T11:51:26.754+02:00,2019-10-11T11:51:26.754+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.757+02:00,null
6e626fdb-326d-4984-9585-7b64976be1a2,pressure,100150.5,2019-10-11T11:51:26.894+02:00,2019-10-11T11:51:26.894+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.896+02:00,null
6869ae37-0200-40c2-85a5-d4412d81b62d,pressure,100154.1015625,2019-10-11T11:51:27.029+02:00,2019-10-11T11:51:27.029+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:27.032+02:00,null
8a0770b9-f8b0-42de-90fe-3e6a6311818b,pressure,100152.3984375,2019-10-11T11:51:28.169+02:00,2019-10-11T11:51:28.169+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.171+02:00,null
cf989dbb-2e73-4042-b339-b4dbc75b0a7c,pressure,100151.3984375,2019-10-11T11:51:28.294+02:00,2019-10-11T11:51:28.294+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.296+02:00,null
53a2d8fb-3b15-48a2-a277-1b821adba088,pressure,100151.1015625,2019-10-11T11:51:28.426+02:00,2019-10-11T11:51:28.426+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.428+02:00,null
9de16913-67f3-4e0b-9976-ddadbd057bae,pressure,100153.8984375,2019-10-11T11:51:29.563+02:00,2019-10-11T11:51:29.563+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:29.565+02:00,null
d8c255a5-c5bd-4e9e-9aec-d0e6ae795719,pressure,100154.6015625,2019-10-11T11:51:30.707+02:00,2019-10-11T11:51:30.707+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:30.71+02:00,null
31ab31ac-e991-4081-8b4e-eba0f789b806,pressure,100153.796875,2019-10-11T11:51:31.843+02:00,2019-10-11T11:51:31.843+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:31.846+02:00,null
ed2ec0a9-9bc1-424a-9edf-85aad7a059ec,pressure,100150.703125,2019-10-11T11:51:32.981+02:00,2019-10-11T11:51:32.981+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:32.984+02:00,null
31e03c4c-49a1-4c0d-a750-16c09b75f96b,pressure,100151.203125,2019-10-11T11:51:34.125+02:00,2019-10-11T11:51:34.126+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:34.133+02:00,null
1 2029cc8c-56cf-4cd1-adf8-06e768fbad29 temperature 20.90999984741211 2019-10-11T11:50:43.317+02:00 2019-10-11T11:50:43.317+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:43.319+02:00 null
2 a588cc90-4dca-4a4e-be81-2d20b9f64458 temperature 20.920000076293945 2019-10-11T11:50:44.459+02:00 2019-10-11T11:50:47.745+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:44.473+02:00 2019-10-11T11:51:01.71+02:00
3 88f5c095-8e88-4c80-b66a-cf154919afa8 temperature 20.93000030517578 2019-10-11T11:50:49.53+02:00 2019-10-11T11:50:49.53+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:50.018+02:00 null
4 7c3b7eac-3b43-4461-b204-d74c2bde3a08 temperature 20.920000076293945 2019-10-11T11:50:51.187+02:00 2019-10-11T11:50:53.652+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:51.189+02:00 2019-10-11T11:51:01.71+02:00
5 acb55d68-d909-41ba-a445-8ef9eab0c7aa temperature 20.950000762939453 2019-10-11T11:50:53.788+02:00 2019-10-11T11:50:53.789+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:53.79+02:00 null
6 6563ff1e-371b-46a2-aca8-cc49b418bf61 temperature 20.93000030517578 2019-10-11T11:50:54.929+02:00 2019-10-11T11:50:55.536+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:55.405+02:00 2019-10-11T11:51:01.71+02:00
7 157eda79-3283-4cda-a21d-db0aa2da8548 temperature 20.959999084472656 2019-10-11T11:50:55.674+02:00 2019-10-11T11:50:55.674+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:55.677+02:00 null
8 c7d8d6ce-59d9-4448-9934-69cebc071cf6 temperature 20.940000534057617 2019-10-11T11:50:56.817+02:00 2019-10-11T11:50:56.817+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:56.82+02:00 null
9 07d08037-7267-44ab-8d90-878271809a4c temperature 20.93000030517578 2019-10-11T11:50:57.959+02:00 2019-10-11T11:50:59.1+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:57.962+02:00 2019-10-11T11:51:01.71+02:00
10 0b865113-d0f4-42f4-af3d-50e60edcc523 temperature 20.959999084472656 2019-10-11T11:50:59.238+02:00 2019-10-11T11:50:59.238+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:59.241+02:00 null
11 c40ad502-b6f1-49d8-8b6d-e7d5e475a6e2 temperature 20.979999542236328 2019-10-11T11:50:59.368+02:00 2019-10-11T11:50:59.368+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:59.371+02:00 null
12 8d98e723-7a07-4ccf-b3bc-7a2653952b8b temperature 20.920000076293945 2019-10-11T11:51:00.509+02:00 2019-10-11T11:51:01.658+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:00.516+02:00 2019-10-11T11:51:01.71+02:00
13 0fc9c4bf-7e86-4cee-b99c-7642984b5fcd humidity 50.8740234375 2019-10-11T11:51:01.969+02:00 2019-10-11T11:51:03.109+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:01.971+02:00 2019-10-11T11:51:20.729+02:00
14 7b4f0fc3-ecec-4226-b621-49dd01cf9eb7 humidity 50.884765625 2019-10-11T11:51:04.251+02:00 2019-10-11T11:51:04.252+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:04.254+02:00 null
15 72ccf0a7-e680-460c-a3b8-7d14f46b5b0c humidity 50.861328125 2019-10-11T11:51:05.402+02:00 2019-10-11T11:51:05.403+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:05.405+02:00 null
16 6f5277d9-abe3-4e87-b0f1-e4320756bbac humidity 50.8505859375 2019-10-11T11:51:06.551+02:00 2019-10-11T11:51:06.552+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:06.565+02:00 null
17 026370e8-2011-4bf0-8da9-ec4a9fe1ee19 humidity 50.7734375 2019-10-11T11:51:07.7+02:00 2019-10-11T11:51:07.7+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:07.702+02:00 null
18 e96eff46-8322-424d-9a79-51c746124354 humidity 50.728515625 2019-10-11T11:51:08.843+02:00 2019-10-11T11:51:08.843+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:08.846+02:00 null
19 6beb6aee-6888-4f4a-82ff-5bf700a22b0a humidity 50.67578125 2019-10-11T11:51:09.986+02:00 2019-10-11T11:51:09.986+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:09.989+02:00 null
20 62a8f169-344d-4f9c-828a-7b4ea4261fad humidity 50.6650390625 2019-10-11T11:51:11.129+02:00 2019-10-11T11:51:11.13+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:11.133+02:00 null
21 b1d64d40-59d7-4093-ba1d-208d869c9aae humidity 50.6884765625 2019-10-11T11:51:11.264+02:00 2019-10-11T11:51:11.265+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:11.267+02:00 null
22 f98b69ee-bef9-4609-8bfa-82f11d7f51b6 humidity 50.76171875 2019-10-11T11:51:12.401+02:00 2019-10-11T11:51:12.402+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:12.41+02:00 null
23 eab34e23-2b43-40c2-9d6e-90e72fa13db5 humidity 50.7373046875 2019-10-11T11:51:13.551+02:00 2019-10-11T11:51:13.551+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:13.554+02:00 null
24 4659b946-8a40-4ba4-9888-6997bd1795f2 humidity 50.73828125 2019-10-11T11:51:14.695+02:00 2019-10-11T11:51:14.695+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:14.698+02:00 null
25 a6552aff-724c-4339-810a-36d911ddaa58 humidity 50.6845703125 2019-10-11T11:51:15.839+02:00 2019-10-11T11:51:15.839+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:15.842+02:00 null
26 c311cacd-e346-4b68-902b-400be0f8e02e humidity 50.6767578125 2019-10-11T11:51:15.976+02:00 2019-10-11T11:51:15.977+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:15.98+02:00 null
27 2d4db614-17f8-4ddd-ba08-e2a5f77d74ea humidity 50.68359375 2019-10-11T11:51:17.118+02:00 2019-10-11T11:51:17.118+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:17.121+02:00 null
28 936d61e4-494e-4959-a39a-c1dbc4a7d162 humidity 50.685546875 2019-10-11T11:51:17.243+02:00 2019-10-11T11:51:17.244+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:17.247+02:00 null
29 b913c9ca-4b5e-4c01-9aea-ee31d0d97977 humidity 50.6943359375 2019-10-11T11:51:18.381+02:00 2019-10-11T11:51:19.529+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:18.385+02:00 2019-10-11T11:51:20.729+02:00
30 d1a5e29c-d7e7-4ccc-9ff7-80ba1f797312 humidity 50.7080078125 2019-10-11T11:51:19.664+02:00 2019-10-11T11:51:19.664+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:19.667+02:00 null
31 140d6183-36c8-4903-a798-def4fcb3bd74 pressure 100150.5 2019-10-11T11:51:20.98+02:00 2019-10-11T11:51:20.98+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:20.982+02:00 null
32 98102a36-a38e-4ccc-9886-c119a7971414 pressure 100151.703125 2019-10-11T11:51:22.123+02:00 2019-10-11T11:51:22.123+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:22.125+02:00 null
33 5ffe51ae-687c-47df-8936-7785de9beed7 pressure 100150.8984375 2019-10-11T11:51:23.261+02:00 2019-10-11T11:51:23.262+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:23.263+02:00 null
34 e534cb23-c725-403b-b5c9-5455256a8662 pressure 100153.203125 2019-10-11T11:51:23.739+02:00 2019-10-11T11:51:23.739+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.594+02:00 null
35 e3c2a10b-373f-43d5-b02c-b381d1ad203b pressure 100149.296875 2019-10-11T11:51:24.721+02:00 2019-10-11T11:51:24.721+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.723+02:00 null
36 e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94 pressure 100150.796875 2019-10-11T11:51:24.849+02:00 2019-10-11T11:51:24.849+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:25.212+02:00 null
37 70f6de4d-3a17-4688-b635-b8965e85be43 pressure 100149.6015625 2019-10-11T11:51:26.352+02:00 2019-10-11T11:51:26.352+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.354+02:00 null
38 0e42478b-da36-4096-afad-d8c2a7685ccd pressure 100149.5 2019-10-11T11:51:26.488+02:00 2019-10-11T11:51:26.488+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.49+02:00 null
39 0313994c-db52-42d6-b059-2ca13afdd5d8 pressure 100151.3984375 2019-10-11T11:51:26.622+02:00 2019-10-11T11:51:26.622+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.624+02:00 null
40 9cb66b4c-31d8-40dc-8c5b-9d243c2bb974 pressure 100150.203125 2019-10-11T11:51:26.754+02:00 2019-10-11T11:51:26.754+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.757+02:00 null
41 6e626fdb-326d-4984-9585-7b64976be1a2 pressure 100150.5 2019-10-11T11:51:26.894+02:00 2019-10-11T11:51:26.894+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.896+02:00 null
42 6869ae37-0200-40c2-85a5-d4412d81b62d pressure 100154.1015625 2019-10-11T11:51:27.029+02:00 2019-10-11T11:51:27.029+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:27.032+02:00 null
43 8a0770b9-f8b0-42de-90fe-3e6a6311818b pressure 100152.3984375 2019-10-11T11:51:28.169+02:00 2019-10-11T11:51:28.169+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.171+02:00 null
44 cf989dbb-2e73-4042-b339-b4dbc75b0a7c pressure 100151.3984375 2019-10-11T11:51:28.294+02:00 2019-10-11T11:51:28.294+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.296+02:00 null
45 53a2d8fb-3b15-48a2-a277-1b821adba088 pressure 100151.1015625 2019-10-11T11:51:28.426+02:00 2019-10-11T11:51:28.426+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.428+02:00 null
46 9de16913-67f3-4e0b-9976-ddadbd057bae pressure 100153.8984375 2019-10-11T11:51:29.563+02:00 2019-10-11T11:51:29.563+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:29.565+02:00 null
47 d8c255a5-c5bd-4e9e-9aec-d0e6ae795719 pressure 100154.6015625 2019-10-11T11:51:30.707+02:00 2019-10-11T11:51:30.707+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:30.71+02:00 null
48 31ab31ac-e991-4081-8b4e-eba0f789b806 pressure 100153.796875 2019-10-11T11:51:31.843+02:00 2019-10-11T11:51:31.843+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:31.846+02:00 null
49 ed2ec0a9-9bc1-424a-9edf-85aad7a059ec pressure 100150.703125 2019-10-11T11:51:32.981+02:00 2019-10-11T11:51:32.981+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:32.984+02:00 null
50 31e03c4c-49a1-4c0d-a750-16c09b75f96b pressure 100151.203125 2019-10-11T11:51:34.125+02:00 2019-10-11T11:51:34.126+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:34.133+02:00 null

View File

@ -1,24 +0,0 @@
2029cc8c-56cf-4cd1-adf8-06e768fbad29,temperature,21,2019-10-11T11:50:43.317+02:00,2019-10-11T11:51:01.658+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:43.319+02:00,2019-10-11T11:51:01.735+02:00
0fc9c4bf-7e86-4cee-b99c-7642984b5fcd,humidity,51,2019-10-11T11:51:01.969+02:00,2019-10-11T11:51:07.7+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:01.971+02:00,2019-10-11T11:51:20.754+02:00
e96eff46-8322-424d-9a79-51c746124354,humidity,50.5,2019-10-11T11:51:08.843+02:00,2019-10-11T11:51:11.265+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:08.846+02:00,2019-10-11T11:51:20.754+02:00
f98b69ee-bef9-4609-8bfa-82f11d7f51b6,humidity,51,2019-10-11T11:51:12.401+02:00,2019-10-11T11:51:12.402+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:12.41+02:00,null
eab34e23-2b43-40c2-9d6e-90e72fa13db5,humidity,50.5,2019-10-11T11:51:13.551+02:00,2019-10-11T11:51:19.664+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:13.554+02:00,2019-10-11T11:51:20.754+02:00
140d6183-36c8-4903-a798-def4fcb3bd74,pressure,100150.5,2019-10-11T11:51:20.98+02:00,2019-10-11T11:51:20.98+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:20.982+02:00,null
98102a36-a38e-4ccc-9886-c119a7971414,pressure,100151.5,2019-10-11T11:51:22.123+02:00,2019-10-11T11:51:22.123+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:22.125+02:00,null
5ffe51ae-687c-47df-8936-7785de9beed7,pressure,100151,2019-10-11T11:51:23.261+02:00,2019-10-11T11:51:23.262+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:23.263+02:00,null
e534cb23-c725-403b-b5c9-5455256a8662,pressure,100153,2019-10-11T11:51:23.739+02:00,2019-10-11T11:51:23.739+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.594+02:00,null
e3c2a10b-373f-43d5-b02c-b381d1ad203b,pressure,100149.5,2019-10-11T11:51:24.721+02:00,2019-10-11T11:51:24.721+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.723+02:00,null
e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94,pressure,100151,2019-10-11T11:51:24.849+02:00,2019-10-11T11:51:24.849+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:25.212+02:00,null
70f6de4d-3a17-4688-b635-b8965e85be43,pressure,100149.5,2019-10-11T11:51:26.352+02:00,2019-10-11T11:51:26.488+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.354+02:00,2019-10-11T11:51:35.22+02:00
0313994c-db52-42d6-b059-2ca13afdd5d8,pressure,100151.5,2019-10-11T11:51:26.622+02:00,2019-10-11T11:51:26.622+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.624+02:00,null
9cb66b4c-31d8-40dc-8c5b-9d243c2bb974,pressure,100150,2019-10-11T11:51:26.754+02:00,2019-10-11T11:51:26.754+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.757+02:00,null
6e626fdb-326d-4984-9585-7b64976be1a2,pressure,100150.5,2019-10-11T11:51:26.894+02:00,2019-10-11T11:51:26.894+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.896+02:00,null
6869ae37-0200-40c2-85a5-d4412d81b62d,pressure,100154,2019-10-11T11:51:27.029+02:00,2019-10-11T11:51:27.029+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:27.032+02:00,null
8a0770b9-f8b0-42de-90fe-3e6a6311818b,pressure,100152.5,2019-10-11T11:51:28.169+02:00,2019-10-11T11:51:28.169+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.171+02:00,null
cf989dbb-2e73-4042-b339-b4dbc75b0a7c,pressure,100151.5,2019-10-11T11:51:28.294+02:00,2019-10-11T11:51:28.294+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.296+02:00,null
53a2d8fb-3b15-48a2-a277-1b821adba088,pressure,100151,2019-10-11T11:51:28.426+02:00,2019-10-11T11:51:28.426+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.428+02:00,null
9de16913-67f3-4e0b-9976-ddadbd057bae,pressure,100154,2019-10-11T11:51:29.563+02:00,2019-10-11T11:51:29.563+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:29.565+02:00,null
d8c255a5-c5bd-4e9e-9aec-d0e6ae795719,pressure,100154.5,2019-10-11T11:51:30.707+02:00,2019-10-11T11:51:30.707+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:30.71+02:00,null
31ab31ac-e991-4081-8b4e-eba0f789b806,pressure,100154,2019-10-11T11:51:31.843+02:00,2019-10-11T11:51:31.843+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:31.846+02:00,null
ed2ec0a9-9bc1-424a-9edf-85aad7a059ec,pressure,100150.5,2019-10-11T11:51:32.981+02:00,2019-10-11T11:51:32.981+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:32.984+02:00,null
31e03c4c-49a1-4c0d-a750-16c09b75f96b,pressure,100151,2019-10-11T11:51:34.125+02:00,2019-10-11T11:51:34.126+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:34.133+02:00,null
1 2029cc8c-56cf-4cd1-adf8-06e768fbad29 temperature 21 2019-10-11T11:50:43.317+02:00 2019-10-11T11:51:01.658+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:43.319+02:00 2019-10-11T11:51:01.735+02:00
2 0fc9c4bf-7e86-4cee-b99c-7642984b5fcd humidity 51 2019-10-11T11:51:01.969+02:00 2019-10-11T11:51:07.7+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:01.971+02:00 2019-10-11T11:51:20.754+02:00
3 e96eff46-8322-424d-9a79-51c746124354 humidity 50.5 2019-10-11T11:51:08.843+02:00 2019-10-11T11:51:11.265+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:08.846+02:00 2019-10-11T11:51:20.754+02:00
4 f98b69ee-bef9-4609-8bfa-82f11d7f51b6 humidity 51 2019-10-11T11:51:12.401+02:00 2019-10-11T11:51:12.402+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:12.41+02:00 null
5 eab34e23-2b43-40c2-9d6e-90e72fa13db5 humidity 50.5 2019-10-11T11:51:13.551+02:00 2019-10-11T11:51:19.664+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:13.554+02:00 2019-10-11T11:51:20.754+02:00
6 140d6183-36c8-4903-a798-def4fcb3bd74 pressure 100150.5 2019-10-11T11:51:20.98+02:00 2019-10-11T11:51:20.98+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:20.982+02:00 null
7 98102a36-a38e-4ccc-9886-c119a7971414 pressure 100151.5 2019-10-11T11:51:22.123+02:00 2019-10-11T11:51:22.123+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:22.125+02:00 null
8 5ffe51ae-687c-47df-8936-7785de9beed7 pressure 100151 2019-10-11T11:51:23.261+02:00 2019-10-11T11:51:23.262+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:23.263+02:00 null
9 e534cb23-c725-403b-b5c9-5455256a8662 pressure 100153 2019-10-11T11:51:23.739+02:00 2019-10-11T11:51:23.739+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.594+02:00 null
10 e3c2a10b-373f-43d5-b02c-b381d1ad203b pressure 100149.5 2019-10-11T11:51:24.721+02:00 2019-10-11T11:51:24.721+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.723+02:00 null
11 e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94 pressure 100151 2019-10-11T11:51:24.849+02:00 2019-10-11T11:51:24.849+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:25.212+02:00 null
12 70f6de4d-3a17-4688-b635-b8965e85be43 pressure 100149.5 2019-10-11T11:51:26.352+02:00 2019-10-11T11:51:26.488+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.354+02:00 2019-10-11T11:51:35.22+02:00
13 0313994c-db52-42d6-b059-2ca13afdd5d8 pressure 100151.5 2019-10-11T11:51:26.622+02:00 2019-10-11T11:51:26.622+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.624+02:00 null
14 9cb66b4c-31d8-40dc-8c5b-9d243c2bb974 pressure 100150 2019-10-11T11:51:26.754+02:00 2019-10-11T11:51:26.754+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.757+02:00 null
15 6e626fdb-326d-4984-9585-7b64976be1a2 pressure 100150.5 2019-10-11T11:51:26.894+02:00 2019-10-11T11:51:26.894+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.896+02:00 null
16 6869ae37-0200-40c2-85a5-d4412d81b62d pressure 100154 2019-10-11T11:51:27.029+02:00 2019-10-11T11:51:27.029+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:27.032+02:00 null
17 8a0770b9-f8b0-42de-90fe-3e6a6311818b pressure 100152.5 2019-10-11T11:51:28.169+02:00 2019-10-11T11:51:28.169+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.171+02:00 null
18 cf989dbb-2e73-4042-b339-b4dbc75b0a7c pressure 100151.5 2019-10-11T11:51:28.294+02:00 2019-10-11T11:51:28.294+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.296+02:00 null
19 53a2d8fb-3b15-48a2-a277-1b821adba088 pressure 100151 2019-10-11T11:51:28.426+02:00 2019-10-11T11:51:28.426+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.428+02:00 null
20 9de16913-67f3-4e0b-9976-ddadbd057bae pressure 100154 2019-10-11T11:51:29.563+02:00 2019-10-11T11:51:29.563+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:29.565+02:00 null
21 d8c255a5-c5bd-4e9e-9aec-d0e6ae795719 pressure 100154.5 2019-10-11T11:51:30.707+02:00 2019-10-11T11:51:30.707+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:30.71+02:00 null
22 31ab31ac-e991-4081-8b4e-eba0f789b806 pressure 100154 2019-10-11T11:51:31.843+02:00 2019-10-11T11:51:31.843+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:31.846+02:00 null
23 ed2ec0a9-9bc1-424a-9edf-85aad7a059ec pressure 100150.5 2019-10-11T11:51:32.981+02:00 2019-10-11T11:51:32.981+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:32.984+02:00 null
24 31e03c4c-49a1-4c0d-a750-16c09b75f96b pressure 100151 2019-10-11T11:51:34.125+02:00 2019-10-11T11:51:34.126+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:34.133+02:00 null

View File

@ -1,60 +0,0 @@
2029cc8c-56cf-4cd1-adf8-06e768fbad29,temperature,20.90999984741211,2019-10-11T11:50:43.317+02:00,2019-10-11T11:50:43.317+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:43.319+02:00,null
a588cc90-4dca-4a4e-be81-2d20b9f64458,temperature,20.920000076293945,2019-10-11T11:50:44.459+02:00,2019-10-11T11:50:44.459+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:44.473+02:00,null
1ebb60de-6066-4731-9f55-ee1c51748b7f,temperature,20.920000076293945,2019-10-11T11:50:45.614+02:00,2019-10-11T11:50:45.614+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:45.615+02:00,null
6cbc586f-a5c9-4296-b3b2-200afae12059,temperature,20.920000076293945,2019-10-11T11:50:46.776+02:00,2019-10-11T11:50:46.776+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:47.134+02:00,null
14e03e32-f214-4f61-97b0-8e021c47bead,temperature,20.920000076293945,2019-10-11T11:50:47.745+02:00,2019-10-11T11:50:47.745+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:47.747+02:00,null
88f5c095-8e88-4c80-b66a-cf154919afa8,temperature,20.93000030517578,2019-10-11T11:50:49.53+02:00,2019-10-11T11:50:49.53+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:50.018+02:00,null
7c3b7eac-3b43-4461-b204-d74c2bde3a08,temperature,20.920000076293945,2019-10-11T11:50:51.187+02:00,2019-10-11T11:50:51.187+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:51.189+02:00,null
3caf0b08-38d5-4536-baa5-86d4d75e359d,temperature,20.920000076293945,2019-10-11T11:50:52.508+02:00,2019-10-11T11:50:52.508+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:52.51+02:00,null
4b8595bf-3624-4963-b796-efc648515cd9,temperature,20.920000076293945,2019-10-11T11:50:53.652+02:00,2019-10-11T11:50:53.652+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:53.654+02:00,null
acb55d68-d909-41ba-a445-8ef9eab0c7aa,temperature,20.950000762939453,2019-10-11T11:50:53.788+02:00,2019-10-11T11:50:53.789+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:53.79+02:00,null
6563ff1e-371b-46a2-aca8-cc49b418bf61,temperature,20.93000030517578,2019-10-11T11:50:54.929+02:00,2019-10-11T11:50:54.93+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:55.405+02:00,null
a64f57c4-b95c-45b1-b78a-a6be263abb85,temperature,20.93000030517578,2019-10-11T11:50:55.536+02:00,2019-10-11T11:50:55.536+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:55.538+02:00,null
157eda79-3283-4cda-a21d-db0aa2da8548,temperature,20.959999084472656,2019-10-11T11:50:55.674+02:00,2019-10-11T11:50:55.674+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:55.677+02:00,null
c7d8d6ce-59d9-4448-9934-69cebc071cf6,temperature,20.940000534057617,2019-10-11T11:50:56.817+02:00,2019-10-11T11:50:56.817+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:56.82+02:00,null
07d08037-7267-44ab-8d90-878271809a4c,temperature,20.93000030517578,2019-10-11T11:50:57.959+02:00,2019-10-11T11:50:57.96+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:57.962+02:00,null
bf2e981f-8bad-466e-9a12-c1a5d9b64399,temperature,20.93000030517578,2019-10-11T11:50:59.1+02:00,2019-10-11T11:50:59.1+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:59.103+02:00,null
0b865113-d0f4-42f4-af3d-50e60edcc523,temperature,20.959999084472656,2019-10-11T11:50:59.238+02:00,2019-10-11T11:50:59.238+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:59.241+02:00,null
c40ad502-b6f1-49d8-8b6d-e7d5e475a6e2,temperature,20.979999542236328,2019-10-11T11:50:59.368+02:00,2019-10-11T11:50:59.368+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:59.371+02:00,null
8d98e723-7a07-4ccf-b3bc-7a2653952b8b,temperature,20.920000076293945,2019-10-11T11:51:00.509+02:00,2019-10-11T11:51:00.51+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:00.516+02:00,null
f61b7bfb-14eb-4b43-a7b6-47b61fa46dda,temperature,20.920000076293945,2019-10-11T11:51:01.658+02:00,2019-10-11T11:51:01.658+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:01.661+02:00,null
0fc9c4bf-7e86-4cee-b99c-7642984b5fcd,humidity,50.8740234375,2019-10-11T11:51:01.969+02:00,2019-10-11T11:51:01.969+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:01.971+02:00,null
45e8b20c-1ab0-4cf1-9ef1-aa82537557f6,humidity,50.8740234375,2019-10-11T11:51:03.109+02:00,2019-10-11T11:51:03.109+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:03.111+02:00,null
7b4f0fc3-ecec-4226-b621-49dd01cf9eb7,humidity,50.884765625,2019-10-11T11:51:04.251+02:00,2019-10-11T11:51:04.252+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:04.254+02:00,null
72ccf0a7-e680-460c-a3b8-7d14f46b5b0c,humidity,50.861328125,2019-10-11T11:51:05.402+02:00,2019-10-11T11:51:05.403+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:05.405+02:00,null
6f5277d9-abe3-4e87-b0f1-e4320756bbac,humidity,50.8505859375,2019-10-11T11:51:06.551+02:00,2019-10-11T11:51:06.552+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:06.565+02:00,null
026370e8-2011-4bf0-8da9-ec4a9fe1ee19,humidity,50.7734375,2019-10-11T11:51:07.7+02:00,2019-10-11T11:51:07.7+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:07.702+02:00,null
e96eff46-8322-424d-9a79-51c746124354,humidity,50.728515625,2019-10-11T11:51:08.843+02:00,2019-10-11T11:51:08.843+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:08.846+02:00,null
6beb6aee-6888-4f4a-82ff-5bf700a22b0a,humidity,50.67578125,2019-10-11T11:51:09.986+02:00,2019-10-11T11:51:09.986+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:09.989+02:00,null
62a8f169-344d-4f9c-828a-7b4ea4261fad,humidity,50.6650390625,2019-10-11T11:51:11.129+02:00,2019-10-11T11:51:11.13+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:11.133+02:00,null
b1d64d40-59d7-4093-ba1d-208d869c9aae,humidity,50.6884765625,2019-10-11T11:51:11.264+02:00,2019-10-11T11:51:11.265+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:11.267+02:00,null
f98b69ee-bef9-4609-8bfa-82f11d7f51b6,humidity,50.76171875,2019-10-11T11:51:12.401+02:00,2019-10-11T11:51:12.402+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:12.41+02:00,null
eab34e23-2b43-40c2-9d6e-90e72fa13db5,humidity,50.7373046875,2019-10-11T11:51:13.551+02:00,2019-10-11T11:51:13.551+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:13.554+02:00,null
4659b946-8a40-4ba4-9888-6997bd1795f2,humidity,50.73828125,2019-10-11T11:51:14.695+02:00,2019-10-11T11:51:14.695+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:14.698+02:00,null
a6552aff-724c-4339-810a-36d911ddaa58,humidity,50.6845703125,2019-10-11T11:51:15.839+02:00,2019-10-11T11:51:15.839+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:15.842+02:00,null
c311cacd-e346-4b68-902b-400be0f8e02e,humidity,50.6767578125,2019-10-11T11:51:15.976+02:00,2019-10-11T11:51:15.977+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:15.98+02:00,null
2d4db614-17f8-4ddd-ba08-e2a5f77d74ea,humidity,50.68359375,2019-10-11T11:51:17.118+02:00,2019-10-11T11:51:17.118+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:17.121+02:00,null
936d61e4-494e-4959-a39a-c1dbc4a7d162,humidity,50.685546875,2019-10-11T11:51:17.243+02:00,2019-10-11T11:51:17.244+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:17.247+02:00,null
b913c9ca-4b5e-4c01-9aea-ee31d0d97977,humidity,50.6943359375,2019-10-11T11:51:18.381+02:00,2019-10-11T11:51:18.382+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:18.385+02:00,null
1171d0dd-2173-4da2-b2c6-7f7daaad7cd1,humidity,50.6943359375,2019-10-11T11:51:19.529+02:00,2019-10-11T11:51:19.529+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:19.532+02:00,null
d1a5e29c-d7e7-4ccc-9ff7-80ba1f797312,humidity,50.7080078125,2019-10-11T11:51:19.664+02:00,2019-10-11T11:51:19.664+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:19.667+02:00,null
140d6183-36c8-4903-a798-def4fcb3bd74,pressure,100150.5,2019-10-11T11:51:20.98+02:00,2019-10-11T11:51:20.98+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:20.982+02:00,null
98102a36-a38e-4ccc-9886-c119a7971414,pressure,100151.703125,2019-10-11T11:51:22.123+02:00,2019-10-11T11:51:22.123+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:22.125+02:00,null
5ffe51ae-687c-47df-8936-7785de9beed7,pressure,100150.8984375,2019-10-11T11:51:23.261+02:00,2019-10-11T11:51:23.262+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:23.263+02:00,null
e534cb23-c725-403b-b5c9-5455256a8662,pressure,100153.203125,2019-10-11T11:51:23.739+02:00,2019-10-11T11:51:23.739+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.594+02:00,null
e3c2a10b-373f-43d5-b02c-b381d1ad203b,pressure,100149.296875,2019-10-11T11:51:24.721+02:00,2019-10-11T11:51:24.721+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.723+02:00,null
e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94,pressure,100150.796875,2019-10-11T11:51:24.849+02:00,2019-10-11T11:51:24.849+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:25.212+02:00,null
70f6de4d-3a17-4688-b635-b8965e85be43,pressure,100149.6015625,2019-10-11T11:51:26.352+02:00,2019-10-11T11:51:26.352+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.354+02:00,null
0e42478b-da36-4096-afad-d8c2a7685ccd,pressure,100149.5,2019-10-11T11:51:26.488+02:00,2019-10-11T11:51:26.488+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.49+02:00,null
0313994c-db52-42d6-b059-2ca13afdd5d8,pressure,100151.3984375,2019-10-11T11:51:26.622+02:00,2019-10-11T11:51:26.622+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.624+02:00,null
9cb66b4c-31d8-40dc-8c5b-9d243c2bb974,pressure,100150.203125,2019-10-11T11:51:26.754+02:00,2019-10-11T11:51:26.754+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.757+02:00,null
6e626fdb-326d-4984-9585-7b64976be1a2,pressure,100150.5,2019-10-11T11:51:26.894+02:00,2019-10-11T11:51:26.894+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.896+02:00,null
6869ae37-0200-40c2-85a5-d4412d81b62d,pressure,100154.1015625,2019-10-11T11:51:27.029+02:00,2019-10-11T11:51:27.029+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:27.032+02:00,null
8a0770b9-f8b0-42de-90fe-3e6a6311818b,pressure,100152.3984375,2019-10-11T11:51:28.169+02:00,2019-10-11T11:51:28.169+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.171+02:00,null
cf989dbb-2e73-4042-b339-b4dbc75b0a7c,pressure,100151.3984375,2019-10-11T11:51:28.294+02:00,2019-10-11T11:51:28.294+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.296+02:00,null
53a2d8fb-3b15-48a2-a277-1b821adba088,pressure,100151.1015625,2019-10-11T11:51:28.426+02:00,2019-10-11T11:51:28.426+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.428+02:00,null
9de16913-67f3-4e0b-9976-ddadbd057bae,pressure,100153.8984375,2019-10-11T11:51:29.563+02:00,2019-10-11T11:51:29.563+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:29.565+02:00,null
d8c255a5-c5bd-4e9e-9aec-d0e6ae795719,pressure,100154.6015625,2019-10-11T11:51:30.707+02:00,2019-10-11T11:51:30.707+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:30.71+02:00,null
31ab31ac-e991-4081-8b4e-eba0f789b806,pressure,100153.796875,2019-10-11T11:51:31.843+02:00,2019-10-11T11:51:31.843+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:31.846+02:00,null
ed2ec0a9-9bc1-424a-9edf-85aad7a059ec,pressure,100150.703125,2019-10-11T11:51:32.981+02:00,2019-10-11T11:51:32.981+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:32.984+02:00,null
31e03c4c-49a1-4c0d-a750-16c09b75f96b,pressure,100151.203125,2019-10-11T11:51:34.125+02:00,2019-10-11T11:51:34.126+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:34.133+02:00,null
1 2029cc8c-56cf-4cd1-adf8-06e768fbad29 temperature 20.90999984741211 2019-10-11T11:50:43.317+02:00 2019-10-11T11:50:43.317+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:43.319+02:00 null
2 a588cc90-4dca-4a4e-be81-2d20b9f64458 temperature 20.920000076293945 2019-10-11T11:50:44.459+02:00 2019-10-11T11:50:44.459+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:44.473+02:00 null
3 1ebb60de-6066-4731-9f55-ee1c51748b7f temperature 20.920000076293945 2019-10-11T11:50:45.614+02:00 2019-10-11T11:50:45.614+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:45.615+02:00 null
4 6cbc586f-a5c9-4296-b3b2-200afae12059 temperature 20.920000076293945 2019-10-11T11:50:46.776+02:00 2019-10-11T11:50:46.776+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:47.134+02:00 null
5 14e03e32-f214-4f61-97b0-8e021c47bead temperature 20.920000076293945 2019-10-11T11:50:47.745+02:00 2019-10-11T11:50:47.745+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:47.747+02:00 null
6 88f5c095-8e88-4c80-b66a-cf154919afa8 temperature 20.93000030517578 2019-10-11T11:50:49.53+02:00 2019-10-11T11:50:49.53+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:50.018+02:00 null
7 7c3b7eac-3b43-4461-b204-d74c2bde3a08 temperature 20.920000076293945 2019-10-11T11:50:51.187+02:00 2019-10-11T11:50:51.187+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:51.189+02:00 null
8 3caf0b08-38d5-4536-baa5-86d4d75e359d temperature 20.920000076293945 2019-10-11T11:50:52.508+02:00 2019-10-11T11:50:52.508+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:52.51+02:00 null
9 4b8595bf-3624-4963-b796-efc648515cd9 temperature 20.920000076293945 2019-10-11T11:50:53.652+02:00 2019-10-11T11:50:53.652+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:53.654+02:00 null
10 acb55d68-d909-41ba-a445-8ef9eab0c7aa temperature 20.950000762939453 2019-10-11T11:50:53.788+02:00 2019-10-11T11:50:53.789+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:53.79+02:00 null
11 6563ff1e-371b-46a2-aca8-cc49b418bf61 temperature 20.93000030517578 2019-10-11T11:50:54.929+02:00 2019-10-11T11:50:54.93+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:55.405+02:00 null
12 a64f57c4-b95c-45b1-b78a-a6be263abb85 temperature 20.93000030517578 2019-10-11T11:50:55.536+02:00 2019-10-11T11:50:55.536+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:55.538+02:00 null
13 157eda79-3283-4cda-a21d-db0aa2da8548 temperature 20.959999084472656 2019-10-11T11:50:55.674+02:00 2019-10-11T11:50:55.674+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:55.677+02:00 null
14 c7d8d6ce-59d9-4448-9934-69cebc071cf6 temperature 20.940000534057617 2019-10-11T11:50:56.817+02:00 2019-10-11T11:50:56.817+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:56.82+02:00 null
15 07d08037-7267-44ab-8d90-878271809a4c temperature 20.93000030517578 2019-10-11T11:50:57.959+02:00 2019-10-11T11:50:57.96+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:57.962+02:00 null
16 bf2e981f-8bad-466e-9a12-c1a5d9b64399 temperature 20.93000030517578 2019-10-11T11:50:59.1+02:00 2019-10-11T11:50:59.1+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:59.103+02:00 null
17 0b865113-d0f4-42f4-af3d-50e60edcc523 temperature 20.959999084472656 2019-10-11T11:50:59.238+02:00 2019-10-11T11:50:59.238+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:59.241+02:00 null
18 c40ad502-b6f1-49d8-8b6d-e7d5e475a6e2 temperature 20.979999542236328 2019-10-11T11:50:59.368+02:00 2019-10-11T11:50:59.368+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:59.371+02:00 null
19 8d98e723-7a07-4ccf-b3bc-7a2653952b8b temperature 20.920000076293945 2019-10-11T11:51:00.509+02:00 2019-10-11T11:51:00.51+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:00.516+02:00 null
20 f61b7bfb-14eb-4b43-a7b6-47b61fa46dda temperature 20.920000076293945 2019-10-11T11:51:01.658+02:00 2019-10-11T11:51:01.658+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:01.661+02:00 null
21 0fc9c4bf-7e86-4cee-b99c-7642984b5fcd humidity 50.8740234375 2019-10-11T11:51:01.969+02:00 2019-10-11T11:51:01.969+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:01.971+02:00 null
22 45e8b20c-1ab0-4cf1-9ef1-aa82537557f6 humidity 50.8740234375 2019-10-11T11:51:03.109+02:00 2019-10-11T11:51:03.109+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:03.111+02:00 null
23 7b4f0fc3-ecec-4226-b621-49dd01cf9eb7 humidity 50.884765625 2019-10-11T11:51:04.251+02:00 2019-10-11T11:51:04.252+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:04.254+02:00 null
24 72ccf0a7-e680-460c-a3b8-7d14f46b5b0c humidity 50.861328125 2019-10-11T11:51:05.402+02:00 2019-10-11T11:51:05.403+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:05.405+02:00 null
25 6f5277d9-abe3-4e87-b0f1-e4320756bbac humidity 50.8505859375 2019-10-11T11:51:06.551+02:00 2019-10-11T11:51:06.552+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:06.565+02:00 null
26 026370e8-2011-4bf0-8da9-ec4a9fe1ee19 humidity 50.7734375 2019-10-11T11:51:07.7+02:00 2019-10-11T11:51:07.7+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:07.702+02:00 null
27 e96eff46-8322-424d-9a79-51c746124354 humidity 50.728515625 2019-10-11T11:51:08.843+02:00 2019-10-11T11:51:08.843+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:08.846+02:00 null
28 6beb6aee-6888-4f4a-82ff-5bf700a22b0a humidity 50.67578125 2019-10-11T11:51:09.986+02:00 2019-10-11T11:51:09.986+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:09.989+02:00 null
29 62a8f169-344d-4f9c-828a-7b4ea4261fad humidity 50.6650390625 2019-10-11T11:51:11.129+02:00 2019-10-11T11:51:11.13+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:11.133+02:00 null
30 b1d64d40-59d7-4093-ba1d-208d869c9aae humidity 50.6884765625 2019-10-11T11:51:11.264+02:00 2019-10-11T11:51:11.265+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:11.267+02:00 null
31 f98b69ee-bef9-4609-8bfa-82f11d7f51b6 humidity 50.76171875 2019-10-11T11:51:12.401+02:00 2019-10-11T11:51:12.402+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:12.41+02:00 null
32 eab34e23-2b43-40c2-9d6e-90e72fa13db5 humidity 50.7373046875 2019-10-11T11:51:13.551+02:00 2019-10-11T11:51:13.551+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:13.554+02:00 null
33 4659b946-8a40-4ba4-9888-6997bd1795f2 humidity 50.73828125 2019-10-11T11:51:14.695+02:00 2019-10-11T11:51:14.695+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:14.698+02:00 null
34 a6552aff-724c-4339-810a-36d911ddaa58 humidity 50.6845703125 2019-10-11T11:51:15.839+02:00 2019-10-11T11:51:15.839+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:15.842+02:00 null
35 c311cacd-e346-4b68-902b-400be0f8e02e humidity 50.6767578125 2019-10-11T11:51:15.976+02:00 2019-10-11T11:51:15.977+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:15.98+02:00 null
36 2d4db614-17f8-4ddd-ba08-e2a5f77d74ea humidity 50.68359375 2019-10-11T11:51:17.118+02:00 2019-10-11T11:51:17.118+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:17.121+02:00 null
37 936d61e4-494e-4959-a39a-c1dbc4a7d162 humidity 50.685546875 2019-10-11T11:51:17.243+02:00 2019-10-11T11:51:17.244+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:17.247+02:00 null
38 b913c9ca-4b5e-4c01-9aea-ee31d0d97977 humidity 50.6943359375 2019-10-11T11:51:18.381+02:00 2019-10-11T11:51:18.382+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:18.385+02:00 null
39 1171d0dd-2173-4da2-b2c6-7f7daaad7cd1 humidity 50.6943359375 2019-10-11T11:51:19.529+02:00 2019-10-11T11:51:19.529+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:19.532+02:00 null
40 d1a5e29c-d7e7-4ccc-9ff7-80ba1f797312 humidity 50.7080078125 2019-10-11T11:51:19.664+02:00 2019-10-11T11:51:19.664+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:19.667+02:00 null
41 140d6183-36c8-4903-a798-def4fcb3bd74 pressure 100150.5 2019-10-11T11:51:20.98+02:00 2019-10-11T11:51:20.98+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:20.982+02:00 null
42 98102a36-a38e-4ccc-9886-c119a7971414 pressure 100151.703125 2019-10-11T11:51:22.123+02:00 2019-10-11T11:51:22.123+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:22.125+02:00 null
43 5ffe51ae-687c-47df-8936-7785de9beed7 pressure 100150.8984375 2019-10-11T11:51:23.261+02:00 2019-10-11T11:51:23.262+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:23.263+02:00 null
44 e534cb23-c725-403b-b5c9-5455256a8662 pressure 100153.203125 2019-10-11T11:51:23.739+02:00 2019-10-11T11:51:23.739+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.594+02:00 null
45 e3c2a10b-373f-43d5-b02c-b381d1ad203b pressure 100149.296875 2019-10-11T11:51:24.721+02:00 2019-10-11T11:51:24.721+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.723+02:00 null
46 e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94 pressure 100150.796875 2019-10-11T11:51:24.849+02:00 2019-10-11T11:51:24.849+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:25.212+02:00 null
47 70f6de4d-3a17-4688-b635-b8965e85be43 pressure 100149.6015625 2019-10-11T11:51:26.352+02:00 2019-10-11T11:51:26.352+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.354+02:00 null
48 0e42478b-da36-4096-afad-d8c2a7685ccd pressure 100149.5 2019-10-11T11:51:26.488+02:00 2019-10-11T11:51:26.488+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.49+02:00 null
49 0313994c-db52-42d6-b059-2ca13afdd5d8 pressure 100151.3984375 2019-10-11T11:51:26.622+02:00 2019-10-11T11:51:26.622+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.624+02:00 null
50 9cb66b4c-31d8-40dc-8c5b-9d243c2bb974 pressure 100150.203125 2019-10-11T11:51:26.754+02:00 2019-10-11T11:51:26.754+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.757+02:00 null
51 6e626fdb-326d-4984-9585-7b64976be1a2 pressure 100150.5 2019-10-11T11:51:26.894+02:00 2019-10-11T11:51:26.894+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.896+02:00 null
52 6869ae37-0200-40c2-85a5-d4412d81b62d pressure 100154.1015625 2019-10-11T11:51:27.029+02:00 2019-10-11T11:51:27.029+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:27.032+02:00 null
53 8a0770b9-f8b0-42de-90fe-3e6a6311818b pressure 100152.3984375 2019-10-11T11:51:28.169+02:00 2019-10-11T11:51:28.169+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.171+02:00 null
54 cf989dbb-2e73-4042-b339-b4dbc75b0a7c pressure 100151.3984375 2019-10-11T11:51:28.294+02:00 2019-10-11T11:51:28.294+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.296+02:00 null
55 53a2d8fb-3b15-48a2-a277-1b821adba088 pressure 100151.1015625 2019-10-11T11:51:28.426+02:00 2019-10-11T11:51:28.426+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.428+02:00 null
56 9de16913-67f3-4e0b-9976-ddadbd057bae pressure 100153.8984375 2019-10-11T11:51:29.563+02:00 2019-10-11T11:51:29.563+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:29.565+02:00 null
57 d8c255a5-c5bd-4e9e-9aec-d0e6ae795719 pressure 100154.6015625 2019-10-11T11:51:30.707+02:00 2019-10-11T11:51:30.707+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:30.71+02:00 null
58 31ab31ac-e991-4081-8b4e-eba0f789b806 pressure 100153.796875 2019-10-11T11:51:31.843+02:00 2019-10-11T11:51:31.843+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:31.846+02:00 null
59 ed2ec0a9-9bc1-424a-9edf-85aad7a059ec pressure 100150.703125 2019-10-11T11:51:32.981+02:00 2019-10-11T11:51:32.981+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:32.984+02:00 null
60 31e03c4c-49a1-4c0d-a750-16c09b75f96b pressure 100151.203125 2019-10-11T11:51:34.125+02:00 2019-10-11T11:51:34.126+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:34.133+02:00 null

View File

@ -1,60 +0,0 @@
2029cc8c-56cf-4cd1-adf8-06e768fbad29,temperature,21,2019-10-11T11:50:43.317+02:00,2019-10-11T11:50:43.317+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:43.319+02:00,null
a588cc90-4dca-4a4e-be81-2d20b9f64458,temperature,21,2019-10-11T11:50:44.459+02:00,2019-10-11T11:50:44.459+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:44.473+02:00,null
1ebb60de-6066-4731-9f55-ee1c51748b7f,temperature,21,2019-10-11T11:50:45.614+02:00,2019-10-11T11:50:45.614+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:45.615+02:00,null
6cbc586f-a5c9-4296-b3b2-200afae12059,temperature,21,2019-10-11T11:50:46.776+02:00,2019-10-11T11:50:46.776+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:47.134+02:00,null
14e03e32-f214-4f61-97b0-8e021c47bead,temperature,21,2019-10-11T11:50:47.745+02:00,2019-10-11T11:50:47.745+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:47.747+02:00,null
88f5c095-8e88-4c80-b66a-cf154919afa8,temperature,21,2019-10-11T11:50:49.53+02:00,2019-10-11T11:50:49.53+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:50.018+02:00,null
7c3b7eac-3b43-4461-b204-d74c2bde3a08,temperature,21,2019-10-11T11:50:51.187+02:00,2019-10-11T11:50:51.187+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:51.189+02:00,null
3caf0b08-38d5-4536-baa5-86d4d75e359d,temperature,21,2019-10-11T11:50:52.508+02:00,2019-10-11T11:50:52.508+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:52.51+02:00,null
4b8595bf-3624-4963-b796-efc648515cd9,temperature,21,2019-10-11T11:50:53.652+02:00,2019-10-11T11:50:53.652+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:53.654+02:00,null
acb55d68-d909-41ba-a445-8ef9eab0c7aa,temperature,21,2019-10-11T11:50:53.788+02:00,2019-10-11T11:50:53.789+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:53.79+02:00,null
6563ff1e-371b-46a2-aca8-cc49b418bf61,temperature,21,2019-10-11T11:50:54.929+02:00,2019-10-11T11:50:54.93+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:55.405+02:00,null
a64f57c4-b95c-45b1-b78a-a6be263abb85,temperature,21,2019-10-11T11:50:55.536+02:00,2019-10-11T11:50:55.536+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:55.538+02:00,null
157eda79-3283-4cda-a21d-db0aa2da8548,temperature,21,2019-10-11T11:50:55.674+02:00,2019-10-11T11:50:55.674+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:55.677+02:00,null
c7d8d6ce-59d9-4448-9934-69cebc071cf6,temperature,21,2019-10-11T11:50:56.817+02:00,2019-10-11T11:50:56.817+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:56.82+02:00,null
07d08037-7267-44ab-8d90-878271809a4c,temperature,21,2019-10-11T11:50:57.959+02:00,2019-10-11T11:50:57.96+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:57.962+02:00,null
bf2e981f-8bad-466e-9a12-c1a5d9b64399,temperature,21,2019-10-11T11:50:59.1+02:00,2019-10-11T11:50:59.1+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:59.103+02:00,null
0b865113-d0f4-42f4-af3d-50e60edcc523,temperature,21,2019-10-11T11:50:59.238+02:00,2019-10-11T11:50:59.238+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:59.241+02:00,null
c40ad502-b6f1-49d8-8b6d-e7d5e475a6e2,temperature,21,2019-10-11T11:50:59.368+02:00,2019-10-11T11:50:59.368+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:59.371+02:00,null
8d98e723-7a07-4ccf-b3bc-7a2653952b8b,temperature,21,2019-10-11T11:51:00.509+02:00,2019-10-11T11:51:00.51+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:00.516+02:00,null
f61b7bfb-14eb-4b43-a7b6-47b61fa46dda,temperature,21,2019-10-11T11:51:01.658+02:00,2019-10-11T11:51:01.658+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:01.661+02:00,null
0fc9c4bf-7e86-4cee-b99c-7642984b5fcd,humidity,51,2019-10-11T11:51:01.969+02:00,2019-10-11T11:51:01.969+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:01.971+02:00,null
45e8b20c-1ab0-4cf1-9ef1-aa82537557f6,humidity,51,2019-10-11T11:51:03.109+02:00,2019-10-11T11:51:03.109+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:03.111+02:00,null
7b4f0fc3-ecec-4226-b621-49dd01cf9eb7,humidity,51,2019-10-11T11:51:04.251+02:00,2019-10-11T11:51:04.252+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:04.254+02:00,null
72ccf0a7-e680-460c-a3b8-7d14f46b5b0c,humidity,51,2019-10-11T11:51:05.402+02:00,2019-10-11T11:51:05.403+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:05.405+02:00,null
6f5277d9-abe3-4e87-b0f1-e4320756bbac,humidity,51,2019-10-11T11:51:06.551+02:00,2019-10-11T11:51:06.552+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:06.565+02:00,null
026370e8-2011-4bf0-8da9-ec4a9fe1ee19,humidity,51,2019-10-11T11:51:07.7+02:00,2019-10-11T11:51:07.7+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:07.702+02:00,null
e96eff46-8322-424d-9a79-51c746124354,humidity,50.5,2019-10-11T11:51:08.843+02:00,2019-10-11T11:51:08.843+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:08.846+02:00,null
6beb6aee-6888-4f4a-82ff-5bf700a22b0a,humidity,50.5,2019-10-11T11:51:09.986+02:00,2019-10-11T11:51:09.986+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:09.989+02:00,null
62a8f169-344d-4f9c-828a-7b4ea4261fad,humidity,50.5,2019-10-11T11:51:11.129+02:00,2019-10-11T11:51:11.13+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:11.133+02:00,null
b1d64d40-59d7-4093-ba1d-208d869c9aae,humidity,50.5,2019-10-11T11:51:11.264+02:00,2019-10-11T11:51:11.265+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:11.267+02:00,null
f98b69ee-bef9-4609-8bfa-82f11d7f51b6,humidity,51,2019-10-11T11:51:12.401+02:00,2019-10-11T11:51:12.402+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:12.41+02:00,null
eab34e23-2b43-40c2-9d6e-90e72fa13db5,humidity,50.5,2019-10-11T11:51:13.551+02:00,2019-10-11T11:51:13.551+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:13.554+02:00,null
4659b946-8a40-4ba4-9888-6997bd1795f2,humidity,50.5,2019-10-11T11:51:14.695+02:00,2019-10-11T11:51:14.695+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:14.698+02:00,null
a6552aff-724c-4339-810a-36d911ddaa58,humidity,50.5,2019-10-11T11:51:15.839+02:00,2019-10-11T11:51:15.839+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:15.842+02:00,null
c311cacd-e346-4b68-902b-400be0f8e02e,humidity,50.5,2019-10-11T11:51:15.976+02:00,2019-10-11T11:51:15.977+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:15.98+02:00,null
2d4db614-17f8-4ddd-ba08-e2a5f77d74ea,humidity,50.5,2019-10-11T11:51:17.118+02:00,2019-10-11T11:51:17.118+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:17.121+02:00,null
936d61e4-494e-4959-a39a-c1dbc4a7d162,humidity,50.5,2019-10-11T11:51:17.243+02:00,2019-10-11T11:51:17.244+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:17.247+02:00,null
b913c9ca-4b5e-4c01-9aea-ee31d0d97977,humidity,50.5,2019-10-11T11:51:18.381+02:00,2019-10-11T11:51:18.382+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:18.385+02:00,null
1171d0dd-2173-4da2-b2c6-7f7daaad7cd1,humidity,50.5,2019-10-11T11:51:19.529+02:00,2019-10-11T11:51:19.529+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:19.532+02:00,null
d1a5e29c-d7e7-4ccc-9ff7-80ba1f797312,humidity,50.5,2019-10-11T11:51:19.664+02:00,2019-10-11T11:51:19.664+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:19.667+02:00,null
140d6183-36c8-4903-a798-def4fcb3bd74,pressure,100150.5,2019-10-11T11:51:20.98+02:00,2019-10-11T11:51:20.98+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:20.982+02:00,null
98102a36-a38e-4ccc-9886-c119a7971414,pressure,100151.5,2019-10-11T11:51:22.123+02:00,2019-10-11T11:51:22.123+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:22.125+02:00,null
5ffe51ae-687c-47df-8936-7785de9beed7,pressure,100151,2019-10-11T11:51:23.261+02:00,2019-10-11T11:51:23.262+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:23.263+02:00,null
e534cb23-c725-403b-b5c9-5455256a8662,pressure,100153,2019-10-11T11:51:23.739+02:00,2019-10-11T11:51:23.739+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.594+02:00,null
e3c2a10b-373f-43d5-b02c-b381d1ad203b,pressure,100149.5,2019-10-11T11:51:24.721+02:00,2019-10-11T11:51:24.721+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.723+02:00,null
e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94,pressure,100151,2019-10-11T11:51:24.849+02:00,2019-10-11T11:51:24.849+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:25.212+02:00,null
70f6de4d-3a17-4688-b635-b8965e85be43,pressure,100149.5,2019-10-11T11:51:26.352+02:00,2019-10-11T11:51:26.352+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.354+02:00,null
0e42478b-da36-4096-afad-d8c2a7685ccd,pressure,100149.5,2019-10-11T11:51:26.488+02:00,2019-10-11T11:51:26.488+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.49+02:00,null
0313994c-db52-42d6-b059-2ca13afdd5d8,pressure,100151.5,2019-10-11T11:51:26.622+02:00,2019-10-11T11:51:26.622+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.624+02:00,null
9cb66b4c-31d8-40dc-8c5b-9d243c2bb974,pressure,100150,2019-10-11T11:51:26.754+02:00,2019-10-11T11:51:26.754+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.757+02:00,null
6e626fdb-326d-4984-9585-7b64976be1a2,pressure,100150.5,2019-10-11T11:51:26.894+02:00,2019-10-11T11:51:26.894+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.896+02:00,null
6869ae37-0200-40c2-85a5-d4412d81b62d,pressure,100154,2019-10-11T11:51:27.029+02:00,2019-10-11T11:51:27.029+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:27.032+02:00,null
8a0770b9-f8b0-42de-90fe-3e6a6311818b,pressure,100152.5,2019-10-11T11:51:28.169+02:00,2019-10-11T11:51:28.169+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.171+02:00,null
cf989dbb-2e73-4042-b339-b4dbc75b0a7c,pressure,100151.5,2019-10-11T11:51:28.294+02:00,2019-10-11T11:51:28.294+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.296+02:00,null
53a2d8fb-3b15-48a2-a277-1b821adba088,pressure,100151,2019-10-11T11:51:28.426+02:00,2019-10-11T11:51:28.426+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.428+02:00,null
9de16913-67f3-4e0b-9976-ddadbd057bae,pressure,100154,2019-10-11T11:51:29.563+02:00,2019-10-11T11:51:29.563+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:29.565+02:00,null
d8c255a5-c5bd-4e9e-9aec-d0e6ae795719,pressure,100154.5,2019-10-11T11:51:30.707+02:00,2019-10-11T11:51:30.707+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:30.71+02:00,null
31ab31ac-e991-4081-8b4e-eba0f789b806,pressure,100154,2019-10-11T11:51:31.843+02:00,2019-10-11T11:51:31.843+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:31.846+02:00,null
ed2ec0a9-9bc1-424a-9edf-85aad7a059ec,pressure,100150.5,2019-10-11T11:51:32.981+02:00,2019-10-11T11:51:32.981+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:32.984+02:00,null
31e03c4c-49a1-4c0d-a750-16c09b75f96b,pressure,100151,2019-10-11T11:51:34.125+02:00,2019-10-11T11:51:34.126+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:34.133+02:00,null
1 2029cc8c-56cf-4cd1-adf8-06e768fbad29 temperature 21 2019-10-11T11:50:43.317+02:00 2019-10-11T11:50:43.317+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:43.319+02:00 null
2 a588cc90-4dca-4a4e-be81-2d20b9f64458 temperature 21 2019-10-11T11:50:44.459+02:00 2019-10-11T11:50:44.459+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:44.473+02:00 null
3 1ebb60de-6066-4731-9f55-ee1c51748b7f temperature 21 2019-10-11T11:50:45.614+02:00 2019-10-11T11:50:45.614+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:45.615+02:00 null
4 6cbc586f-a5c9-4296-b3b2-200afae12059 temperature 21 2019-10-11T11:50:46.776+02:00 2019-10-11T11:50:46.776+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:47.134+02:00 null
5 14e03e32-f214-4f61-97b0-8e021c47bead temperature 21 2019-10-11T11:50:47.745+02:00 2019-10-11T11:50:47.745+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:47.747+02:00 null
6 88f5c095-8e88-4c80-b66a-cf154919afa8 temperature 21 2019-10-11T11:50:49.53+02:00 2019-10-11T11:50:49.53+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:50.018+02:00 null
7 7c3b7eac-3b43-4461-b204-d74c2bde3a08 temperature 21 2019-10-11T11:50:51.187+02:00 2019-10-11T11:50:51.187+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:51.189+02:00 null
8 3caf0b08-38d5-4536-baa5-86d4d75e359d temperature 21 2019-10-11T11:50:52.508+02:00 2019-10-11T11:50:52.508+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:52.51+02:00 null
9 4b8595bf-3624-4963-b796-efc648515cd9 temperature 21 2019-10-11T11:50:53.652+02:00 2019-10-11T11:50:53.652+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:53.654+02:00 null
10 acb55d68-d909-41ba-a445-8ef9eab0c7aa temperature 21 2019-10-11T11:50:53.788+02:00 2019-10-11T11:50:53.789+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:53.79+02:00 null
11 6563ff1e-371b-46a2-aca8-cc49b418bf61 temperature 21 2019-10-11T11:50:54.929+02:00 2019-10-11T11:50:54.93+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:55.405+02:00 null
12 a64f57c4-b95c-45b1-b78a-a6be263abb85 temperature 21 2019-10-11T11:50:55.536+02:00 2019-10-11T11:50:55.536+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:55.538+02:00 null
13 157eda79-3283-4cda-a21d-db0aa2da8548 temperature 21 2019-10-11T11:50:55.674+02:00 2019-10-11T11:50:55.674+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:55.677+02:00 null
14 c7d8d6ce-59d9-4448-9934-69cebc071cf6 temperature 21 2019-10-11T11:50:56.817+02:00 2019-10-11T11:50:56.817+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:56.82+02:00 null
15 07d08037-7267-44ab-8d90-878271809a4c temperature 21 2019-10-11T11:50:57.959+02:00 2019-10-11T11:50:57.96+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:57.962+02:00 null
16 bf2e981f-8bad-466e-9a12-c1a5d9b64399 temperature 21 2019-10-11T11:50:59.1+02:00 2019-10-11T11:50:59.1+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:59.103+02:00 null
17 0b865113-d0f4-42f4-af3d-50e60edcc523 temperature 21 2019-10-11T11:50:59.238+02:00 2019-10-11T11:50:59.238+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:59.241+02:00 null
18 c40ad502-b6f1-49d8-8b6d-e7d5e475a6e2 temperature 21 2019-10-11T11:50:59.368+02:00 2019-10-11T11:50:59.368+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:59.371+02:00 null
19 8d98e723-7a07-4ccf-b3bc-7a2653952b8b temperature 21 2019-10-11T11:51:00.509+02:00 2019-10-11T11:51:00.51+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:00.516+02:00 null
20 f61b7bfb-14eb-4b43-a7b6-47b61fa46dda temperature 21 2019-10-11T11:51:01.658+02:00 2019-10-11T11:51:01.658+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:01.661+02:00 null
21 0fc9c4bf-7e86-4cee-b99c-7642984b5fcd humidity 51 2019-10-11T11:51:01.969+02:00 2019-10-11T11:51:01.969+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:01.971+02:00 null
22 45e8b20c-1ab0-4cf1-9ef1-aa82537557f6 humidity 51 2019-10-11T11:51:03.109+02:00 2019-10-11T11:51:03.109+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:03.111+02:00 null
23 7b4f0fc3-ecec-4226-b621-49dd01cf9eb7 humidity 51 2019-10-11T11:51:04.251+02:00 2019-10-11T11:51:04.252+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:04.254+02:00 null
24 72ccf0a7-e680-460c-a3b8-7d14f46b5b0c humidity 51 2019-10-11T11:51:05.402+02:00 2019-10-11T11:51:05.403+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:05.405+02:00 null
25 6f5277d9-abe3-4e87-b0f1-e4320756bbac humidity 51 2019-10-11T11:51:06.551+02:00 2019-10-11T11:51:06.552+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:06.565+02:00 null
26 026370e8-2011-4bf0-8da9-ec4a9fe1ee19 humidity 51 2019-10-11T11:51:07.7+02:00 2019-10-11T11:51:07.7+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:07.702+02:00 null
27 e96eff46-8322-424d-9a79-51c746124354 humidity 50.5 2019-10-11T11:51:08.843+02:00 2019-10-11T11:51:08.843+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:08.846+02:00 null
28 6beb6aee-6888-4f4a-82ff-5bf700a22b0a humidity 50.5 2019-10-11T11:51:09.986+02:00 2019-10-11T11:51:09.986+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:09.989+02:00 null
29 62a8f169-344d-4f9c-828a-7b4ea4261fad humidity 50.5 2019-10-11T11:51:11.129+02:00 2019-10-11T11:51:11.13+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:11.133+02:00 null
30 b1d64d40-59d7-4093-ba1d-208d869c9aae humidity 50.5 2019-10-11T11:51:11.264+02:00 2019-10-11T11:51:11.265+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:11.267+02:00 null
31 f98b69ee-bef9-4609-8bfa-82f11d7f51b6 humidity 51 2019-10-11T11:51:12.401+02:00 2019-10-11T11:51:12.402+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:12.41+02:00 null
32 eab34e23-2b43-40c2-9d6e-90e72fa13db5 humidity 50.5 2019-10-11T11:51:13.551+02:00 2019-10-11T11:51:13.551+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:13.554+02:00 null
33 4659b946-8a40-4ba4-9888-6997bd1795f2 humidity 50.5 2019-10-11T11:51:14.695+02:00 2019-10-11T11:51:14.695+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:14.698+02:00 null
34 a6552aff-724c-4339-810a-36d911ddaa58 humidity 50.5 2019-10-11T11:51:15.839+02:00 2019-10-11T11:51:15.839+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:15.842+02:00 null
35 c311cacd-e346-4b68-902b-400be0f8e02e humidity 50.5 2019-10-11T11:51:15.976+02:00 2019-10-11T11:51:15.977+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:15.98+02:00 null
36 2d4db614-17f8-4ddd-ba08-e2a5f77d74ea humidity 50.5 2019-10-11T11:51:17.118+02:00 2019-10-11T11:51:17.118+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:17.121+02:00 null
37 936d61e4-494e-4959-a39a-c1dbc4a7d162 humidity 50.5 2019-10-11T11:51:17.243+02:00 2019-10-11T11:51:17.244+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:17.247+02:00 null
38 b913c9ca-4b5e-4c01-9aea-ee31d0d97977 humidity 50.5 2019-10-11T11:51:18.381+02:00 2019-10-11T11:51:18.382+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:18.385+02:00 null
39 1171d0dd-2173-4da2-b2c6-7f7daaad7cd1 humidity 50.5 2019-10-11T11:51:19.529+02:00 2019-10-11T11:51:19.529+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:19.532+02:00 null
40 d1a5e29c-d7e7-4ccc-9ff7-80ba1f797312 humidity 50.5 2019-10-11T11:51:19.664+02:00 2019-10-11T11:51:19.664+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:19.667+02:00 null
41 140d6183-36c8-4903-a798-def4fcb3bd74 pressure 100150.5 2019-10-11T11:51:20.98+02:00 2019-10-11T11:51:20.98+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:20.982+02:00 null
42 98102a36-a38e-4ccc-9886-c119a7971414 pressure 100151.5 2019-10-11T11:51:22.123+02:00 2019-10-11T11:51:22.123+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:22.125+02:00 null
43 5ffe51ae-687c-47df-8936-7785de9beed7 pressure 100151 2019-10-11T11:51:23.261+02:00 2019-10-11T11:51:23.262+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:23.263+02:00 null
44 e534cb23-c725-403b-b5c9-5455256a8662 pressure 100153 2019-10-11T11:51:23.739+02:00 2019-10-11T11:51:23.739+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.594+02:00 null
45 e3c2a10b-373f-43d5-b02c-b381d1ad203b pressure 100149.5 2019-10-11T11:51:24.721+02:00 2019-10-11T11:51:24.721+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.723+02:00 null
46 e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94 pressure 100151 2019-10-11T11:51:24.849+02:00 2019-10-11T11:51:24.849+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:25.212+02:00 null
47 70f6de4d-3a17-4688-b635-b8965e85be43 pressure 100149.5 2019-10-11T11:51:26.352+02:00 2019-10-11T11:51:26.352+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.354+02:00 null
48 0e42478b-da36-4096-afad-d8c2a7685ccd pressure 100149.5 2019-10-11T11:51:26.488+02:00 2019-10-11T11:51:26.488+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.49+02:00 null
49 0313994c-db52-42d6-b059-2ca13afdd5d8 pressure 100151.5 2019-10-11T11:51:26.622+02:00 2019-10-11T11:51:26.622+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.624+02:00 null
50 9cb66b4c-31d8-40dc-8c5b-9d243c2bb974 pressure 100150 2019-10-11T11:51:26.754+02:00 2019-10-11T11:51:26.754+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.757+02:00 null
51 6e626fdb-326d-4984-9585-7b64976be1a2 pressure 100150.5 2019-10-11T11:51:26.894+02:00 2019-10-11T11:51:26.894+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.896+02:00 null
52 6869ae37-0200-40c2-85a5-d4412d81b62d pressure 100154 2019-10-11T11:51:27.029+02:00 2019-10-11T11:51:27.029+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:27.032+02:00 null
53 8a0770b9-f8b0-42de-90fe-3e6a6311818b pressure 100152.5 2019-10-11T11:51:28.169+02:00 2019-10-11T11:51:28.169+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.171+02:00 null
54 cf989dbb-2e73-4042-b339-b4dbc75b0a7c pressure 100151.5 2019-10-11T11:51:28.294+02:00 2019-10-11T11:51:28.294+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.296+02:00 null
55 53a2d8fb-3b15-48a2-a277-1b821adba088 pressure 100151 2019-10-11T11:51:28.426+02:00 2019-10-11T11:51:28.426+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.428+02:00 null
56 9de16913-67f3-4e0b-9976-ddadbd057bae pressure 100154 2019-10-11T11:51:29.563+02:00 2019-10-11T11:51:29.563+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:29.565+02:00 null
57 d8c255a5-c5bd-4e9e-9aec-d0e6ae795719 pressure 100154.5 2019-10-11T11:51:30.707+02:00 2019-10-11T11:51:30.707+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:30.71+02:00 null
58 31ab31ac-e991-4081-8b4e-eba0f789b806 pressure 100154 2019-10-11T11:51:31.843+02:00 2019-10-11T11:51:31.843+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:31.846+02:00 null
59 ed2ec0a9-9bc1-424a-9edf-85aad7a059ec pressure 100150.5 2019-10-11T11:51:32.981+02:00 2019-10-11T11:51:32.981+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:32.984+02:00 null
60 31e03c4c-49a1-4c0d-a750-16c09b75f96b pressure 100151 2019-10-11T11:51:34.125+02:00 2019-10-11T11:51:34.126+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:34.133+02:00 null

View File

@ -1,20 +0,0 @@
140d6183-36c8-4903-a798-def4fcb3bd74,pressure,100150.5,2019-10-11T11:51:20.98+02:00,2019-10-11T11:51:20.98+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:20.982+02:00,null
98102a36-a38e-4ccc-9886-c119a7971414,pressure,100151.703125,2019-10-11T11:51:22.123+02:00,2019-10-11T11:51:22.123+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:22.125+02:00,null
5ffe51ae-687c-47df-8936-7785de9beed7,pressure,100150.8984375,2019-10-11T11:51:23.261+02:00,2019-10-11T11:51:23.262+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:23.263+02:00,null
e534cb23-c725-403b-b5c9-5455256a8662,pressure,100153.203125,2019-10-11T11:51:23.739+02:00,2019-10-11T11:51:23.739+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.594+02:00,null
e3c2a10b-373f-43d5-b02c-b381d1ad203b,pressure,100149.296875,2019-10-11T11:51:24.721+02:00,2019-10-11T11:51:24.721+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.723+02:00,null
e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94,pressure,100150.796875,2019-10-11T11:51:24.849+02:00,2019-10-11T11:51:24.849+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:25.212+02:00,null
70f6de4d-3a17-4688-b635-b8965e85be43,pressure,100149.6015625,2019-10-11T11:51:26.352+02:00,2019-10-11T11:51:26.352+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.354+02:00,null
0e42478b-da36-4096-afad-d8c2a7685ccd,pressure,100149.5,2019-10-11T11:51:26.488+02:00,2019-10-11T11:51:26.488+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.49+02:00,null
0313994c-db52-42d6-b059-2ca13afdd5d8,pressure,100151.3984375,2019-10-11T11:51:26.622+02:00,2019-10-11T11:51:26.622+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.624+02:00,null
9cb66b4c-31d8-40dc-8c5b-9d243c2bb974,pressure,100150.203125,2019-10-11T11:51:26.754+02:00,2019-10-11T11:51:26.754+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.757+02:00,null
6e626fdb-326d-4984-9585-7b64976be1a2,pressure,100150.5,2019-10-11T11:51:26.894+02:00,2019-10-11T11:51:26.894+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.896+02:00,null
6869ae37-0200-40c2-85a5-d4412d81b62d,pressure,100154.1015625,2019-10-11T11:51:27.029+02:00,2019-10-11T11:51:27.029+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:27.032+02:00,null
8a0770b9-f8b0-42de-90fe-3e6a6311818b,pressure,100152.3984375,2019-10-11T11:51:28.169+02:00,2019-10-11T11:51:28.169+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.171+02:00,null
cf989dbb-2e73-4042-b339-b4dbc75b0a7c,pressure,100151.3984375,2019-10-11T11:51:28.294+02:00,2019-10-11T11:51:28.294+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.296+02:00,null
53a2d8fb-3b15-48a2-a277-1b821adba088,pressure,100151.1015625,2019-10-11T11:51:28.426+02:00,2019-10-11T11:51:28.426+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.428+02:00,null
9de16913-67f3-4e0b-9976-ddadbd057bae,pressure,100153.8984375,2019-10-11T11:51:29.563+02:00,2019-10-11T11:51:29.563+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:29.565+02:00,null
d8c255a5-c5bd-4e9e-9aec-d0e6ae795719,pressure,100154.6015625,2019-10-11T11:51:30.707+02:00,2019-10-11T11:51:30.707+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:30.71+02:00,null
31ab31ac-e991-4081-8b4e-eba0f789b806,pressure,100153.796875,2019-10-11T11:51:31.843+02:00,2019-10-11T11:51:31.843+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:31.846+02:00,null
ed2ec0a9-9bc1-424a-9edf-85aad7a059ec,pressure,100150.703125,2019-10-11T11:51:32.981+02:00,2019-10-11T11:51:32.981+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:32.984+02:00,null
31e03c4c-49a1-4c0d-a750-16c09b75f96b,pressure,100151.203125,2019-10-11T11:51:34.125+02:00,2019-10-11T11:51:34.126+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:34.133+02:00,null
1 140d6183-36c8-4903-a798-def4fcb3bd74 pressure 100150.5 2019-10-11T11:51:20.98+02:00 2019-10-11T11:51:20.98+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:20.982+02:00 null
2 98102a36-a38e-4ccc-9886-c119a7971414 pressure 100151.703125 2019-10-11T11:51:22.123+02:00 2019-10-11T11:51:22.123+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:22.125+02:00 null
3 5ffe51ae-687c-47df-8936-7785de9beed7 pressure 100150.8984375 2019-10-11T11:51:23.261+02:00 2019-10-11T11:51:23.262+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:23.263+02:00 null
4 e534cb23-c725-403b-b5c9-5455256a8662 pressure 100153.203125 2019-10-11T11:51:23.739+02:00 2019-10-11T11:51:23.739+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.594+02:00 null
5 e3c2a10b-373f-43d5-b02c-b381d1ad203b pressure 100149.296875 2019-10-11T11:51:24.721+02:00 2019-10-11T11:51:24.721+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.723+02:00 null
6 e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94 pressure 100150.796875 2019-10-11T11:51:24.849+02:00 2019-10-11T11:51:24.849+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:25.212+02:00 null
7 70f6de4d-3a17-4688-b635-b8965e85be43 pressure 100149.6015625 2019-10-11T11:51:26.352+02:00 2019-10-11T11:51:26.352+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.354+02:00 null
8 0e42478b-da36-4096-afad-d8c2a7685ccd pressure 100149.5 2019-10-11T11:51:26.488+02:00 2019-10-11T11:51:26.488+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.49+02:00 null
9 0313994c-db52-42d6-b059-2ca13afdd5d8 pressure 100151.3984375 2019-10-11T11:51:26.622+02:00 2019-10-11T11:51:26.622+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.624+02:00 null
10 9cb66b4c-31d8-40dc-8c5b-9d243c2bb974 pressure 100150.203125 2019-10-11T11:51:26.754+02:00 2019-10-11T11:51:26.754+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.757+02:00 null
11 6e626fdb-326d-4984-9585-7b64976be1a2 pressure 100150.5 2019-10-11T11:51:26.894+02:00 2019-10-11T11:51:26.894+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.896+02:00 null
12 6869ae37-0200-40c2-85a5-d4412d81b62d pressure 100154.1015625 2019-10-11T11:51:27.029+02:00 2019-10-11T11:51:27.029+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:27.032+02:00 null
13 8a0770b9-f8b0-42de-90fe-3e6a6311818b pressure 100152.3984375 2019-10-11T11:51:28.169+02:00 2019-10-11T11:51:28.169+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.171+02:00 null
14 cf989dbb-2e73-4042-b339-b4dbc75b0a7c pressure 100151.3984375 2019-10-11T11:51:28.294+02:00 2019-10-11T11:51:28.294+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.296+02:00 null
15 53a2d8fb-3b15-48a2-a277-1b821adba088 pressure 100151.1015625 2019-10-11T11:51:28.426+02:00 2019-10-11T11:51:28.426+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.428+02:00 null
16 9de16913-67f3-4e0b-9976-ddadbd057bae pressure 100153.8984375 2019-10-11T11:51:29.563+02:00 2019-10-11T11:51:29.563+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:29.565+02:00 null
17 d8c255a5-c5bd-4e9e-9aec-d0e6ae795719 pressure 100154.6015625 2019-10-11T11:51:30.707+02:00 2019-10-11T11:51:30.707+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:30.71+02:00 null
18 31ab31ac-e991-4081-8b4e-eba0f789b806 pressure 100153.796875 2019-10-11T11:51:31.843+02:00 2019-10-11T11:51:31.843+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:31.846+02:00 null
19 ed2ec0a9-9bc1-424a-9edf-85aad7a059ec pressure 100150.703125 2019-10-11T11:51:32.981+02:00 2019-10-11T11:51:32.981+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:32.984+02:00 null
20 31e03c4c-49a1-4c0d-a750-16c09b75f96b pressure 100151.203125 2019-10-11T11:51:34.125+02:00 2019-10-11T11:51:34.126+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:34.133+02:00 null

View File

@ -1,19 +0,0 @@
140d6183-36c8-4903-a798-def4fcb3bd74,pressure,100150.5,2019-10-11T11:51:20.98+02:00,2019-10-11T11:51:20.98+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:20.982+02:00,null
98102a36-a38e-4ccc-9886-c119a7971414,pressure,100151.5,2019-10-11T11:51:22.123+02:00,2019-10-11T11:51:22.123+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:22.125+02:00,null
5ffe51ae-687c-47df-8936-7785de9beed7,pressure,100151,2019-10-11T11:51:23.261+02:00,2019-10-11T11:51:23.262+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:23.263+02:00,null
e534cb23-c725-403b-b5c9-5455256a8662,pressure,100153,2019-10-11T11:51:23.739+02:00,2019-10-11T11:51:23.739+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.594+02:00,null
e3c2a10b-373f-43d5-b02c-b381d1ad203b,pressure,100149.5,2019-10-11T11:51:24.721+02:00,2019-10-11T11:51:24.721+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.723+02:00,null
e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94,pressure,100151,2019-10-11T11:51:24.849+02:00,2019-10-11T11:51:24.849+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:25.212+02:00,null
70f6de4d-3a17-4688-b635-b8965e85be43,pressure,100149.5,2019-10-11T11:51:26.352+02:00,2019-10-11T11:51:26.488+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.354+02:00,2019-10-11T11:51:35.22+02:00
0313994c-db52-42d6-b059-2ca13afdd5d8,pressure,100151.5,2019-10-11T11:51:26.622+02:00,2019-10-11T11:51:26.622+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.624+02:00,null
9cb66b4c-31d8-40dc-8c5b-9d243c2bb974,pressure,100150,2019-10-11T11:51:26.754+02:00,2019-10-11T11:51:26.754+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.757+02:00,null
6e626fdb-326d-4984-9585-7b64976be1a2,pressure,100150.5,2019-10-11T11:51:26.894+02:00,2019-10-11T11:51:26.894+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.896+02:00,null
6869ae37-0200-40c2-85a5-d4412d81b62d,pressure,100154,2019-10-11T11:51:27.029+02:00,2019-10-11T11:51:27.029+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:27.032+02:00,null
8a0770b9-f8b0-42de-90fe-3e6a6311818b,pressure,100152.5,2019-10-11T11:51:28.169+02:00,2019-10-11T11:51:28.169+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.171+02:00,null
cf989dbb-2e73-4042-b339-b4dbc75b0a7c,pressure,100151.5,2019-10-11T11:51:28.294+02:00,2019-10-11T11:51:28.294+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.296+02:00,null
53a2d8fb-3b15-48a2-a277-1b821adba088,pressure,100151,2019-10-11T11:51:28.426+02:00,2019-10-11T11:51:28.426+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.428+02:00,null
9de16913-67f3-4e0b-9976-ddadbd057bae,pressure,100154,2019-10-11T11:51:29.563+02:00,2019-10-11T11:51:29.563+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:29.565+02:00,null
d8c255a5-c5bd-4e9e-9aec-d0e6ae795719,pressure,100154.5,2019-10-11T11:51:30.707+02:00,2019-10-11T11:51:30.707+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:30.71+02:00,null
31ab31ac-e991-4081-8b4e-eba0f789b806,pressure,100154,2019-10-11T11:51:31.843+02:00,2019-10-11T11:51:31.843+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:31.846+02:00,null
ed2ec0a9-9bc1-424a-9edf-85aad7a059ec,pressure,100150.5,2019-10-11T11:51:32.981+02:00,2019-10-11T11:51:32.981+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:32.984+02:00,null
31e03c4c-49a1-4c0d-a750-16c09b75f96b,pressure,100151,2019-10-11T11:51:34.125+02:00,2019-10-11T11:51:34.126+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:34.133+02:00,null
1 140d6183-36c8-4903-a798-def4fcb3bd74 pressure 100150.5 2019-10-11T11:51:20.98+02:00 2019-10-11T11:51:20.98+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:20.982+02:00 null
2 98102a36-a38e-4ccc-9886-c119a7971414 pressure 100151.5 2019-10-11T11:51:22.123+02:00 2019-10-11T11:51:22.123+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:22.125+02:00 null
3 5ffe51ae-687c-47df-8936-7785de9beed7 pressure 100151 2019-10-11T11:51:23.261+02:00 2019-10-11T11:51:23.262+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:23.263+02:00 null
4 e534cb23-c725-403b-b5c9-5455256a8662 pressure 100153 2019-10-11T11:51:23.739+02:00 2019-10-11T11:51:23.739+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.594+02:00 null
5 e3c2a10b-373f-43d5-b02c-b381d1ad203b pressure 100149.5 2019-10-11T11:51:24.721+02:00 2019-10-11T11:51:24.721+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.723+02:00 null
6 e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94 pressure 100151 2019-10-11T11:51:24.849+02:00 2019-10-11T11:51:24.849+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:25.212+02:00 null
7 70f6de4d-3a17-4688-b635-b8965e85be43 pressure 100149.5 2019-10-11T11:51:26.352+02:00 2019-10-11T11:51:26.488+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.354+02:00 2019-10-11T11:51:35.22+02:00
8 0313994c-db52-42d6-b059-2ca13afdd5d8 pressure 100151.5 2019-10-11T11:51:26.622+02:00 2019-10-11T11:51:26.622+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.624+02:00 null
9 9cb66b4c-31d8-40dc-8c5b-9d243c2bb974 pressure 100150 2019-10-11T11:51:26.754+02:00 2019-10-11T11:51:26.754+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.757+02:00 null
10 6e626fdb-326d-4984-9585-7b64976be1a2 pressure 100150.5 2019-10-11T11:51:26.894+02:00 2019-10-11T11:51:26.894+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.896+02:00 null
11 6869ae37-0200-40c2-85a5-d4412d81b62d pressure 100154 2019-10-11T11:51:27.029+02:00 2019-10-11T11:51:27.029+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:27.032+02:00 null
12 8a0770b9-f8b0-42de-90fe-3e6a6311818b pressure 100152.5 2019-10-11T11:51:28.169+02:00 2019-10-11T11:51:28.169+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.171+02:00 null
13 cf989dbb-2e73-4042-b339-b4dbc75b0a7c pressure 100151.5 2019-10-11T11:51:28.294+02:00 2019-10-11T11:51:28.294+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.296+02:00 null
14 53a2d8fb-3b15-48a2-a277-1b821adba088 pressure 100151 2019-10-11T11:51:28.426+02:00 2019-10-11T11:51:28.426+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.428+02:00 null
15 9de16913-67f3-4e0b-9976-ddadbd057bae pressure 100154 2019-10-11T11:51:29.563+02:00 2019-10-11T11:51:29.563+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:29.565+02:00 null
16 d8c255a5-c5bd-4e9e-9aec-d0e6ae795719 pressure 100154.5 2019-10-11T11:51:30.707+02:00 2019-10-11T11:51:30.707+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:30.71+02:00 null
17 31ab31ac-e991-4081-8b4e-eba0f789b806 pressure 100154 2019-10-11T11:51:31.843+02:00 2019-10-11T11:51:31.843+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:31.846+02:00 null
18 ed2ec0a9-9bc1-424a-9edf-85aad7a059ec pressure 100150.5 2019-10-11T11:51:32.981+02:00 2019-10-11T11:51:32.981+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:32.984+02:00 null
19 31e03c4c-49a1-4c0d-a750-16c09b75f96b pressure 100151 2019-10-11T11:51:34.125+02:00 2019-10-11T11:51:34.126+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:34.133+02:00 null

View File

@ -1,20 +0,0 @@
140d6183-36c8-4903-a798-def4fcb3bd74,pressure,100150.5,2019-10-11T11:51:20.98+02:00,2019-10-11T11:51:20.98+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:20.982+02:00,null
98102a36-a38e-4ccc-9886-c119a7971414,pressure,100151.703125,2019-10-11T11:51:22.123+02:00,2019-10-11T11:51:22.123+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:22.125+02:00,null
5ffe51ae-687c-47df-8936-7785de9beed7,pressure,100150.8984375,2019-10-11T11:51:23.261+02:00,2019-10-11T11:51:23.262+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:23.263+02:00,null
e534cb23-c725-403b-b5c9-5455256a8662,pressure,100153.203125,2019-10-11T11:51:23.739+02:00,2019-10-11T11:51:23.739+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.594+02:00,null
e3c2a10b-373f-43d5-b02c-b381d1ad203b,pressure,100149.296875,2019-10-11T11:51:24.721+02:00,2019-10-11T11:51:24.721+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.723+02:00,null
e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94,pressure,100150.796875,2019-10-11T11:51:24.849+02:00,2019-10-11T11:51:24.849+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:25.212+02:00,null
70f6de4d-3a17-4688-b635-b8965e85be43,pressure,100149.6015625,2019-10-11T11:51:26.352+02:00,2019-10-11T11:51:26.352+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.354+02:00,null
0e42478b-da36-4096-afad-d8c2a7685ccd,pressure,100149.5,2019-10-11T11:51:26.488+02:00,2019-10-11T11:51:26.488+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.49+02:00,null
0313994c-db52-42d6-b059-2ca13afdd5d8,pressure,100151.3984375,2019-10-11T11:51:26.622+02:00,2019-10-11T11:51:26.622+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.624+02:00,null
9cb66b4c-31d8-40dc-8c5b-9d243c2bb974,pressure,100150.203125,2019-10-11T11:51:26.754+02:00,2019-10-11T11:51:26.754+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.757+02:00,null
6e626fdb-326d-4984-9585-7b64976be1a2,pressure,100150.5,2019-10-11T11:51:26.894+02:00,2019-10-11T11:51:26.894+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.896+02:00,null
6869ae37-0200-40c2-85a5-d4412d81b62d,pressure,100154.1015625,2019-10-11T11:51:27.029+02:00,2019-10-11T11:51:27.029+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:27.032+02:00,null
8a0770b9-f8b0-42de-90fe-3e6a6311818b,pressure,100152.3984375,2019-10-11T11:51:28.169+02:00,2019-10-11T11:51:28.169+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.171+02:00,null
cf989dbb-2e73-4042-b339-b4dbc75b0a7c,pressure,100151.3984375,2019-10-11T11:51:28.294+02:00,2019-10-11T11:51:28.294+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.296+02:00,null
53a2d8fb-3b15-48a2-a277-1b821adba088,pressure,100151.1015625,2019-10-11T11:51:28.426+02:00,2019-10-11T11:51:28.426+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.428+02:00,null
9de16913-67f3-4e0b-9976-ddadbd057bae,pressure,100153.8984375,2019-10-11T11:51:29.563+02:00,2019-10-11T11:51:29.563+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:29.565+02:00,null
d8c255a5-c5bd-4e9e-9aec-d0e6ae795719,pressure,100154.6015625,2019-10-11T11:51:30.707+02:00,2019-10-11T11:51:30.707+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:30.71+02:00,null
31ab31ac-e991-4081-8b4e-eba0f789b806,pressure,100153.796875,2019-10-11T11:51:31.843+02:00,2019-10-11T11:51:31.843+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:31.846+02:00,null
ed2ec0a9-9bc1-424a-9edf-85aad7a059ec,pressure,100150.703125,2019-10-11T11:51:32.981+02:00,2019-10-11T11:51:32.981+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:32.984+02:00,null
31e03c4c-49a1-4c0d-a750-16c09b75f96b,pressure,100151.203125,2019-10-11T11:51:34.125+02:00,2019-10-11T11:51:34.126+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:34.133+02:00,null
1 140d6183-36c8-4903-a798-def4fcb3bd74 pressure 100150.5 2019-10-11T11:51:20.98+02:00 2019-10-11T11:51:20.98+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:20.982+02:00 null
2 98102a36-a38e-4ccc-9886-c119a7971414 pressure 100151.703125 2019-10-11T11:51:22.123+02:00 2019-10-11T11:51:22.123+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:22.125+02:00 null
3 5ffe51ae-687c-47df-8936-7785de9beed7 pressure 100150.8984375 2019-10-11T11:51:23.261+02:00 2019-10-11T11:51:23.262+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:23.263+02:00 null
4 e534cb23-c725-403b-b5c9-5455256a8662 pressure 100153.203125 2019-10-11T11:51:23.739+02:00 2019-10-11T11:51:23.739+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.594+02:00 null
5 e3c2a10b-373f-43d5-b02c-b381d1ad203b pressure 100149.296875 2019-10-11T11:51:24.721+02:00 2019-10-11T11:51:24.721+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.723+02:00 null
6 e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94 pressure 100150.796875 2019-10-11T11:51:24.849+02:00 2019-10-11T11:51:24.849+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:25.212+02:00 null
7 70f6de4d-3a17-4688-b635-b8965e85be43 pressure 100149.6015625 2019-10-11T11:51:26.352+02:00 2019-10-11T11:51:26.352+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.354+02:00 null
8 0e42478b-da36-4096-afad-d8c2a7685ccd pressure 100149.5 2019-10-11T11:51:26.488+02:00 2019-10-11T11:51:26.488+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.49+02:00 null
9 0313994c-db52-42d6-b059-2ca13afdd5d8 pressure 100151.3984375 2019-10-11T11:51:26.622+02:00 2019-10-11T11:51:26.622+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.624+02:00 null
10 9cb66b4c-31d8-40dc-8c5b-9d243c2bb974 pressure 100150.203125 2019-10-11T11:51:26.754+02:00 2019-10-11T11:51:26.754+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.757+02:00 null
11 6e626fdb-326d-4984-9585-7b64976be1a2 pressure 100150.5 2019-10-11T11:51:26.894+02:00 2019-10-11T11:51:26.894+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.896+02:00 null
12 6869ae37-0200-40c2-85a5-d4412d81b62d pressure 100154.1015625 2019-10-11T11:51:27.029+02:00 2019-10-11T11:51:27.029+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:27.032+02:00 null
13 8a0770b9-f8b0-42de-90fe-3e6a6311818b pressure 100152.3984375 2019-10-11T11:51:28.169+02:00 2019-10-11T11:51:28.169+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.171+02:00 null
14 cf989dbb-2e73-4042-b339-b4dbc75b0a7c pressure 100151.3984375 2019-10-11T11:51:28.294+02:00 2019-10-11T11:51:28.294+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.296+02:00 null
15 53a2d8fb-3b15-48a2-a277-1b821adba088 pressure 100151.1015625 2019-10-11T11:51:28.426+02:00 2019-10-11T11:51:28.426+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.428+02:00 null
16 9de16913-67f3-4e0b-9976-ddadbd057bae pressure 100153.8984375 2019-10-11T11:51:29.563+02:00 2019-10-11T11:51:29.563+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:29.565+02:00 null
17 d8c255a5-c5bd-4e9e-9aec-d0e6ae795719 pressure 100154.6015625 2019-10-11T11:51:30.707+02:00 2019-10-11T11:51:30.707+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:30.71+02:00 null
18 31ab31ac-e991-4081-8b4e-eba0f789b806 pressure 100153.796875 2019-10-11T11:51:31.843+02:00 2019-10-11T11:51:31.843+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:31.846+02:00 null
19 ed2ec0a9-9bc1-424a-9edf-85aad7a059ec pressure 100150.703125 2019-10-11T11:51:32.981+02:00 2019-10-11T11:51:32.981+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:32.984+02:00 null
20 31e03c4c-49a1-4c0d-a750-16c09b75f96b pressure 100151.203125 2019-10-11T11:51:34.125+02:00 2019-10-11T11:51:34.126+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:34.133+02:00 null

View File

@ -1,20 +0,0 @@
140d6183-36c8-4903-a798-def4fcb3bd74,pressure,100150.5,2019-10-11T11:51:20.98+02:00,2019-10-11T11:51:20.98+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:20.982+02:00,null
98102a36-a38e-4ccc-9886-c119a7971414,pressure,100151.5,2019-10-11T11:51:22.123+02:00,2019-10-11T11:51:22.123+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:22.125+02:00,null
5ffe51ae-687c-47df-8936-7785de9beed7,pressure,100151,2019-10-11T11:51:23.261+02:00,2019-10-11T11:51:23.262+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:23.263+02:00,null
e534cb23-c725-403b-b5c9-5455256a8662,pressure,100153,2019-10-11T11:51:23.739+02:00,2019-10-11T11:51:23.739+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.594+02:00,null
e3c2a10b-373f-43d5-b02c-b381d1ad203b,pressure,100149.5,2019-10-11T11:51:24.721+02:00,2019-10-11T11:51:24.721+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:24.723+02:00,null
e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94,pressure,100151,2019-10-11T11:51:24.849+02:00,2019-10-11T11:51:24.849+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:25.212+02:00,null
70f6de4d-3a17-4688-b635-b8965e85be43,pressure,100149.5,2019-10-11T11:51:26.352+02:00,2019-10-11T11:51:26.352+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.354+02:00,null
0e42478b-da36-4096-afad-d8c2a7685ccd,pressure,100149.5,2019-10-11T11:51:26.488+02:00,2019-10-11T11:51:26.488+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.49+02:00,null
0313994c-db52-42d6-b059-2ca13afdd5d8,pressure,100151.5,2019-10-11T11:51:26.622+02:00,2019-10-11T11:51:26.622+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.624+02:00,null
9cb66b4c-31d8-40dc-8c5b-9d243c2bb974,pressure,100150,2019-10-11T11:51:26.754+02:00,2019-10-11T11:51:26.754+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.757+02:00,null
6e626fdb-326d-4984-9585-7b64976be1a2,pressure,100150.5,2019-10-11T11:51:26.894+02:00,2019-10-11T11:51:26.894+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:26.896+02:00,null
6869ae37-0200-40c2-85a5-d4412d81b62d,pressure,100154,2019-10-11T11:51:27.029+02:00,2019-10-11T11:51:27.029+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:27.032+02:00,null
8a0770b9-f8b0-42de-90fe-3e6a6311818b,pressure,100152.5,2019-10-11T11:51:28.169+02:00,2019-10-11T11:51:28.169+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.171+02:00,null
cf989dbb-2e73-4042-b339-b4dbc75b0a7c,pressure,100151.5,2019-10-11T11:51:28.294+02:00,2019-10-11T11:51:28.294+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.296+02:00,null
53a2d8fb-3b15-48a2-a277-1b821adba088,pressure,100151,2019-10-11T11:51:28.426+02:00,2019-10-11T11:51:28.426+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:28.428+02:00,null
9de16913-67f3-4e0b-9976-ddadbd057bae,pressure,100154,2019-10-11T11:51:29.563+02:00,2019-10-11T11:51:29.563+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:29.565+02:00,null
d8c255a5-c5bd-4e9e-9aec-d0e6ae795719,pressure,100154.5,2019-10-11T11:51:30.707+02:00,2019-10-11T11:51:30.707+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:30.71+02:00,null
31ab31ac-e991-4081-8b4e-eba0f789b806,pressure,100154,2019-10-11T11:51:31.843+02:00,2019-10-11T11:51:31.843+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:31.846+02:00,null
ed2ec0a9-9bc1-424a-9edf-85aad7a059ec,pressure,100150.5,2019-10-11T11:51:32.981+02:00,2019-10-11T11:51:32.981+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:32.984+02:00,null
31e03c4c-49a1-4c0d-a750-16c09b75f96b,pressure,100151,2019-10-11T11:51:34.125+02:00,2019-10-11T11:51:34.126+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:34.133+02:00,null
1 140d6183-36c8-4903-a798-def4fcb3bd74 pressure 100150.5 2019-10-11T11:51:20.98+02:00 2019-10-11T11:51:20.98+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:20.982+02:00 null
2 98102a36-a38e-4ccc-9886-c119a7971414 pressure 100151.5 2019-10-11T11:51:22.123+02:00 2019-10-11T11:51:22.123+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:22.125+02:00 null
3 5ffe51ae-687c-47df-8936-7785de9beed7 pressure 100151 2019-10-11T11:51:23.261+02:00 2019-10-11T11:51:23.262+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:23.263+02:00 null
4 e534cb23-c725-403b-b5c9-5455256a8662 pressure 100153 2019-10-11T11:51:23.739+02:00 2019-10-11T11:51:23.739+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.594+02:00 null
5 e3c2a10b-373f-43d5-b02c-b381d1ad203b pressure 100149.5 2019-10-11T11:51:24.721+02:00 2019-10-11T11:51:24.721+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:24.723+02:00 null
6 e9fbdd79-10ca-4fda-aed4-6fa2dbf3ff94 pressure 100151 2019-10-11T11:51:24.849+02:00 2019-10-11T11:51:24.849+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:25.212+02:00 null
7 70f6de4d-3a17-4688-b635-b8965e85be43 pressure 100149.5 2019-10-11T11:51:26.352+02:00 2019-10-11T11:51:26.352+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.354+02:00 null
8 0e42478b-da36-4096-afad-d8c2a7685ccd pressure 100149.5 2019-10-11T11:51:26.488+02:00 2019-10-11T11:51:26.488+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.49+02:00 null
9 0313994c-db52-42d6-b059-2ca13afdd5d8 pressure 100151.5 2019-10-11T11:51:26.622+02:00 2019-10-11T11:51:26.622+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.624+02:00 null
10 9cb66b4c-31d8-40dc-8c5b-9d243c2bb974 pressure 100150 2019-10-11T11:51:26.754+02:00 2019-10-11T11:51:26.754+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.757+02:00 null
11 6e626fdb-326d-4984-9585-7b64976be1a2 pressure 100150.5 2019-10-11T11:51:26.894+02:00 2019-10-11T11:51:26.894+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:26.896+02:00 null
12 6869ae37-0200-40c2-85a5-d4412d81b62d pressure 100154 2019-10-11T11:51:27.029+02:00 2019-10-11T11:51:27.029+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:27.032+02:00 null
13 8a0770b9-f8b0-42de-90fe-3e6a6311818b pressure 100152.5 2019-10-11T11:51:28.169+02:00 2019-10-11T11:51:28.169+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.171+02:00 null
14 cf989dbb-2e73-4042-b339-b4dbc75b0a7c pressure 100151.5 2019-10-11T11:51:28.294+02:00 2019-10-11T11:51:28.294+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.296+02:00 null
15 53a2d8fb-3b15-48a2-a277-1b821adba088 pressure 100151 2019-10-11T11:51:28.426+02:00 2019-10-11T11:51:28.426+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:28.428+02:00 null
16 9de16913-67f3-4e0b-9976-ddadbd057bae pressure 100154 2019-10-11T11:51:29.563+02:00 2019-10-11T11:51:29.563+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:29.565+02:00 null
17 d8c255a5-c5bd-4e9e-9aec-d0e6ae795719 pressure 100154.5 2019-10-11T11:51:30.707+02:00 2019-10-11T11:51:30.707+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:30.71+02:00 null
18 31ab31ac-e991-4081-8b4e-eba0f789b806 pressure 100154 2019-10-11T11:51:31.843+02:00 2019-10-11T11:51:31.843+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:31.846+02:00 null
19 ed2ec0a9-9bc1-424a-9edf-85aad7a059ec pressure 100150.5 2019-10-11T11:51:32.981+02:00 2019-10-11T11:51:32.981+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:32.984+02:00 null
20 31e03c4c-49a1-4c0d-a750-16c09b75f96b pressure 100151 2019-10-11T11:51:34.125+02:00 2019-10-11T11:51:34.126+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:34.133+02:00 null

View File

@ -1,12 +0,0 @@
2029cc8c-56cf-4cd1-adf8-06e768fbad29,temperature,20.90999984741211,2019-10-11T11:50:43.317+02:00,2019-10-11T11:50:43.317+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:43.319+02:00,null
a588cc90-4dca-4a4e-be81-2d20b9f64458,temperature,20.920000076293945,2019-10-11T11:50:44.459+02:00,2019-10-11T11:50:47.745+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:44.473+02:00,2019-10-11T11:51:01.71+02:00
88f5c095-8e88-4c80-b66a-cf154919afa8,temperature,20.93000030517578,2019-10-11T11:50:49.53+02:00,2019-10-11T11:50:49.53+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:50.018+02:00,null
7c3b7eac-3b43-4461-b204-d74c2bde3a08,temperature,20.920000076293945,2019-10-11T11:50:51.187+02:00,2019-10-11T11:50:53.652+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:51.189+02:00,2019-10-11T11:51:01.71+02:00
acb55d68-d909-41ba-a445-8ef9eab0c7aa,temperature,20.950000762939453,2019-10-11T11:50:53.788+02:00,2019-10-11T11:50:53.789+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:53.79+02:00,null
6563ff1e-371b-46a2-aca8-cc49b418bf61,temperature,20.93000030517578,2019-10-11T11:50:54.929+02:00,2019-10-11T11:50:55.536+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:55.405+02:00,2019-10-11T11:51:01.71+02:00
157eda79-3283-4cda-a21d-db0aa2da8548,temperature,20.959999084472656,2019-10-11T11:50:55.674+02:00,2019-10-11T11:50:55.674+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:55.677+02:00,null
c7d8d6ce-59d9-4448-9934-69cebc071cf6,temperature,20.940000534057617,2019-10-11T11:50:56.817+02:00,2019-10-11T11:50:56.817+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:56.82+02:00,null
07d08037-7267-44ab-8d90-878271809a4c,temperature,20.93000030517578,2019-10-11T11:50:57.959+02:00,2019-10-11T11:50:59.1+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:57.962+02:00,2019-10-11T11:51:01.71+02:00
0b865113-d0f4-42f4-af3d-50e60edcc523,temperature,20.959999084472656,2019-10-11T11:50:59.238+02:00,2019-10-11T11:50:59.238+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:59.241+02:00,null
c40ad502-b6f1-49d8-8b6d-e7d5e475a6e2,temperature,20.979999542236328,2019-10-11T11:50:59.368+02:00,2019-10-11T11:50:59.368+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:59.371+02:00,null
8d98e723-7a07-4ccf-b3bc-7a2653952b8b,temperature,20.920000076293945,2019-10-11T11:51:00.509+02:00,2019-10-11T11:51:01.658+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:00.516+02:00,2019-10-11T11:51:01.71+02:00
1 2029cc8c-56cf-4cd1-adf8-06e768fbad29 temperature 20.90999984741211 2019-10-11T11:50:43.317+02:00 2019-10-11T11:50:43.317+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:43.319+02:00 null
2 a588cc90-4dca-4a4e-be81-2d20b9f64458 temperature 20.920000076293945 2019-10-11T11:50:44.459+02:00 2019-10-11T11:50:47.745+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:44.473+02:00 2019-10-11T11:51:01.71+02:00
3 88f5c095-8e88-4c80-b66a-cf154919afa8 temperature 20.93000030517578 2019-10-11T11:50:49.53+02:00 2019-10-11T11:50:49.53+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:50.018+02:00 null
4 7c3b7eac-3b43-4461-b204-d74c2bde3a08 temperature 20.920000076293945 2019-10-11T11:50:51.187+02:00 2019-10-11T11:50:53.652+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:51.189+02:00 2019-10-11T11:51:01.71+02:00
5 acb55d68-d909-41ba-a445-8ef9eab0c7aa temperature 20.950000762939453 2019-10-11T11:50:53.788+02:00 2019-10-11T11:50:53.789+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:53.79+02:00 null
6 6563ff1e-371b-46a2-aca8-cc49b418bf61 temperature 20.93000030517578 2019-10-11T11:50:54.929+02:00 2019-10-11T11:50:55.536+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:55.405+02:00 2019-10-11T11:51:01.71+02:00
7 157eda79-3283-4cda-a21d-db0aa2da8548 temperature 20.959999084472656 2019-10-11T11:50:55.674+02:00 2019-10-11T11:50:55.674+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:55.677+02:00 null
8 c7d8d6ce-59d9-4448-9934-69cebc071cf6 temperature 20.940000534057617 2019-10-11T11:50:56.817+02:00 2019-10-11T11:50:56.817+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:56.82+02:00 null
9 07d08037-7267-44ab-8d90-878271809a4c temperature 20.93000030517578 2019-10-11T11:50:57.959+02:00 2019-10-11T11:50:59.1+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:57.962+02:00 2019-10-11T11:51:01.71+02:00
10 0b865113-d0f4-42f4-af3d-50e60edcc523 temperature 20.959999084472656 2019-10-11T11:50:59.238+02:00 2019-10-11T11:50:59.238+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:59.241+02:00 null
11 c40ad502-b6f1-49d8-8b6d-e7d5e475a6e2 temperature 20.979999542236328 2019-10-11T11:50:59.368+02:00 2019-10-11T11:50:59.368+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:59.371+02:00 null
12 8d98e723-7a07-4ccf-b3bc-7a2653952b8b temperature 20.920000076293945 2019-10-11T11:51:00.509+02:00 2019-10-11T11:51:01.658+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:00.516+02:00 2019-10-11T11:51:01.71+02:00

View File

@ -1 +0,0 @@
2029cc8c-56cf-4cd1-adf8-06e768fbad29,temperature,21,2019-10-11T11:50:43.317+02:00,2019-10-11T11:51:01.658+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:43.319+02:00,2019-10-11T11:51:01.735+02:00
1 2029cc8c-56cf-4cd1-adf8-06e768fbad29 temperature 21 2019-10-11T11:50:43.317+02:00 2019-10-11T11:51:01.658+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:43.319+02:00 2019-10-11T11:51:01.735+02:00

View File

@ -1,20 +0,0 @@
2029cc8c-56cf-4cd1-adf8-06e768fbad29,temperature,20.90999984741211,2019-10-11T11:50:43.317+02:00,2019-10-11T11:50:43.317+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:43.319+02:00,null
a588cc90-4dca-4a4e-be81-2d20b9f64458,temperature,20.920000076293945,2019-10-11T11:50:44.459+02:00,2019-10-11T11:50:44.459+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:44.473+02:00,null
1ebb60de-6066-4731-9f55-ee1c51748b7f,temperature,20.920000076293945,2019-10-11T11:50:45.614+02:00,2019-10-11T11:50:45.614+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:45.615+02:00,null
6cbc586f-a5c9-4296-b3b2-200afae12059,temperature,20.920000076293945,2019-10-11T11:50:46.776+02:00,2019-10-11T11:50:46.776+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:47.134+02:00,null
14e03e32-f214-4f61-97b0-8e021c47bead,temperature,20.920000076293945,2019-10-11T11:50:47.745+02:00,2019-10-11T11:50:47.745+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:47.747+02:00,null
88f5c095-8e88-4c80-b66a-cf154919afa8,temperature,20.93000030517578,2019-10-11T11:50:49.53+02:00,2019-10-11T11:50:49.53+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:50.018+02:00,null
7c3b7eac-3b43-4461-b204-d74c2bde3a08,temperature,20.920000076293945,2019-10-11T11:50:51.187+02:00,2019-10-11T11:50:51.187+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:51.189+02:00,null
3caf0b08-38d5-4536-baa5-86d4d75e359d,temperature,20.920000076293945,2019-10-11T11:50:52.508+02:00,2019-10-11T11:50:52.508+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:52.51+02:00,null
4b8595bf-3624-4963-b796-efc648515cd9,temperature,20.920000076293945,2019-10-11T11:50:53.652+02:00,2019-10-11T11:50:53.652+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:53.654+02:00,null
acb55d68-d909-41ba-a445-8ef9eab0c7aa,temperature,20.950000762939453,2019-10-11T11:50:53.788+02:00,2019-10-11T11:50:53.789+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:53.79+02:00,null
6563ff1e-371b-46a2-aca8-cc49b418bf61,temperature,20.93000030517578,2019-10-11T11:50:54.929+02:00,2019-10-11T11:50:54.93+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:55.405+02:00,null
a64f57c4-b95c-45b1-b78a-a6be263abb85,temperature,20.93000030517578,2019-10-11T11:50:55.536+02:00,2019-10-11T11:50:55.536+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:55.538+02:00,null
157eda79-3283-4cda-a21d-db0aa2da8548,temperature,20.959999084472656,2019-10-11T11:50:55.674+02:00,2019-10-11T11:50:55.674+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:55.677+02:00,null
c7d8d6ce-59d9-4448-9934-69cebc071cf6,temperature,20.940000534057617,2019-10-11T11:50:56.817+02:00,2019-10-11T11:50:56.817+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:56.82+02:00,null
07d08037-7267-44ab-8d90-878271809a4c,temperature,20.93000030517578,2019-10-11T11:50:57.959+02:00,2019-10-11T11:50:57.96+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:57.962+02:00,null
bf2e981f-8bad-466e-9a12-c1a5d9b64399,temperature,20.93000030517578,2019-10-11T11:50:59.1+02:00,2019-10-11T11:50:59.1+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:59.103+02:00,null
0b865113-d0f4-42f4-af3d-50e60edcc523,temperature,20.959999084472656,2019-10-11T11:50:59.238+02:00,2019-10-11T11:50:59.238+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:59.241+02:00,null
c40ad502-b6f1-49d8-8b6d-e7d5e475a6e2,temperature,20.979999542236328,2019-10-11T11:50:59.368+02:00,2019-10-11T11:50:59.368+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:50:59.371+02:00,null
8d98e723-7a07-4ccf-b3bc-7a2653952b8b,temperature,20.920000076293945,2019-10-11T11:51:00.509+02:00,2019-10-11T11:51:00.51+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:00.516+02:00,null
f61b7bfb-14eb-4b43-a7b6-47b61fa46dda,temperature,20.920000076293945,2019-10-11T11:51:01.658+02:00,2019-10-11T11:51:01.658+02:00,d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6,2019-10-11T11:51:01.661+02:00,null
1 2029cc8c-56cf-4cd1-adf8-06e768fbad29 temperature 20.90999984741211 2019-10-11T11:50:43.317+02:00 2019-10-11T11:50:43.317+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:43.319+02:00 null
2 a588cc90-4dca-4a4e-be81-2d20b9f64458 temperature 20.920000076293945 2019-10-11T11:50:44.459+02:00 2019-10-11T11:50:44.459+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:44.473+02:00 null
3 1ebb60de-6066-4731-9f55-ee1c51748b7f temperature 20.920000076293945 2019-10-11T11:50:45.614+02:00 2019-10-11T11:50:45.614+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:45.615+02:00 null
4 6cbc586f-a5c9-4296-b3b2-200afae12059 temperature 20.920000076293945 2019-10-11T11:50:46.776+02:00 2019-10-11T11:50:46.776+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:47.134+02:00 null
5 14e03e32-f214-4f61-97b0-8e021c47bead temperature 20.920000076293945 2019-10-11T11:50:47.745+02:00 2019-10-11T11:50:47.745+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:47.747+02:00 null
6 88f5c095-8e88-4c80-b66a-cf154919afa8 temperature 20.93000030517578 2019-10-11T11:50:49.53+02:00 2019-10-11T11:50:49.53+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:50.018+02:00 null
7 7c3b7eac-3b43-4461-b204-d74c2bde3a08 temperature 20.920000076293945 2019-10-11T11:50:51.187+02:00 2019-10-11T11:50:51.187+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:51.189+02:00 null
8 3caf0b08-38d5-4536-baa5-86d4d75e359d temperature 20.920000076293945 2019-10-11T11:50:52.508+02:00 2019-10-11T11:50:52.508+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:52.51+02:00 null
9 4b8595bf-3624-4963-b796-efc648515cd9 temperature 20.920000076293945 2019-10-11T11:50:53.652+02:00 2019-10-11T11:50:53.652+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:53.654+02:00 null
10 acb55d68-d909-41ba-a445-8ef9eab0c7aa temperature 20.950000762939453 2019-10-11T11:50:53.788+02:00 2019-10-11T11:50:53.789+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:53.79+02:00 null
11 6563ff1e-371b-46a2-aca8-cc49b418bf61 temperature 20.93000030517578 2019-10-11T11:50:54.929+02:00 2019-10-11T11:50:54.93+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:55.405+02:00 null
12 a64f57c4-b95c-45b1-b78a-a6be263abb85 temperature 20.93000030517578 2019-10-11T11:50:55.536+02:00 2019-10-11T11:50:55.536+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:55.538+02:00 null
13 157eda79-3283-4cda-a21d-db0aa2da8548 temperature 20.959999084472656 2019-10-11T11:50:55.674+02:00 2019-10-11T11:50:55.674+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:55.677+02:00 null
14 c7d8d6ce-59d9-4448-9934-69cebc071cf6 temperature 20.940000534057617 2019-10-11T11:50:56.817+02:00 2019-10-11T11:50:56.817+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:56.82+02:00 null
15 07d08037-7267-44ab-8d90-878271809a4c temperature 20.93000030517578 2019-10-11T11:50:57.959+02:00 2019-10-11T11:50:57.96+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:57.962+02:00 null
16 bf2e981f-8bad-466e-9a12-c1a5d9b64399 temperature 20.93000030517578 2019-10-11T11:50:59.1+02:00 2019-10-11T11:50:59.1+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:59.103+02:00 null
17 0b865113-d0f4-42f4-af3d-50e60edcc523 temperature 20.959999084472656 2019-10-11T11:50:59.238+02:00 2019-10-11T11:50:59.238+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:59.241+02:00 null
18 c40ad502-b6f1-49d8-8b6d-e7d5e475a6e2 temperature 20.979999542236328 2019-10-11T11:50:59.368+02:00 2019-10-11T11:50:59.368+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:50:59.371+02:00 null
19 8d98e723-7a07-4ccf-b3bc-7a2653952b8b temperature 20.920000076293945 2019-10-11T11:51:00.509+02:00 2019-10-11T11:51:00.51+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:00.516+02:00 null
20 f61b7bfb-14eb-4b43-a7b6-47b61fa46dda temperature 20.920000076293945 2019-10-11T11:51:01.658+02:00 2019-10-11T11:51:01.658+02:00 d7fc0e1f-9d5a-45c7-bc01-e85bd1b610c6 2019-10-11T11:51:01.661+02:00 null

Some files were not shown because too many files have changed in this diff Show More