Compare commits

...

No commits in common. "v0.1.0" and "master" have entirely different histories.

114 changed files with 91 additions and 7062 deletions

16
.SRCINFO Normal file
View File

@ -0,0 +1,16 @@
pkgbase = flucky
pkgdesc = A lightweight golang program to read values from different sensors
pkgver = 0.3.2
pkgrel = 2
url = https://git.cryptic.systems/flucky/flucky
arch = x86_64
arch = armv7h
license = Apache 2.0
makedepends = git
makedepends = go
makedepends = make
source = https://git.cryptic.systems/flucky/flucky/archive/v0.3.2.tar.gz
sha512sums = 1e53256f0b45c0e887b224ca697b3c1fadfb4135399595cbfee758f8f6c2357471da287a816d7fd0924adb96bc43676adf3149b0dd6bfa3f1c6c6947fcc6c760
pkgname = flucky

55
.drone.yml Normal file
View File

@ -0,0 +1,55 @@
---
kind: pipeline
type: kubernetes
name: build
node_selector:
kubernetes.io/os: linux
kubernetes.io/arch: amd64
steps:
- name: build-pkg
commands:
- makepkg --sign
environment:
MAKEPKG_PACKAGER: "csrbot@cryptic.systems"
MAKEPKG_PKGEXT: ".pkg.tar.zst"
MAKEPKG_SRCEXT: ".src.tar.zst"
image: docker.io/volkerraschek/build-image:latest
trigger:
event:
include:
- pull_request
- push
exclude:
- tag
---
kind: pipeline
type: kubernetes
name: deploy
node_selector:
kubernetes.io/os: linux
kubernetes.io/arch: amd64
steps:
- name: build-pkg
commands:
- makepkg --sign
- scp *.pkg.tar.zst* ${AUR_SERVER}:/${AUR_PATH}
environment:
MAKEPKG_PACKAGER: "csrbot@cryptic.systems"
MAKEPKG_PKGEXT: ".pkg.tar.zst"
MAKEPKG_SRCEXT: ".src.tar.zst"
SSH_KEY:
from_secret:
ssh_key
image: docker.io/volkerraschek/build-image:latest
trigger:
event:
include:
- pull_request
- push

View File

@ -1,12 +0,0 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = false
[Makefile]
indent_style = tab

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

1
.gitattributes vendored
View File

@ -1 +0,0 @@
Makefile eol=lf

17
.gitignore vendored
View File

@ -1,13 +1,4 @@
# absolute files
.env
flucky
flucky.rpm
flucky.tar.bz2
flucky.tar.gz
flucky.tar.xz
# relative files
**/bindata*.go
# directories
.vscode/
*
!.drone.yml
!.gitignore
!PKGBUILD

View File

@ -1,22 +0,0 @@
language: go
services:
- docker
jobs:
include:
- stage: build
name: go-build
script: make container/all
deploy:
- provider: script
script: make container/release
# skip_cleanup: true
on:
tags: true
notifications:
email:
on_success: change
on_failure: change

13
LICENSE
View File

@ -1,13 +0,0 @@
Copyright 2019 Markus Pesch
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

140
Makefile
View File

@ -1,140 +0,0 @@
# UID/GID
# UID or GID is the UNIX user ID or group ID of the user who executes
# make. If the UID or GID is not passed as a make variable, an attempt
# is made to determine it.
UID?=$(shell id --user)
GID?=$(shell id --group)
# VERSION
# If no version is specified as a parameter of make, the last git hash
# value is taken.
VERSION:=$(or ${TRAVIS_TAG}, $(shell git rev-parse --short HEAD)-git)
# CONTAINER_RUNTIME
CONTAINER_RUNTIME?=$(shell which docker)
# BUILD_IMAGE
BUILD_IMAGE:=volkerraschek/build-image:latest
# GH_USER/GITHUB_TOKEN
# It's the user name from github.com and his token. This token and the username
# can be set over encrypted environment variables in the ci/cd pipeline
GITHUB_USER=volker-raschek
GITHUB_TOKEN?=""
# EXECUTABLE
# Executable binary which should be compiled for different architecures
EXECUTABLE:=flucky
# UNIX_EXECUTABLES
# Define all executables for different architectures and operation systems
UNIX_EXECUTABLES := \
darwin/amd64/$(EXECUTABLE) \
freebsd/amd64/$(EXECUTABLE) \
linux/amd64/$(EXECUTABLE) \
linux/arm/5/$(EXECUTABLE) \
linux/arm/7/$(EXECUTABLE) \
# EXECUTABLE_TARGETS
# Include the relative paths to all executables
EXECUTABLE_TARGETS=$(UNIX_EXECUTABLES:%=bin/%)
# COMPRSSED_EXECUTABLES
# Append to all defined executables the compression extentions to detect the
# different make steps which compress the binary
COMPRESSED_EXECUTABLES=$(UNIX_EXECUTABLES:%=%.tar.bz2) $(UNIX_EXECUTABLES:%=%.tar.gz) $(UNIX_EXECUTABLES:%=%.tar.xz)
COMPRESSED_EXECUTABLE_TARGETS=$(COMPRESSED_EXECUTABLES:%=bin/%)
# PHONY
# To avoid a conflict with an existing file and a defined make step
.PHONY: clean container/${EXECUTABLE_TARGETS} container/test release remote test
all: ${EXECUTABLE}
$(EXECUTABLE): bindata
go build -ldflags "-X main.version=${VERSION}" -o "$@"
bin/tmp/${EXECUTABLE}: bindata
go build -ldflags "-X main.version=${VERSION}" -o "$@"
# arm
bin/linux/arm/5/${EXECUTABLE}: bindata
GOARM=5 GOARCH=arm go build -ldflags "-X main.version=${VERSION}" -o "$@"
bin/linux/arm/7/${EXECUTABLE}: bindata
GOARM=7 GOARCH=arm go build -ldflags "-X main.version=${VERSION}" -o "$@"
# 386
bin/darwin/386/$(EXECUTABLE): bindata
GOARCH=386 GOOS=darwin go build -ldflags "-X main.version=${VERSION}" -o "$@"
bin/linux/386/$(EXECUTABLE): bindata
GOARCH=386 GOOS=linux go build -ldflags "-X main.version=${VERSION}" -o "$@"
# amd64
bin/freebsd/amd64/$(EXECUTABLE): bindata
GOARCH=amd64 GOOS=freebsd go build -ldflags "-X main.version=${VERSION}" -o "$@"
bin/darwin/amd64/$(EXECUTABLE): bindata
GOARCH=amd64 GOOS=darwin go build -ldflags "-X main.version=${VERSION}" -o "$@"
bin/linux/amd64/$(EXECUTABLE): bindata
GOARCH=amd64 GOOS=linux go build -ldflags "-X main.version=${VERSION}" -o "$@"
bindata:
go-bindata -pkg db -o ./pkg/db/bindataSQL.go ./pkg/db/sql/***
go-bindata -pkg goldenfiles -o ./test/goldenfiles/bindata.go ./test/goldenfiles/json/***
%.tar.bz2: %
tar --create --bzip2 --file "$@" "$<"
%.tar.gz: %
tar --create --gunzip --file "$@" "$<"
%.tar.xz: %
tar --create --xz --file "$@" "$<"
test: ${EXECUTABLE}
go test -v ./pkg/...
release: clean
$(MAKE) $(COMPRESSED_EXECUTABLE_TARGETS)
github-release release \
--user ${GITHUB_USER} \
--repo ${EXECUTABLE} \
--tag ${VERSION}
$(foreach FILE,$(COMPRESSED_EXECUTABLES),github-release upload --user ${GITHUB_USER} --repo ${EXECUTABLE} --tag ${VERSION} --name $(subst /,-,$(FILE)) --file bin/$(FILE) --replace;)
clean:
rm ${EXECUTABLE} || true
rm --recursive --force bin/ || true
container/all:
$(MAKE) container-run COMMAND=all
container/test:
$(MAKE) container-run COMMAND=test
container/release:
$(MAKE) container-run COMMAND=release
container-run:
${CONTAINER_RUNTIME} run \
--rm \
--volume ${PWD}:/workspace \
${BUILD_IMAGE} \
make ${COMMAND} \
UID=${UID} \
GID=${GID} \
VERSION=${VERSION} \
GITHUB_TOKEN=${GITHUB_TOKEN}
remote: bin/linux/arm/7/${EXECUTABLE}
scp bin/linux/arm/7/${EXECUTABLE} ${FLUCKY_REMOTE}:/usr/local/bin
ssh ${FLUCKY_REMOTE} "chmod +x /usr/local/bin/flucky"

16
PKGBUILD Normal file
View File

@ -0,0 +1,16 @@
# Maintainer: Markus Pesch <markus.pesch@cryptic.systems>
pkgname=flucky
pkgver=0.3.2
pkgrel=2
pkgdesc='A lightweight golang program to read values from different sensors'
arch=('x86_64' 'armv7h')
url=https://git.cryptic.systems/flucky/flucky
license=('Apache 2.0')
makedepends=('git' 'go' 'make')
source=("https://git.cryptic.systems/flucky/flucky/archive/v${pkgver}.tar.gz")
sha512sums=('1e53256f0b45c0e887b224ca697b3c1fadfb4135399595cbfee758f8f6c2357471da287a816d7fd0924adb96bc43676adf3149b0dd6bfa3f1c6c6947fcc6c760')
package() {
make --directory ${srcdir}/${pkgname} DESTDIR=${pkgdir} PREFIX=/usr VERSION=${pkgver} install
}

View File

@ -1,91 +0,0 @@
# flucky
[![Build Status](https://travis-ci.com/go-flucky/flucky.svg?branch=master)](https://travis-ci.com/go-flucky/flucky)
[![Go Report Card](https://goreportcard.com/badge/github.com/volker-raschek/flucky)](https://goreportcard.com/report/github.com/volker-raschek/flucky)
Flucky is a lightweight program written in go for reading data from sensors, for
example with a banana or raspberry pi. In addition, flucky provides a REST-API
to receive from other flucky installations measured values. The measured values
can be saved in a logfile or into a database. Currently is only postgres
supported.
## Installation
Flucky can be installed over multiple ways, for example with your local package
manager from your linux distribution. At the moment it's only the rpm package
format supported. Alternatively the source code can be compiled to get the
flucky binary.
### RPM-Package
To install flucky over a RPM package you can download a specific version from
github.com or configure the RPM-Mirror.
#### RPM-Mirror
```bash
$ cat > /etc/yum.repos.d/flucky.rpm <<EOF
[flucky]
name=flucky
baseurl=http://rpm-mirror.cryptic.systems
gpgcheck=0
EOF
$ yum update && yum install --assumeyes flucky
```
#### Download github.com
```bash
$ VERSION=v0.1.0 \
curl --location https://github.com/volker-raschek/flucky/releases/${VERSION}/flucky.rpm -O flucky-${VERSION}.rpm
$ yum install --assumeyes ./flucky-${VERSION}.rpm
```
### Compiling the source code
An additional ways to install flucky is to compile the source code. There are
two different ways to compile flucky from scratch. The easier ways is
to use the pre-defined container image in the Makefile, which has included all
dependancies to compile flucky. Alternatively, if all dependencies are met,
flucky can also be compiled without the container image. Both variants are
briefly described.
#### Compiling the source code via container image
To compile flucky via container image it's necessary, that a container runtime
is installed. In the Makefile is predefined docker, but it's can be also used
podman. Execute `make container-go-build` to start the compiling process.
```bash
$ make container-go-build
make container-run COMMAND=go-build
make[1]: Directory „/home/markus/workspace/flucky“ is entered
/usr/bin/docker run \
--rm \
--volume /home/markus/workspace/flucky:/workspace \
volkerraschek/build-image:latest \
make go-build \
UID=1000 \
GID=1000 \
VERSION=60ee044-git \
GOOS=linux \
GOARCH=amd64
go-bindata -pkg db -o ./pkg/db/bindataSQL.go ./pkg/db/sql/***
go-bindata -pkg goldenfiles -o ./test/goldenfiles/bindata.go ./test/goldenfiles/json/***
GOOS=linux \
GOARCH=amd64 \
go build -ldflags "-X main.version=60ee044-git"
go: finding github.com/spf13/pflag v1.0.3
go: finding github.com/satori/go.uuid v1.2.0
...
```
#### Compiling the source code without container image
Make sure you have installed go >= v1.12. Execute `make go-build` to compile
flucky without a container-image. There should be a similar output as when
compiling flucky via the container image.
## Enhancements
+ Provide flucky additionally as AUR and deb package

View File

@ -1,80 +0,0 @@
package cmd
import (
"fmt"
"math"
"os"
"time"
"github.com/go-flucky/flucky/cmd/compression"
"github.com/go-flucky/flucky/cmd/convert"
"github.com/go-flucky/flucky/cmd/daemon"
"github.com/go-flucky/flucky/cmd/humidity"
"github.com/go-flucky/flucky/cmd/pressure"
"github.com/go-flucky/flucky/cmd/rgbled"
"github.com/go-flucky/flucky/cmd/sensor"
"github.com/go-flucky/flucky/cmd/temperature"
"github.com/go-flucky/flucky/pkg/types"
"github.com/go-flucky/flucky/pkg/config"
uuid "github.com/satori/go.uuid"
"github.com/spf13/cobra"
)
var configFile string
var rootCmd = &cobra.Command{
Use: "flucky",
Short: "flucky - operate with differen sensors, his values and remote servers to synchronize measured values",
PersistentPreRunE: func(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
t := time.Now()
l, _ := time.LoadLocation("Europe/Berlin")
t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), int(math.Round(float64(t.Nanosecond())/1000000)*1000000), l)
cnf := config.Configuration{
Device: &types.Device{
DeviceID: uuid.NewV4().String(),
DeviceName: hostname,
Logfile: "/var/log/flucky/logfile.csv",
CreationDate: t,
},
}
err = config.Write(&cnf, configFile)
if err != nil {
return err
}
}
return nil
},
}
// Execute a
func Execute(version string) {
rootCmd.Version = version
rootCmd.PersistentFlags().StringVar(&configFile, "config", "/etc/flucky/config.json", "Config file")
compression.InitCmd(rootCmd, &configFile)
convert.InitCmd(rootCmd, &configFile)
daemon.InitCmd(rootCmd, &configFile)
// db.InitCmd(rootCmd, &configFile)
humidity.InitCmd(rootCmd, &configFile)
pressure.InitCmd(rootCmd, &configFile)
rgbled.InitCmd(rootCmd, &configFile)
sensor.InitCmd(rootCmd, &configFile)
temperature.InitCmd(rootCmd, &configFile)
rootCmd.Execute()
}

View File

@ -1,40 +0,0 @@
package compression
import (
"log"
"github.com/go-flucky/flucky/pkg/logfile"
"github.com/spf13/cobra"
)
var compression bool
var configFile *string
var compressionCmd = &cobra.Command{
Use: "compression",
Short: "Compress a logfile",
Args: cobra.ExactArgs(1),
Example: "flucky compression /var/log/flucky/logfile.csv",
Run: func(cmd *cobra.Command, args []string) {
measuredValueLogfile := logfile.New(args[0])
measuredValues, err := measuredValueLogfile.Read()
if err != nil {
log.Fatalln(err)
}
measuredValues = logfile.Compression(measuredValues)
err = measuredValueLogfile.Write(measuredValues)
if err != nil {
log.Fatalln(err)
}
},
}
func InitCmd(cmd *cobra.Command, cnfFile *string) {
configFile = cnfFile
cmd.AddCommand(compressionCmd)
}

View File

@ -1,45 +0,0 @@
package convert
import (
"log"
"github.com/go-flucky/flucky/pkg/logfile"
"github.com/spf13/cobra"
)
var compression bool
var configFile *string
var 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",
Run: func(cmd *cobra.Command, args []string) {
measuredValuesInput := logfile.New(args[0])
measuredValues, err := measuredValuesInput.Read()
if err != nil {
log.Fatalln(err)
}
if compression {
measuredValues = logfile.Compression(measuredValues)
}
measuredValuesOutput := logfile.New(args[1])
err = measuredValuesOutput.Write(measuredValues)
if err != nil {
log.Fatalln(err)
}
},
}
// Execute a
func InitCmd(cmd *cobra.Command, cnfFile *string) {
configFile = cnfFile
cmd.AddCommand(convertCmd)
convertCmd.Flags().BoolVar(&compression, "compression", true, "Compress measured values")
}

View File

@ -1,47 +0,0 @@
package daemon
import (
"log"
"time"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/daemon"
"github.com/go-flucky/flucky/pkg/logger"
"github.com/spf13/cobra"
)
var cleanCacheInterval string
var compression bool
var configFile *string
var round float64
var temperatureUnit string
var daemonCmd = &cobra.Command{
Use: "daemon",
Short: "Read continuously data from all enabled sensors",
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
duration, err := time.ParseDuration(cleanCacheInterval)
if err != nil {
log.Fatalf("Can not parse clean cache interval into duration time: %v", err)
}
logger := logger.NewDefaultLogger(logger.LogLevelDebug)
daemon.Start(cnf, duration, compression, round, logger)
},
}
func InitCmd(cmd *cobra.Command, cnfFile *string) {
configFile = cnfFile
cmd.AddCommand(daemonCmd)
daemonCmd.Flags().BoolVar(&compression, "compression", true, "Compress measured values")
daemonCmd.Flags().StringVar(&cleanCacheInterval, "clean-cache-interval", "5m", "Minute intervall to clean cache and write measured values into logfile")
daemonCmd.Flags().Float64Var(&round, "round", 0.5, "Round values. The value 0 deactivates the function")
}

View File

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

View File

@ -1,22 +0,0 @@
package humidity
import (
"github.com/spf13/cobra"
)
var compression bool
var configFile *string
var round float64
var humidityCmd = &cobra.Command{
Use: "humidity",
Short: "Operates with humidity values",
}
// Execute a
func InitCmd(cmd *cobra.Command, cnfFile *string) {
configFile = cnfFile
cmd.AddCommand(humidityCmd)
}

View File

@ -1,54 +0,0 @@
package humidity
import (
"fmt"
"log"
"os"
"github.com/go-flucky/flucky/pkg/types"
"github.com/go-flucky/flucky/pkg/cli"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/logfile"
"github.com/go-flucky/flucky/pkg/rgbled"
"github.com/spf13/cobra"
)
var listTemperatureCmd = &cobra.Command{
Use: "list",
Short: "List humidity values from different or specified sensors by arguments",
Example: fmt.Sprintf("flucky humidity logs"),
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
logfile := logfile.New(cnf.Device.Logfile)
rgbLEDs := cnf.GetRGBLEDs(config.ENABLED)
if err := rgbled.Logfile(rgbLEDs); err != nil {
log.Fatalln(err)
}
measuredValues, err := logfile.Read()
if err != nil {
log.Fatalln(err)
}
if err := rgbled.Off(rgbLEDs); err != nil {
log.Fatalln(err)
}
measuredValues = types.SelectMeasuredValues(types.MeasuredValueTypeHumidity, measuredValues)
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
},
}
func init() {
humidityCmd.AddCommand(listTemperatureCmd)
}

View File

@ -1,75 +0,0 @@
package humidity
import (
"context"
"log"
"os"
"github.com/go-flucky/flucky/pkg/types"
"github.com/go-flucky/flucky/pkg/cli"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/logfile"
"github.com/go-flucky/flucky/pkg/rgbled"
"github.com/go-flucky/flucky/pkg/sensor"
"github.com/spf13/cobra"
)
var logs bool
var readHumidityCmd = &cobra.Command{
Use: "read",
Short: "Reading air pressure values from different or specified sensors by arguments",
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(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
}
rgbLEDs := cnf.GetRGBLEDs(config.ENABLED)
if err := rgbled.Run(rgbLEDs); err != nil {
log.Fatalln(err)
}
ctx := context.Background()
measuredValues, err := sensor.Read(ctx, sensors)
if err != nil {
log.Fatalln(err)
}
measuredValues = types.SelectMeasuredValues(types.MeasuredValueTypeHumidity, measuredValues)
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
if logs {
measuredValuesLogfile := logfile.New(cnf.Device.Logfile)
err := logfile.Append(measuredValuesLogfile, compression, round, measuredValues)
if err != nil {
log.Fatalln(err)
}
}
rgbled.Off(rgbLEDs)
},
}
func init() {
humidityCmd.AddCommand(readHumidityCmd)
readHumidityCmd.Flags().BoolVar(&logs, "logs", true, "Log temperature")
readHumidityCmd.Flags().BoolVar(&compression, "compression", true, "Compress measured with logged temperatures")
readHumidityCmd.Flags().Float64VarP(&round, "round", "r", 0.25, "Round values. The value 0 deactivates the function")
}

View File

@ -1,54 +0,0 @@
package pressure
import (
"fmt"
"log"
"os"
"github.com/go-flucky/flucky/pkg/types"
"github.com/go-flucky/flucky/pkg/cli"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/logfile"
"github.com/go-flucky/flucky/pkg/rgbled"
"github.com/spf13/cobra"
)
var listTemperatureCmd = &cobra.Command{
Use: "list",
Short: "Reading temperature values from different or specified sensors by arguments",
Example: fmt.Sprintf("flucky pressure logs"),
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
logfile := logfile.New(cnf.Device.Logfile)
rgbLEDs := cnf.GetRGBLEDs(config.ENABLED)
if err := rgbled.Logfile(rgbLEDs); err != nil {
log.Fatalln(err)
}
measuredValues, err := logfile.Read()
if err != nil {
log.Fatalln(err)
}
if err := rgbled.Off(rgbLEDs); err != nil {
log.Fatalln(err)
}
measuredValues = types.SelectMeasuredValues(types.MeasuredValueTypePressure, measuredValues)
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
},
}
func init() {
pressureCmd.AddCommand(listTemperatureCmd)
}

View File

@ -1,22 +0,0 @@
package pressure
import (
"github.com/spf13/cobra"
)
var compression bool
var configFile *string
var round float64
var pressureCmd = &cobra.Command{
Use: "pressure",
Short: "List air pressure values from different or specified sensors by arguments",
}
// Execute a
func InitCmd(cmd *cobra.Command, cnfFile *string) {
configFile = cnfFile
cmd.AddCommand(pressureCmd)
}

View File

@ -1,75 +0,0 @@
package pressure
import (
"context"
"log"
"os"
"github.com/go-flucky/flucky/pkg/types"
"github.com/go-flucky/flucky/pkg/cli"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/logfile"
"github.com/go-flucky/flucky/pkg/rgbled"
"github.com/go-flucky/flucky/pkg/sensor"
"github.com/spf13/cobra"
)
var logs bool
var readPressureCmd = &cobra.Command{
Use: "read",
Short: "Operates with air pressure values",
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(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
}
rgbLEDs := cnf.GetRGBLEDs(config.ENABLED)
if err := rgbled.Run(rgbLEDs); err != nil {
log.Fatalln(err)
}
ctx := context.Background()
measuredValues, err := sensor.Read(ctx, sensors)
if err != nil {
log.Fatalln(err)
}
measuredValues = types.SelectMeasuredValues(types.MeasuredValueTypePressure, measuredValues)
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
if logs {
measuredValuesLogfile := logfile.New(cnf.Device.Logfile)
err := logfile.Append(measuredValuesLogfile, compression, round, measuredValues)
if err != nil {
log.Fatalln(err)
}
}
rgbled.Off(rgbLEDs)
},
}
func init() {
pressureCmd.AddCommand(readPressureCmd)
readPressureCmd.Flags().BoolVar(&logs, "logs", true, "Log temperature")
readPressureCmd.Flags().BoolVar(&compression, "compression", true, "Compress measured with logged temperatures")
readPressureCmd.Flags().Float64VarP(&round, "round", "r", 0.25, "Round values. The value 0 deactivates the function")
}

View File

@ -1,77 +0,0 @@
package rgbled
import (
"fmt"
"log"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/types"
"github.com/spf13/cobra"
)
var enabled bool
var location string
var 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`),
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
// determine gpio port
gpioBlue, err := types.StringToGPIO(args[1])
if err != nil {
log.Fatalln(err)
}
gpioGreen, err := types.StringToGPIO(args[2])
if err != nil {
log.Fatalln(err)
}
gpioRed, err := types.StringToGPIO(args[3])
if err != nil {
log.Fatalln(err)
}
// create new sensor struct
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,
},
}
// // add sensor entry to list
err = cnf.AddRGBLED(rgbLED)
if err != nil {
log.Fatalln(err)
}
// save new configuration
err = config.Write(cnf, *configFile)
if err != nil {
log.Fatalln(err)
}
},
}
func init() {
rgbLedCmd.AddCommand(addRgbLedCmd)
addRgbLedCmd.Flags().BoolVarP(&enabled, "enabled", "e", true, "Enable Sensor")
addRgbLedCmd.Flags().StringVarP(&location, "location", "l", "", "Sensor location")
}

View File

@ -1,42 +0,0 @@
package rgbled
import (
"log"
"github.com/go-flucky/flucky/pkg/config"
"github.com/spf13/cobra"
)
var 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`,
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
// disable sensor entry to list
err = cnf.DisableRGBLED(args[0])
if err != nil {
log.Fatalln(err)
}
// save new configuration
err = config.Write(cnf, *configFile)
if err != nil {
log.Fatalln(err)
}
},
}
func init() {
rgbLedCmd.AddCommand(disableRgbLedCmd)
}

View File

@ -1,41 +0,0 @@
package rgbled
import (
"log"
"github.com/go-flucky/flucky/pkg/config"
"github.com/spf13/cobra"
)
var enableRgbLedCmd = &cobra.Command{
Use: "enable",
Short: "Enable a RGB-LED",
Example: `flucky rgb-led enable <name/uuid>
flucky rgb-led enable my-led
flucky rgb-led enable 9f8abfc5-91f3-480c-a42d-b990b6f89e5d`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
// disable sensor entry to list
err = cnf.EnableRGBLED(args[0])
if err != nil {
log.Fatalln(err)
}
// save new configuration
err = config.Write(cnf, *configFile)
if err != nil {
log.Fatalln(err)
}
},
}
func init() {
rgbLedCmd.AddCommand(enableRgbLedCmd)
}

View File

@ -1,30 +0,0 @@
package rgbled
import (
"log"
"os"
"github.com/go-flucky/flucky/pkg/cli"
"github.com/go-flucky/flucky/pkg/config"
"github.com/spf13/cobra"
)
var listRgbLedCmd = &cobra.Command{
Use: "list",
Short: "List RGB-LEDs",
Aliases: []string{"ls"},
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
// print sensors on stdout
cli.PrintRGBLEDs(cnf, os.Stdout)
},
}
func init() {
rgbLedCmd.AddCommand(listRgbLedCmd)
}

View File

@ -1,41 +0,0 @@
package rgbled
import (
"log"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/rgbled"
"github.com/spf13/cobra"
)
var offRgbLedCmd = &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`,
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(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 {
log.Fatalln(err)
}
},
}
func init() {
rgbLedCmd.AddCommand(offRgbLedCmd)
}

View File

@ -1,50 +0,0 @@
package rgbled
import (
"log"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/rgbled"
"github.com/go-flucky/flucky/pkg/types"
"github.com/spf13/cobra"
)
var onRgbLedCmd = &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),
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(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 {
log.Fatalln(err)
}
err = rgbled.CustomColor(rgbLEDs, color)
if err != nil {
log.Fatalln(err)
}
},
}
func init() {
rgbLedCmd.AddCommand(onRgbLedCmd)
}

View File

@ -1,41 +0,0 @@
package rgbled
import (
"log"
"github.com/go-flucky/flucky/pkg/config"
"github.com/spf13/cobra"
)
var 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),
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
// // add remote entry to list
err = cnf.RemoveRGBLED(args[0])
if err != nil {
log.Fatalln(err)
}
// save new configuration
err = config.Write(cnf, *configFile)
if err != nil {
log.Fatalln(err)
}
},
}
func init() {
rgbLedCmd.AddCommand(removeRgbLedCmd)
}

View File

@ -1,40 +0,0 @@
package rgbled
import (
"log"
"github.com/go-flucky/flucky/pkg/config"
"github.com/spf13/cobra"
)
var 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`,
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
// rename sensor
err = cnf.RenameRGBLED(args[0], args[1])
if err != nil {
log.Println(err)
}
// save new configuration
err = config.Write(cnf, *configFile)
if err != nil {
log.Fatalln(err)
}
},
}
func init() {
rgbLedCmd.AddCommand(renameRgbLedCmd)
}

View File

@ -1,20 +0,0 @@
package rgbled
import (
"github.com/spf13/cobra"
)
var configFile *string
var rgbLedCmd = &cobra.Command{
Use: "rgb-led",
Short: "Manage RGB-LEDs",
}
// InitCmd da
func InitCmd(cmd *cobra.Command, cnfFile *string) {
configFile = cnfFile
cmd.AddCommand(rgbLedCmd)
}

View File

@ -1,99 +0,0 @@
package sensor
import (
"log"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/types"
"github.com/spf13/cobra"
)
var enabled bool
var gpioIn string
var i2cAddress uint8
var i2cBus int
var location string
var wireID string
var 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`,
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
// determine sensor model
sensorModel, err := types.SelectSensorModel(args[1])
if err != nil {
log.Fatalln(err)
}
// create new sensor struct
sensor := &types.Sensor{
SensorName: args[0],
SensorModel: sensorModel,
SensorLocation: location,
SensorEnabled: enabled,
}
// determine gpio port if set
if gpioIn != "" &&
i2cAddress == 0 &&
i2cBus == 0 &&
wireID == "" {
gpio, err := types.StringToGPIO(gpioIn)
if err != nil {
log.Fatalln(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 {
log.Fatalln(err)
}
// save new configuration
err = config.Write(cnf, *configFile)
if err != nil {
log.Fatalln(err)
}
},
}
func init() {
sensorCmd.AddCommand(addSensorCmd)
addSensorCmd.Flags().BoolVar(&enabled, "enabled", true, "Enable Sensor")
addSensorCmd.Flags().StringVar(&gpioIn, "gpio", "", "GPIO")
addSensorCmd.Flags().Uint8Var(&i2cAddress, "i2c-address", 0, "I2C-Address")
addSensorCmd.Flags().IntVar(&i2cBus, "i2c-bus", 0, "I2C-Bus")
addSensorCmd.Flags().StringVar(&location, "location", "", "Sensor location")
addSensorCmd.Flags().StringVar(&wireID, "wire-id", "", "Wire-ID")
}

View File

@ -1,40 +0,0 @@
package sensor
import (
"log"
"github.com/go-flucky/flucky/pkg/config"
"github.com/spf13/cobra"
)
var disableSensorCmd = &cobra.Command{
Use: "disable",
Short: "Disable Sensor",
Args: cobra.ExactArgs(1),
Example: "flucky sensor disable outdoor",
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
// disable sensor entry to list
err = cnf.DisableSensor(args[0])
if err != nil {
log.Fatalln(err)
}
// save new configuration
err = config.Write(cnf, *configFile)
if err != nil {
log.Fatalln(err)
}
},
}
func init() {
sensorCmd.AddCommand(disableSensorCmd)
}

View File

@ -1,39 +0,0 @@
package sensor
import (
"log"
"github.com/go-flucky/flucky/pkg/config"
"github.com/spf13/cobra"
)
var enableSensorCmd = &cobra.Command{
Use: "enable",
Short: "Enable Sensor",
Example: "flucky sensor enable outdoor",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
// disable sensor entry to list
err = cnf.EnableSensor(args[0])
if err != nil {
log.Fatalln(err)
}
// save new configuration
err = config.Write(cnf, *configFile)
if err != nil {
log.Fatalln(err)
}
},
}
func init() {
sensorCmd.AddCommand(enableSensorCmd)
}

View File

@ -1,39 +0,0 @@
package sensor
import (
"log"
"os"
"github.com/go-flucky/flucky/pkg/cli"
"github.com/go-flucky/flucky/pkg/config"
"github.com/spf13/cobra"
)
var listSensorCmd = &cobra.Command{
Use: "list",
Short: "List Sensors",
Aliases: []string{"ls"},
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
// print sensors on stdout
err = cli.PrintSensors(cnf, os.Stdout)
if err != nil {
log.Fatalln(err)
}
// save new configuration
err = config.Write(cnf, *configFile)
if err != nil {
log.Fatalln(err)
}
},
}
func init() {
sensorCmd.AddCommand(listSensorCmd)
}

View File

@ -1,39 +0,0 @@
package sensor
import (
"log"
"github.com/go-flucky/flucky/pkg/config"
"github.com/spf13/cobra"
)
var rmSensorCmd = &cobra.Command{
Use: "remove",
Short: "Remove Sensor",
Example: "flucky sensor rm outdoor",
Aliases: []string{"rm"},
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
// // add remote entry to list
err = cnf.RemoveSensor(args[0])
if err != nil {
log.Fatalln(err)
}
// save new configuration
err = config.Write(cnf, *configFile)
if err != nil {
log.Fatalln(err)
}
},
}
func init() {
sensorCmd.AddCommand(rmSensorCmd)
}

View File

@ -1,39 +0,0 @@
package sensor
import (
"fmt"
"log"
"github.com/go-flucky/flucky/pkg/config"
"github.com/spf13/cobra"
)
var renameSensorCmd = &cobra.Command{
Use: "rename",
Short: "Rename Sensor",
Args: cobra.ExactArgs(2),
Example: fmt.Sprintf("flucky sensor rename indoor outdoor\nflucky sensor rename f98b00ea-a9b2-4e00-924f-113859d0af2d outdoor"),
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
// rename sensor
err = cnf.RenameSensor(args[0], args[1])
if err != nil {
log.Println(err)
}
// save new configuration
err = config.Write(cnf, *configFile)
if err != nil {
log.Fatalln(err)
}
},
}
func init() {
sensorCmd.AddCommand(renameSensorCmd)
}

View File

@ -1,20 +0,0 @@
package sensor
import (
"github.com/spf13/cobra"
)
var configFile *string
var sensorCmd = &cobra.Command{
Use: "sensor",
Short: "Manage Sensors",
}
// InitCmd da
func InitCmd(cmd *cobra.Command, cnfFile *string) {
configFile = cnfFile
cmd.AddCommand(sensorCmd)
}

View File

@ -1,54 +0,0 @@
package temperature
import (
"fmt"
"log"
"os"
"github.com/go-flucky/flucky/pkg/types"
"github.com/go-flucky/flucky/pkg/cli"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/logfile"
"github.com/go-flucky/flucky/pkg/rgbled"
"github.com/spf13/cobra"
)
var listTemperatureCmd = &cobra.Command{
Use: "list",
Short: "List temperature values from different or specified sensors by arguments",
Example: fmt.Sprintf("flucky temperature logs"),
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
logfile := logfile.New(cnf.Device.Logfile)
rgbLEDs := cnf.GetRGBLEDs(config.ENABLED)
if err := rgbled.Logfile(rgbLEDs); err != nil {
log.Fatalln(err)
}
measuredValues, err := logfile.Read()
if err != nil {
log.Fatalln(err)
}
if err := rgbled.Off(rgbLEDs); err != nil {
log.Fatalln(err)
}
measuredValues = types.SelectMeasuredValues(types.MeasuredValueTypeTemperature, measuredValues)
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
},
}
func init() {
temperatureCmd.AddCommand(listTemperatureCmd)
}

View File

@ -1,79 +0,0 @@
package temperature
import (
"context"
"fmt"
"log"
"os"
"github.com/go-flucky/flucky/pkg/rgbled"
"github.com/go-flucky/flucky/pkg/types"
"github.com/go-flucky/flucky/pkg/cli"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/logfile"
"github.com/go-flucky/flucky/pkg/sensor"
"github.com/spf13/cobra"
)
var logs bool
var readTemperatureCmd = &cobra.Command{
Use: "read",
Short: "Reading temperature values from different or specified sensors by arguments",
Example: fmt.Sprintf("flucky temperature read\nflucky temperature read outdoor"),
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(*configFile)
if err != nil {
log.Fatalln(err)
}
// fetch all temperature sensors or sensors by args
sensors := make([]sensor.Sensor, 0)
if len(args) == 0 {
sensors = cnf.GetTemperatureSensors(config.ENABLED)
} else {
sensors = cnf.GetTemperatureSensorsByName(args)
}
if len(sensors) == 0 {
return
}
rgbLEDs := cnf.GetRGBLEDs(config.ENABLED)
if err := rgbled.Run(rgbLEDs); err != nil {
log.Fatalln(err)
}
ctx := context.Background()
measuredValues, err := sensor.Read(ctx, sensors)
if err != nil {
rgbled.Error(rgbLEDs)
log.Fatalln(err)
}
measuredValues = types.SelectMeasuredValues(types.MeasuredValueTypeTemperature, measuredValues)
// print temperatures on stdout
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
if logs {
measuredValuesLogfile := logfile.New(cnf.Device.Logfile)
err := logfile.Append(measuredValuesLogfile, compression, round, measuredValues)
if err != nil {
rgbled.Error(rgbLEDs)
log.Fatalln(err)
}
}
rgbled.Off(rgbLEDs)
},
}
func init() {
temperatureCmd.AddCommand(readTemperatureCmd)
readTemperatureCmd.Flags().BoolVar(&logs, "logs", true, "Log temperature")
readTemperatureCmd.Flags().BoolVar(&compression, "compression", true, "Compress measured with logged temperatures")
readTemperatureCmd.Flags().Float64VarP(&round, "round", "r", 0.25, "Round values. The value 0 deactivates the function")
}

View File

@ -1,24 +0,0 @@
package temperature
import (
"fmt"
"github.com/spf13/cobra"
)
var compression bool
var configFile *string
var round float64
var temperatureCmd = &cobra.Command{
Use: "temperature",
Short: "Operates with temperature values",
Example: fmt.Sprintf("flucky temperature read\nflucky temperature read outdoor"),
}
// Execute a
func InitCmd(cmd *cobra.Command, cnfFile *string) {
configFile = cnfFile
cmd.AddCommand(temperatureCmd)
}

View File

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

20
go.mod
View File

@ -1,20 +0,0 @@
module github.com/go-flucky/flucky
go 1.12
require (
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/go-dht v0.1.1
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/lib/pq v1.2.0
github.com/satori/go.uuid v1.2.0
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3 // indirect
github.com/stianeikeland/go-rpio v4.2.0+incompatible
github.com/stretchr/testify v1.3.0
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
periph.io/x/periph v3.4.0+incompatible
)

36
go.sum
View File

@ -1,36 +0,0 @@
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=
github.com/d2r2/go-i2c v0.0.0-20181113114621-14f8dd4e89ce/go.mod h1:AwxDPnsgIpy47jbGXZHA9Rv7pDkOJvQbezPuK1Y+nNk=
github.com/d2r2/go-logger v0.0.0-20181221090742-9998a510495e h1:ZG3JBA6rPRl0xxQ+nNSfO7tor8w+CNCTs05DNJQYbLM=
github.com/d2r2/go-logger v0.0.0-20181221090742-9998a510495e/go.mod h1:oA+9PUt8F1aKJ6o4YU1T120i7sgo1T6/1LWEEBy0BSs=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-flucky/go-dht v0.1.1 h1:dPz9F5D7oUaTd0GUGTvT4lBH2R043h1bkmhTlpQax1Y=
github.com/go-flucky/go-dht v0.1.1/go.mod h1:Yk/cct+/u+eCS7pB/kc0tc7MrVXdFI4W15MJCj5FRUc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stianeikeland/go-rpio v4.2.0+incompatible h1:CUOlIxdJdT+H1obJPsmg8byu7jMSECLfAN9zynm5QGo=
github.com/stianeikeland/go-rpio v4.2.0+incompatible/go.mod h1:Sh81rdJwD96E2wja2Gd7rrKM+XZ9LrwvN2w4IXrqLR8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
periph.io/x/periph v3.4.0+incompatible h1:5gzxE4ryPq52cdqSw0mErR6pyJK8cBF2qdUAcOWh0bo=
periph.io/x/periph v3.4.0+incompatible/go.mod h1:EWr+FCIU2dBWz5/wSWeiIUJTriYv9v2j2ENBmgYyy7Y=

27
main.go
View File

@ -1,27 +0,0 @@
package main
import (
"log"
"os"
"os/user"
"github.com/go-flucky/flucky/cmd"
)
var version string
func main() {
user, err := user.Current()
if err != nil {
panic(err)
}
if user.Uid != "0" {
log.Println("you need to be root to run this command")
os.Exit(1)
}
cmd.Execute(version)
}

View File

@ -1,152 +0,0 @@
package cli
import (
"fmt"
"io"
"text/tabwriter"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/types"
)
// GetSensorsByMeasuredValues returns commulated list of sensors by measured values
func GetSensorsByMeasuredValues(measuredValues []*types.MeasuredValue, cnf *config.Configuration) []*types.Sensor {
sensors := []*types.Sensor{}
for _, measuredValue := range measuredValues {
duplicated := false
foundSensor := &types.Sensor{}
for _, cnfSensor := range cnf.Sensors {
if measuredValue.SensorID == cnfSensor.SensorID {
foundSensor = cnfSensor
// compare if id has already been added to list
for _, sensor := range sensors {
if cnfSensor.SensorID == sensor.SensorID {
duplicated = true
break
}
}
}
}
if duplicated {
continue
}
if foundSensor != nil {
sensors = append(sensors, foundSensor)
continue
} else {
sensors = append(sensors, &types.Sensor{SensorID: measuredValue.SensorID})
}
}
return sensors
}
// 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\tenabled\n")
for _, sensor := range cnf.Sensors {
fmt.Fprintf(tw, "%v\t%v\t%v\t", sensor.SensorName, sensor.SensorLocation, sensor.SensorModel)
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\n", sensor.SensorEnabled)
}
tw.Flush()
return nil
}
// PrintMeasuredValues displays a list of measured values
func PrintMeasuredValues(measuredValues []*types.MeasuredValue, cnf *config.Configuration, w io.Writer) {
sensors := GetSensorsByMeasuredValues(measuredValues, cnf)
// 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
for i, sensor := range sensors {
fmt.Fprintf(tw, "%v\t", sensor.Name())
if i == len(sensors)-1 {
fmt.Fprintf(tw, "\n")
}
}
// find sensor with maximum temperature 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.SensorID]) > i {
fmt.Fprintf(tw, "%3.3f\t", orderedMeasuredValues[sensor.SensorID][i].Value)
} else {
fmt.Fprint(tw, "\t")
}
}
fmt.Fprint(tw, "\n")
}
tw.Flush()
}

View File

@ -1,59 +0,0 @@
package config
import (
"encoding/json"
"fmt"
"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}$")
// Read the configuration file
func Read(configFile string) (*Configuration, error) {
fc := &Configuration{}
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
}
// Write the configuration into a file, specified by the configuration filepath
func Write(cfg *Configuration, 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)
}
}
f, err := os.Create(configFile)
if err != nil {
return fmt.Errorf("Can not write config file: %v", 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
}

View File

@ -1,582 +0,0 @@
package config
import (
"fmt"
"time"
"github.com/go-flucky/flucky/pkg/internal/format"
"github.com/go-flucky/flucky/pkg/rgbled"
"github.com/go-flucky/flucky/pkg/sensor"
"github.com/go-flucky/flucky/pkg/types"
uuid "github.com/satori/go.uuid"
)
var humiditySensorModels = map[types.SensorModel]types.SensorModel{
types.BME280: types.BME280,
types.DHT11: types.DHT11,
types.DHT22: types.DHT22,
}
var pressureSensorModels = map[types.SensorModel]types.SensorModel{
types.BME280: types.BME280,
}
var temperatureSensorModels = map[types.SensorModel]types.SensorModel{
types.BME280: types.BME280,
types.DHT11: types.DHT11,
types.DHT22: types.DHT22,
types.DS18B20: types.DS18B20,
}
// Configuration of flucky
type Configuration struct {
Device *types.Device `json:"device"`
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.DeviceID {
rgbLED.DeviceID = c.Device.DeviceID
}
// overwrite creation date
rgbLED.CreationDate = time.Now()
// check
c.RGBLEDs = append(c.RGBLEDs, rgbLED)
return nil
}
// AddSensor add a new sensor
func (c *Configuration) AddSensor(sensor *types.Sensor) error {
// check if sensorID is a valid UUID string
if !validUUID.MatchString(sensor.SensorID) {
sensor.SensorID = uuid.NewV4().String()
}
// check if sensor name and sensor uuid already exists
for _, s := range c.Sensors {
if s.SensorName == sensor.SensorName {
return fmt.Errorf("Sensor %v already exists", s.SensorName)
}
if s.SensorID == sensor.SensorID {
return fmt.Errorf("Sensor %v with UUID %v already exists", s.SensorName, s.SensorID)
}
if sensor.WireID != nil {
if *s.WireID == *sensor.WireID {
return fmt.Errorf("Sensor with 1wire-id %v already exists as %v", *s.WireID, s.SensorName)
}
}
}
// check if sensor has a valid device id
if sensor.DeviceID != c.Device.DeviceID {
sensor.DeviceID = c.Device.DeviceID
}
// 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
}
}
if !found {
return fmt.Errorf("Can not found RGB-LED %v", name)
}
return nil
}
// DisableSensor disables a sensor by its name or its unique UUID
func (c *Configuration) DisableSensor(name string) error {
found := false
for _, sensor := range c.Sensors {
// disable sensor matched after name
if !validUUID.MatchString(name) &&
sensor.SensorName == name {
sensor.SensorEnabled = false
found = true
break
}
// remove machted uuid
if validUUID.MatchString(name) &&
sensor.SensorID == name {
sensor.SensorEnabled = false
found = true
break
}
}
if !found {
return fmt.Errorf("Can not found sensor %v", name)
}
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 {
found := false
for _, sensor := range c.Sensors {
// disable sensor matched after name
if !validUUID.MatchString(name) &&
sensor.SensorName == name {
sensor.SensorEnabled = true
found = true
break
}
// remove machted uuid
if validUUID.MatchString(name) &&
sensor.SensorID == name {
sensor.SensorEnabled = true
found = true
break
}
}
if !found {
return fmt.Errorf("Can not found sensor %v", name)
}
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.SensorEnabled {
cachedSensors = append(cachedSensors, sensor)
}
}
return c.convertSensors(cachedSensors)
case DISABLED:
for _, sensor := range sensors {
if !sensor.SensorEnabled {
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.SensorID:
configHumiditySensors[s.SensorID] = s
case s.SensorName:
configHumiditySensors[s.SensorID] = 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.SensorEnabled {
cachedSensors = append(cachedSensors, sensor)
}
}
return c.convertSensors(cachedSensors)
case DISABLED:
for _, sensor := range sensors {
if !sensor.SensorEnabled {
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.SensorID:
configPressureSensors[s.SensorID] = s
case s.SensorName:
configPressureSensors[s.SensorID] = 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)
}
// 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.SensorEnabled {
cachedSensors = append(cachedSensors, sensor)
}
}
return c.convertSensors(cachedSensors)
case DISABLED:
for _, sensor := range c.Sensors {
if !sensor.SensorEnabled {
cachedSensors = append(cachedSensors, sensor)
}
}
return c.convertSensors(cachedSensors)
default:
return c.convertSensors(cachedSensors)
}
}
// 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.SensorEnabled {
cachedSensors = append(cachedSensors, sensor)
}
}
return c.convertSensors(cachedSensors)
case DISABLED:
for _, sensor := range sensors {
if !sensor.SensorEnabled {
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.SensorID:
configTemperatureSensors[s.SensorID] = s
case s.SensorName:
configTemperatureSensors[s.SensorID] = 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 {
// remove machted name
if !validUUID.MatchString(name) &&
sensor.SensorName == name {
c.Sensors = append(c.Sensors[:i], c.Sensors[i+1:]...)
return nil
}
// remove machted uuid
if validUUID.MatchString(name) &&
sensor.SensorID == name {
c.Sensors = append(c.Sensors[:i], c.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.SensorName == oldName ||
sensor.SensorID == oldName {
sensor.SensorName = newName
return nil
}
}
return fmt.Errorf("Could not find remote %v to replace into with %v", oldName, newName)
}
func (c *Configuration) convertSensors(sensors []*types.Sensor) []sensor.Sensor {
cachedSensors := make([]sensor.Sensor, 0)
for _, s := range sensors {
switch s.SensorModel {
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.SensorModel]; 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.SensorModel]; 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.SensorModel]; ok {
temperatureSensors = append(temperatureSensors, s)
}
}
return temperatureSensors
}

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,130 +0,0 @@
package daemon
import (
"context"
"os"
"os/signal"
"syscall"
"time"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/db"
"github.com/go-flucky/flucky/pkg/logfile"
"github.com/go-flucky/flucky/pkg/logger"
"github.com/go-flucky/flucky/pkg/rgbled"
"github.com/go-flucky/flucky/pkg/sensor"
"github.com/go-flucky/flucky/pkg/types"
)
var (
postgresHost = "markus-pc.trier.cryptic.systems"
postgresPort = "5432"
postgresDatabase = "postgres"
postgresUser = "postgres"
postgresPassword = "postgres"
)
// Start the daemon
func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compression bool, round float64, logger logger.Logger) {
// Info
logger.Info("Use clean-cache-interval: %v", cleanCacheInterval.String())
logger.Info("Use compression: %v", compression)
logger.Info("Round: %v", round)
ticker := time.Tick(cleanCacheInterval)
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, os.Kill, syscall.SIGTERM)
errorChannel := make(chan error, 0)
measuredValuesChannel := make(chan []*types.MeasuredValue, 0)
ctx := context.Background()
childContext, cancel := context.WithCancel(ctx)
measuredValuesLogfile := logfile.New(cnf.Device.Logfile)
measuredValuesCache := make([]*types.MeasuredValue, 0)
go sensor.ReadContinuously(childContext, cnf.GetSensors(config.ENABLED), measuredValuesChannel, errorChannel)
rgbLEDs := cnf.GetRGBLEDs(config.ENABLED)
for {
err := rgbled.Run(rgbLEDs)
if err != nil {
logger.Error("Can not turn on green info light: %v", err)
}
select {
case err, _ := <-errorChannel:
logger.Error("%v", err)
err = rgbled.Error(rgbLEDs)
if err != nil {
logger.Error("Can not turn on red info light: %v", err)
}
time.Sleep(time.Second * 2)
case <-ticker:
err := rgbled.Logfile(rgbLEDs)
if err != nil {
logger.Error("Can not turn on blue info light: %v", err)
}
// err = logfile.Append(measuredValuesLogfile, compression, round, measuredValuesCache)
postgres, err := db.New(db.DBOTypePostgres, postgresHost, postgresPort, postgresDatabase, postgresUser, postgresPassword)
if err != nil {
err = rgbled.Error(rgbLEDs)
if err != nil {
logger.Error("Can not turn on red info light: %v", err)
}
cancel()
logger.Error("Can not open database connection: %v", err)
}
postgresCtx := context.Background()
err = postgres.InsertMeasuredValues(postgresCtx, measuredValuesCache)
if err != nil {
err = rgbled.Error(rgbLEDs)
if err != nil {
logger.Error("Can not turn on red info light: %v", err)
}
cancel()
logger.Error("Can not save caches measured values in database: %v", err)
}
measuredValuesCache = make([]*types.MeasuredValue, 0)
case measuredValues, _ := <-measuredValuesChannel:
measuredValuesCache = append(measuredValuesCache, measuredValues...)
case killSignal := <-interrupt:
logger.Warn("Daemon was interruped by system signal %v\n", killSignal)
cancel()
err := rgbled.Error(rgbLEDs)
if err != nil {
logger.Error("Can not turn on red info light: %v", err)
}
logger.Warn("Save remaining data from the cache")
err = logfile.Append(measuredValuesLogfile, compression, round, measuredValuesCache)
if err != nil {
logger.Fatal("%v", err)
}
return
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 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,7 +0,0 @@
INSERT INTO devices (
device_id,
device_name,
device_location,
creation_date
)
VALUES ($1, $2, $3, $4);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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,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,16 +0,0 @@
SELECT
sensor_id,
sensor_name,
sensor_location,
wire_id,
i2c_bus,
i2c_address,
gpio_number,
sensor_model,
sensor_enabled,
device_id,
creation_date
FROM
sensors
WHERE
sensor_id = $1;

View File

@ -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,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(measuredValuesChannel <-chan []*types.MeasuredValue) []*types.MeasuredValue {
cachedMeasuredValues := make([]*types.MeasuredValue, 0)
for {
select {
case measuredValues, more := <-measuredValuesChannel:
if more {
cachedMeasuredValues = append(cachedMeasuredValues, measuredValues...)
continue
}
default:
return cachedMeasuredValues
}
}
}

View File

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

View File

@ -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,129 +0,0 @@
package logfile
import (
"encoding/csv"
"fmt"
"os"
"strconv"
"time"
"github.com/go-flucky/flucky/pkg/internal/format"
"github.com/go-flucky/flucky/pkg/types"
)
type csvLogfile struct {
logfile string
}
func (cl *csvLogfile) Read() ([]*types.MeasuredValue, error) {
if _, err := os.Stat(cl.logfile); os.IsNotExist(err) {
return nil, fmt.Errorf("%v: %v", errorLogfileNotFound, cl.logfile)
}
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
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
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()
writeCreationDate(measuredValues)
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,28 +0,0 @@
package logfile
import "errors"
var (
errorLogfileCreate = errors.New("Can not create logfile")
errorLogfileDecode = errors.New("Can not decode from reader")
errorLogfileEncode = errors.New("Can not encode from writer")
errorLogfileMarshal = errors.New("Can not marshal values")
errorLogfileNotFound = errors.New("Can not find logfile")
errorLogfileOpen = errors.New("Can not open logfile")
errorLogfileRead = errors.New("Can not read from given reader")
errorLogfileUnmarshal = errors.New("Can not unmarshal values")
errorLogfileWrite = errors.New("Can not write with given writer")
errorParseFloat = errors.New("Can not parse float")
errorParseMeasurementUnit = errors.New("Can not parse mesaurement unit")
errorParseTime = errors.New("Can not parse time")
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")
errorTypeSwitch = errors.New("Can not detect type via type switch")
)

View File

@ -1,11 +0,0 @@
package logfile
import (
"github.com/go-flucky/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,60 +0,0 @@
package logfile
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/go-flucky/flucky/pkg/types"
)
type jsonLogfile struct {
logfile string
}
func (jl *jsonLogfile) Read() ([]*types.MeasuredValue, error) {
if _, err := os.Stat(jl.logfile); os.IsNotExist(err) {
return nil, fmt.Errorf("%v: %v", errorLogfileNotFound, jl.logfile)
}
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)
}
}
writeCreationDate(measuredValues)
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,132 +0,0 @@
package logfile
import (
"math"
"path/filepath"
"sort"
"github.com/go-flucky/flucky/pkg/internal/format"
"github.com/go-flucky/flucky/pkg/types"
)
// var validUUID = regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$")
// Append adds an array of several measured values to a logfile
func Append(logfile Logfile, compression bool, round float64, measuredValues []*types.MeasuredValue) error {
if round != 0 {
for _, measuredValue := range measuredValues {
measuredValue.Value = math.Round(measuredValue.Value/round) * round
}
}
allMeasuredValues, err := logfile.Read()
if err != nil {
return err
}
allMeasuredValues = append(allMeasuredValues, measuredValues...)
if compression {
allMeasuredValues = Compression(allMeasuredValues)
}
err = logfile.Write(allMeasuredValues)
if err != nil {
return err
}
return nil
}
// 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 _, 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
}
// 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,
}
}
}
func writeCreationDate(measuredValues []*types.MeasuredValue) error {
for _, measuredValue := range measuredValues {
now := format.FormatedTime()
measuredValue.CreationDate = now
}
return nil
}

View File

@ -1 +0,0 @@
package logfile_test

View File

@ -1,19 +0,0 @@
package logfile
import (
"encoding/xml"
"github.com/go-flucky/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,74 +0,0 @@
package logfile
import (
"encoding/xml"
"fmt"
"os"
"github.com/go-flucky/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) {
return nil, fmt.Errorf("%v: %v", errorLogfileNotFound, xl.logfile)
}
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()
writeCreationDate(measuredValues)
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

@ -1,93 +0,0 @@
package logger
import (
"fmt"
"io"
"io/ioutil"
"os"
"sync"
"time"
)
type defaultLogger struct {
logLevel LogLevel
mutex *sync.Mutex
stdout io.Writer
stderr io.Writer
}
func (dl *defaultLogger) Debug(f string, v ...interface{}) {
dl.log(LogLevelDebug, f, v...)
}
func (dl *defaultLogger) Info(f string, v ...interface{}) {
dl.log(LogLevelInfo, f, v...)
}
func (dl *defaultLogger) Warn(f string, v ...interface{}) {
dl.log(LogLevelWarn, f, v...)
}
func (dl *defaultLogger) Error(f string, v ...interface{}) {
dl.log(LogLevelError, f, v...)
}
func (dl *defaultLogger) Fatal(f string, v ...interface{}) {
dl.log(LogLevelFatal, f, v...)
os.Exit(1)
}
func (dl *defaultLogger) log(ll LogLevel, f string, v ...interface{}) {
if dl.logLevel > ll {
return
}
layout := "2006/01/02 15:04:05"
dw := dl.stdout
prefix := ""
switch ll {
case LogLevelDebug:
prefix = "DEBUG"
case LogLevelInfo:
prefix = "INFO"
case LogLevelWarn:
prefix = "WARN"
case LogLevelError:
prefix = "ERROR"
dw = dl.stderr
case LogLevelFatal:
prefix = "FATAL"
dw = dl.stderr
}
dl.mutex.Lock()
fmt.Fprintf(dw, "%v %v: %v\n", time.Now().Format(layout), prefix, fmt.Sprintf(f, v...))
dl.mutex.Unlock()
}
func NewDefaultLogger(logLevel LogLevel) Logger {
return &defaultLogger{
logLevel: logLevel,
mutex: new(sync.Mutex),
stdout: os.Stdout,
stderr: os.Stderr,
}
}
func NewCustomLogger(loglevel LogLevel, out io.Writer, err io.Writer) Logger {
return &defaultLogger{
logLevel: loglevel,
mutex: new(sync.Mutex),
stdout: out,
stderr: err,
}
}
func NewSilentLogger() Logger {
return &defaultLogger{
logLevel: LogLevelDebug,
mutex: new(sync.Mutex),
stdout: ioutil.Discard,
stderr: ioutil.Discard,
}
}

View File

@ -1,69 +0,0 @@
package logger_test
import (
"bytes"
"strings"
"testing"
"github.com/go-flucky/flucky/pkg/logger"
"github.com/stretchr/testify/require"
)
func TestLogger(t *testing.T) {
require := require.New(t)
stdoutBuffer := new(bytes.Buffer)
stderrBuffer := new(bytes.Buffer)
// LogLevelDebug
l := logger.NewCustomLogger(logger.LogLevelDebug, stdoutBuffer, stderrBuffer)
l.Debug("DEBUG")
require.NotEmpty(stdoutBuffer.Bytes())
require.Empty(stderrBuffer.Bytes())
stdoutBuffer.Reset()
stderrBuffer.Reset()
// LogLevelInfo
l = logger.NewCustomLogger(logger.LogLevelInfo, stdoutBuffer, stderrBuffer)
l.Debug("DEBUG")
require.Empty(stdoutBuffer.Bytes())
require.Empty(stderrBuffer.Bytes())
stdoutBuffer.Reset()
stderrBuffer.Reset()
l.Info("INFO")
require.NotEmpty(stdoutBuffer.Bytes())
require.Empty(stderrBuffer.Bytes())
stringArray := strings.Split(stdoutBuffer.String(), ": ")
require.Equal(2, len(stringArray))
require.Equal("INFO\n", stringArray[1])
stdoutBuffer.Reset()
stderrBuffer.Reset()
// LogLevelWarn
l = logger.NewCustomLogger(logger.LogLevelWarn, stdoutBuffer, stderrBuffer)
l.Warn("WARN")
require.NotEmpty(stdoutBuffer.Bytes())
require.Empty(stderrBuffer.Bytes())
stringArray = strings.Split(stdoutBuffer.String(), ": ")
require.Equal(2, len(stringArray))
require.Equal("WARN\n", stringArray[1])
stdoutBuffer.Reset()
stderrBuffer.Reset()
// LogLevelError
l = logger.NewCustomLogger(logger.LogLevelError, stdoutBuffer, stderrBuffer)
l.Error("ERROR")
require.Empty(stdoutBuffer.Bytes())
require.NotEmpty(stderrBuffer.Bytes())
stringArray = strings.Split(stderrBuffer.String(), ": ")
require.Equal(2, len(stringArray))
require.Equal("ERROR\n", stringArray[1])
}

View File

@ -1,9 +0,0 @@
package logger
type Logger interface {
Debug(string, ...interface{})
Info(string, ...interface{})
Warn(string, ...interface{})
Error(string, ...interface{})
Fatal(string, ...interface{})
}

View File

@ -1,11 +0,0 @@
package logger
type LogLevel int
const (
LogLevelDebug LogLevel = iota + 1
LogLevelInfo
LogLevelWarn
LogLevelError
LogLevelFatal
)

View File

@ -1,239 +0,0 @@
package rgbled
import (
"fmt"
"github.com/go-flucky/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/go-flucky/flucky/pkg/internal/collect"
"github.com/go-flucky/flucky/pkg/internal/prittyprint"
"github.com/go-flucky/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,124 +0,0 @@
package sensor
import (
"context"
"fmt"
"log"
"sync"
"github.com/d2r2/go-bsbmp"
"github.com/d2r2/go-i2c"
"github.com/d2r2/go-logger"
"github.com/go-flucky/flucky/pkg/internal/format"
"github.com/go-flucky/flucky/pkg/types"
uuid "github.com/satori/go.uuid"
)
// BME280 is a sensor to measure humidity and temperature.
type BME280 struct {
*types.Sensor
}
// GetSensorModel returns the sensor model
func (s *BME280) GetSensorModel() types.SensorModel {
return s.Sensor.SensorModel
}
// Read measured values
func (s *BME280) Read() ([]*types.MeasuredValue, error) {
// 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)
if err != nil {
log.Fatal(err)
}
defer i2c.Close()
logger.ChangePackageLogLevel("i2c", logger.InfoLevel)
sensor, err := bsbmp.NewBMP(bsbmp.BME280, i2c)
if err != nil {
log.Fatal(err)
}
logger.ChangePackageLogLevel("bsbmp", logger.InfoLevel)
temperatureValue, err := sensor.ReadTemperatureC(bsbmp.ACCURACY_STANDARD)
if err != nil {
log.Fatal(err)
}
pressureValue, err := sensor.ReadPressurePa(bsbmp.ACCURACY_STANDARD)
if err != nil {
log.Fatal(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)
}
measuredValues := []*types.MeasuredValue{
&types.MeasuredValue{
ID: uuid.NewV4().String(),
Value: float64(humidityValue),
ValueType: types.MeasuredValueTypeHumidity,
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
&types.MeasuredValue{
ID: uuid.NewV4().String(),
Value: float64(pressureValue),
ValueType: types.MeasuredValueTypePressure,
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
&types.MeasuredValue{
ID: uuid.NewV4().String(),
Value: float64(temperatureValue),
ValueType: types.MeasuredValueTypeTemperature,
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
}
return measuredValues, nil
}
// ReadChannel reads the measured values from the sensor and writes them to a
// channel.
func (s *BME280) ReadChannel(measuredValuesChannel chan<- []*types.MeasuredValue, errorChannel chan<- error, wg *sync.WaitGroup) {
if wg != nil {
defer wg.Done()
}
measuredValues, err := s.Read()
if err != nil {
errorChannel <- err
return
}
measuredValuesChannel <- measuredValues
}
// ReadContinously reads the measured values continously from the sensor and
// writes them to a channel.
func (s *BME280) ReadContinously(ctx context.Context, measuredValuesChannel chan<- []*types.MeasuredValue, errorChannel chan<- error) {
for {
select {
case <-ctx.Done():
errorChannel <- fmt.Errorf("%v: Context closed: %v", s.SensorName, ctx.Err())
return
default:
s.ReadChannel(measuredValuesChannel, errorChannel, nil)
}
}
}

View File

@ -1,98 +0,0 @@
package sensor
import (
"context"
"fmt"
"sync"
"github.com/go-flucky/flucky/pkg/internal/format"
"github.com/go-flucky/flucky/pkg/types"
"github.com/go-flucky/go-dht"
uuid "github.com/satori/go.uuid"
)
// DHT11 is a sensor to measure humidity and temperature.
type DHT11 struct {
*types.Sensor
}
// GetSensorModel returns the sensor model
func (s *DHT11) GetSensorModel() types.SensorModel {
return s.Sensor.SensorModel
}
// Read measured values
func (s *DHT11) Read() ([]*types.MeasuredValue, error) {
err := dht.HostInit()
if err != nil {
return nil, fmt.Errorf("HostInit error: %v", err)
}
gpio, err := types.GPIOToString(*s.GPIONumber)
if err != nil {
return nil, err
}
dht, err := dht.NewDHT(gpio, dht.Celsius, "")
if err != nil {
return nil, fmt.Errorf("NewDHT error: %v", err)
}
humidityValue, temperatureValue, err := dht.Read()
if err != nil {
return nil, fmt.Errorf("Read error: %v", err)
}
measuredValues := []*types.MeasuredValue{
&types.MeasuredValue{
ID: uuid.NewV4().String(),
Value: float64(humidityValue),
ValueType: types.MeasuredValueTypeHumidity,
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
&types.MeasuredValue{
ID: uuid.NewV4().String(),
Value: float64(temperatureValue),
ValueType: types.MeasuredValueTypeTemperature,
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
}
return measuredValues, nil
}
// ReadChannel reads the measured values from the sensor and writes them to a
// channel.
func (s *DHT11) ReadChannel(measuredValuesChannel chan<- []*types.MeasuredValue, errorChannel chan<- error, wg *sync.WaitGroup) {
if wg != nil {
defer wg.Done()
}
measuredValues, err := s.Read()
if err != nil {
errorChannel <- err
return
}
measuredValuesChannel <- measuredValues
}
// ReadContinously reads the measured values continously from the sensor and
// writes them to a channel.
func (s *DHT11) ReadContinously(ctx context.Context, measuredValuesChannel chan<- []*types.MeasuredValue, errorChannel chan<- error) {
for {
select {
case <-ctx.Done():
errorChannel <- fmt.Errorf("%v: Context closed: %v", s.SensorName, ctx.Err())
return
default:
s.ReadChannel(measuredValuesChannel, errorChannel, nil)
}
}
}

View File

@ -1,98 +0,0 @@
package sensor
import (
"context"
"fmt"
"sync"
"github.com/go-flucky/flucky/pkg/internal/format"
"github.com/go-flucky/flucky/pkg/types"
"github.com/go-flucky/go-dht"
uuid "github.com/satori/go.uuid"
)
// DHT22 is a sensor to measure humidity and temperature.
type DHT22 struct {
*types.Sensor
}
// GetSensorModel returns the sensor model
func (s *DHT22) GetSensorModel() types.SensorModel {
return s.Sensor.SensorModel
}
// Read measured values
func (s *DHT22) Read() ([]*types.MeasuredValue, error) {
err := dht.HostInit()
if err != nil {
return nil, fmt.Errorf("HostInit error: %v", err)
}
gpio, err := types.GPIOToString(*s.GPIONumber)
if err != nil {
return nil, err
}
dht, err := dht.NewDHT(gpio, dht.Celsius, "")
if err != nil {
return nil, fmt.Errorf("NewDHT error: %v", err)
}
humidityValue, temperatureValue, err := dht.Read()
if err != nil {
return nil, fmt.Errorf("Read error: %v", err)
}
measuredValues := []*types.MeasuredValue{
&types.MeasuredValue{
ID: uuid.NewV4().String(),
Value: float64(humidityValue),
ValueType: types.MeasuredValueTypeHumidity,
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
&types.MeasuredValue{
ID: uuid.NewV4().String(),
Value: float64(temperatureValue),
ValueType: types.MeasuredValueTypeTemperature,
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
}
return measuredValues, nil
}
// ReadChannel reads the measured values from the sensor and writes them to a
// channel.
func (s *DHT22) ReadChannel(measuredValuesChannel chan<- []*types.MeasuredValue, errorChannel chan<- error, wg *sync.WaitGroup) {
if wg != nil {
defer wg.Done()
}
measuredValues, err := s.Read()
if err != nil {
errorChannel <- err
return
}
measuredValuesChannel <- measuredValues
}
// ReadContinously reads the measured values continously from the sensor and
// writes them to a channel.
func (s *DHT22) ReadContinously(ctx context.Context, measuredValuesChannel chan<- []*types.MeasuredValue, errorChannel chan<- error) {
for {
select {
case <-ctx.Done():
errorChannel <- fmt.Errorf("%v: Context closed: %v", s.SensorName, ctx.Err())
return
default:
s.ReadChannel(measuredValuesChannel, errorChannel, nil)
}
}
}

View File

@ -1,97 +0,0 @@
package sensor
import (
"context"
"fmt"
"io/ioutil"
"path/filepath"
"strconv"
"strings"
"sync"
"github.com/go-flucky/flucky/pkg/internal/format"
"github.com/go-flucky/flucky/pkg/types"
uuid "github.com/satori/go.uuid"
)
// DS18B20 is a sensor to measure humidity and temperature.
type DS18B20 struct {
*types.Sensor
}
// GetSensorModel returns the sensor model
func (s *DS18B20) GetSensorModel() types.SensorModel {
return s.Sensor.SensorModel
}
// Read measured values
func (s *DS18B20) Read() ([]*types.MeasuredValue, error) {
if s.WireID == nil {
return nil, fmt.Errorf("WireID is not specified for sensor %v", s.Name())
}
data, err := ioutil.ReadFile(filepath.Join("/sys/bus/w1/devices", *s.WireID, "/w1_slave"))
if err != nil {
return nil, fmt.Errorf("Can not read data from sensor %v", s.SensorName)
}
raw := string(data)
i := strings.LastIndex(raw, "t=")
if i == -1 {
return nil, ErrReadSensor
}
c, err := strconv.ParseFloat(raw[i+2:len(raw)-1], 64)
if err != nil {
return nil, ErrParseData
}
temperatureValue := c / 1000
measuredValues := []*types.MeasuredValue{
&types.MeasuredValue{
ID: uuid.NewV4().String(),
Value: float64(temperatureValue),
ValueType: types.MeasuredValueTypeTemperature,
FromDate: format.FormatedTime(),
TillDate: format.FormatedTime(),
SensorID: s.SensorID,
},
}
return measuredValues, nil
}
// ReadChannel reads the measured values from the sensor and writes them to a
// channel.
func (s *DS18B20) ReadChannel(measuredValuesChannel chan<- []*types.MeasuredValue, errorChannel chan<- error, wg *sync.WaitGroup) {
if wg != nil {
defer wg.Done()
}
measuredValues, err := s.Read()
if err != nil {
errorChannel <- err
return
}
measuredValuesChannel <- measuredValues
}
// ReadContinously reads the measured values continously from the sensor and
// writes them to a channel.
func (s *DS18B20) ReadContinously(ctx context.Context, measuredValuesChannel chan<- []*types.MeasuredValue, errorChannel chan<- error) {
for {
select {
case <-ctx.Done():
errorChannel <- fmt.Errorf("%v: Context closed: %v", s.SensorName, ctx.Err())
return
default:
s.ReadChannel(measuredValuesChannel, errorChannel, nil)
}
}
}

View File

@ -1,8 +0,0 @@
package sensor
import (
"errors"
)
var ErrParseData = errors.New("Can not parse data")
var ErrReadSensor = errors.New("Can not read data from Sensor")

View File

@ -1,15 +0,0 @@
package sensor
import (
"context"
"sync"
"github.com/go-flucky/flucky/pkg/types"
)
type Sensor interface {
GetSensorModel() types.SensorModel
Read() ([]*types.MeasuredValue, error)
ReadChannel(measuredValuesChannel chan<- []*types.MeasuredValue, errorChannel chan<- error, wg *sync.WaitGroup)
ReadContinously(ctx context.Context, measuredValuesChannel chan<- []*types.MeasuredValue, errorChannel chan<- error)
}

View File

@ -1,58 +0,0 @@
package sensor
import (
"context"
"fmt"
"sync"
"github.com/go-flucky/flucky/pkg/internal/collect"
"github.com/go-flucky/flucky/pkg/internal/prittyprint"
"github.com/go-flucky/flucky/pkg/types"
)
// Read measured values from sensors
func Read(ctx context.Context, sensors []Sensor) ([]*types.MeasuredValue, error) {
measuredValuesChannel := make(chan []*types.MeasuredValue, len(sensors))
errorChannel := make(chan error, len(sensors))
ReadChannel(ctx, sensors, measuredValuesChannel, errorChannel)
errors := collect.Errors(errorChannel)
if len(errors) > 0 {
return nil, prittyprint.FormatErrors(errors)
}
measuredValues := collect.MeasuredValues(measuredValuesChannel)
return measuredValues, nil
}
// ReadChannel reads the measured values from sensors and writes them to a
// channel.
func ReadChannel(ctx context.Context, sensors []Sensor, measuredValuesChannel chan<- []*types.MeasuredValue, errorChannel chan<- error) {
wg := new(sync.WaitGroup)
wg.Add(len(sensors))
for _, sensor := range sensors {
go sensor.ReadChannel(measuredValuesChannel, errorChannel, wg)
}
wg.Wait()
}
// ReadContinuously reads the measured values continously from sensors and writes
// them to a channel.
func ReadContinuously(ctx context.Context, sensors []Sensor, measuredValuesChannel chan<- []*types.MeasuredValue, errorChannel chan<- error) {
for {
select {
case <-ctx.Done():
errorChannel <- fmt.Errorf("Context closed: %v", ctx.Err())
return
default:
ReadChannel(ctx, sensors, measuredValuesChannel, errorChannel)
}
}
}

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