Add Tilt support
This commit is contained in:
parent
488508a431
commit
d28db0788f
10 changed files with 553 additions and 1 deletions
|
@ -16,6 +16,7 @@ import (
|
|||
"git.joco.dk/sng/fermentord/internal/metrics"
|
||||
"git.joco.dk/sng/fermentord/pkg/daemon"
|
||||
"git.joco.dk/sng/fermentord/pkg/temperature"
|
||||
"git.joco.dk/sng/fermentord/pkg/tilt"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/nats-io/nats.go"
|
||||
|
@ -48,10 +49,11 @@ func mainLoop(ctx context.Context, wg *sync.WaitGroup, js nats.JetStream, config
|
|||
}
|
||||
defer gpio.Close()
|
||||
|
||||
wg.Add(3)
|
||||
wg.Add(4)
|
||||
go ctrl.Run(ctx, wg)
|
||||
go ingest.Run(ctx, wg, js, config)
|
||||
go temperature.PollSensors(ctx, wg, 1*time.Second, config.Sensors.Weight)
|
||||
go tilt.PollSensors(ctx, wg, 1*time.Minute, 20*time.Second)
|
||||
|
||||
for {
|
||||
select {
|
||||
|
|
10
go.mod
10
go.mod
|
@ -3,21 +3,29 @@ module git.joco.dk/sng/fermentord
|
|||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/JuulLabs-OSS/ble v0.0.0-20200716215611-d4fcc9d598bb
|
||||
github.com/fsnotify/fsnotify v1.5.4
|
||||
github.com/getsentry/sentry-go v0.13.0
|
||||
github.com/nats-io/nats.go v1.15.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.12.2
|
||||
github.com/spf13/viper v1.11.0
|
||||
github.com/warthog618/gpiod v0.8.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/JuulLabs-OSS/cbgo v0.0.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/mgutz/logxi v0.0.0-20161027140823-aebf8a7d67ab // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/nats-io/nats-server/v2 v2.7.1 // indirect
|
||||
github.com/nats-io/nkeys v0.3.0 // indirect
|
||||
|
@ -27,6 +35,8 @@ require (
|
|||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.34.0 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/raff/goble v0.0.0-20190909174656-72afc67d6a99 // indirect
|
||||
github.com/sirupsen/logrus v1.6.0 // indirect
|
||||
github.com/spf13/afero v1.8.2 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
|
|
26
go.sum
26
go.sum
|
@ -38,6 +38,10 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
|
|||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/JuulLabs-OSS/ble v0.0.0-20200716215611-d4fcc9d598bb h1:kIZ7fr8RxucJXNHifPxm71yiWuzpw0SmjlafMzoOd0U=
|
||||
github.com/JuulLabs-OSS/ble v0.0.0-20200716215611-d4fcc9d598bb/go.mod h1:6deIuswYSv6W1l3sM/nonw0OKWtIZCn7ZOWvIREoq2A=
|
||||
github.com/JuulLabs-OSS/cbgo v0.0.1 h1:A5JdglvFot1J9qYR0POZ4qInttpsVPN9lqatjaPp2ro=
|
||||
github.com/JuulLabs-OSS/cbgo v0.0.1/go.mod h1:L4YtGP+gnyD84w7+jN66ncspFRfOYB5aj9QSXaFHmBA=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
|
@ -58,6 +62,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
|||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -164,6 +169,7 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
|
|||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.13.4 h1:0zhec2I8zGnjWcKyLl6i3gPqKANCCn5e9xmviEEeX6s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
|
@ -174,8 +180,18 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mgutz/logxi v0.0.0-20161027140823-aebf8a7d67ab h1:n8cgpHzJ5+EDyDri2s/GC7a9+qK3/YEGnBsd0uS/8PY=
|
||||
github.com/mgutz/logxi v0.0.0-20161027140823-aebf8a7d67ab/go.mod h1:y1pL58r5z2VvAjeG1VLGc8zOQgSOzbKN7kMHPvFXJ+8=
|
||||
github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz/0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
|
@ -232,10 +248,16 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
|||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/raff/goble v0.0.0-20190909174656-72afc67d6a99 h1:JtoVdxWJ3tgyqtnPq3r4hJ9aULcIDDnPXBWxZsdmqWU=
|
||||
github.com/raff/goble v0.0.0-20190909174656-72afc67d6a99/go.mod h1:CxaUhijgLFX0AROtH5mluSY71VqpjQBw9JXE2UKZmc4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
|
||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
|
||||
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
|
||||
|
@ -258,6 +280,7 @@ github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMT
|
|||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/warthog618/gpiod v0.8.0 h1:qxH9XVvWHpTxzWFSndBcujFyNH5zVRzHM63tcmm85o4=
|
||||
github.com/warthog618/gpiod v0.8.0/go.mod h1:a7Csa+IJtDBZ39++zC/6Srjo01qWejt/5velrDWuNkY=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
|
@ -388,6 +411,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -414,6 +438,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
71
pkg/tilt/ibeacon.go
Normal file
71
pkg/tilt/ibeacon.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Alex Howarth
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package tilt
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
)
|
||||
|
||||
//iBeaconType identifies an iBeacon
|
||||
const iBeaconType uint32 = 0x4c000215
|
||||
|
||||
// ErrNotBeacon - the BLE device is not an iBeacon
|
||||
var ErrNotBeacon = errors.New("not an iBeacon")
|
||||
|
||||
// IBeacon data
|
||||
type IBeacon struct {
|
||||
UUID string
|
||||
Major uint16
|
||||
Minor uint16
|
||||
}
|
||||
|
||||
// IsIBeacon to determine if data is an iBeacon
|
||||
func IsIBeacon(data []byte) bool {
|
||||
if len(data) < 25 {
|
||||
return false
|
||||
}
|
||||
|
||||
if binary.BigEndian.Uint32(data) != iBeaconType {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// NewIBeacon creates an iBeacon from a valid BLE event
|
||||
func NewIBeacon(data []byte) (b *IBeacon, err error) {
|
||||
if !IsIBeacon(data) {
|
||||
err = ErrNotBeacon
|
||||
return
|
||||
}
|
||||
|
||||
b = &IBeacon{
|
||||
UUID: hex.EncodeToString(data[4:20]),
|
||||
Major: binary.BigEndian.Uint16(data[20:22]),
|
||||
Minor: binary.BigEndian.Uint16(data[22:24]),
|
||||
}
|
||||
|
||||
return
|
||||
}
|
76
pkg/tilt/ibeacon_test.go
Normal file
76
pkg/tilt/ibeacon_test.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Alex Howarth
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package tilt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewIBeacon(t *testing.T) {
|
||||
tt := []struct {
|
||||
name string
|
||||
data []byte
|
||||
err error
|
||||
uuid string
|
||||
major uint16
|
||||
minor uint16
|
||||
}{
|
||||
{
|
||||
name: "Valid iBeacon",
|
||||
data: []uint8{0x4c, 0x0, 0x2, 0x15, 0xa4, 0x95, 0xbb, 0x30, 0xc5, 0xb1, 0x4b, 0x44, 0xb5, 0x12, 0x13, 0x70, 0xf0, 0x2d, 0x74, 0xde, 0x0, 0x46, 0x3, 0xfc, 0xc5},
|
||||
err: nil,
|
||||
uuid: "a495bb30c5b14b44b5121370f02d74de",
|
||||
major: 70,
|
||||
minor: 1020,
|
||||
},
|
||||
{
|
||||
name: "Invalid iBeacon",
|
||||
data: []uint8{0x4c, 0x0, 0x2, 0x15},
|
||||
err: ErrNotBeacon,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := NewIBeacon(tc.data)
|
||||
|
||||
if tc.err != nil {
|
||||
// expecting an error
|
||||
if !errors.Is(err, tc.err) {
|
||||
t.Fatalf("Expected '%v' error, got '%v' error", tc.err, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if got.UUID != tc.uuid {
|
||||
t.Errorf("Expected %v, got %v", tc.uuid, got.UUID)
|
||||
}
|
||||
if got.Major != tc.major {
|
||||
t.Errorf("Expected %v, got %v", tc.major, got.Major)
|
||||
}
|
||||
if got.Minor != tc.minor {
|
||||
t.Errorf("Expected %v, got %v", tc.minor, got.Minor)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
38
pkg/tilt/main.go
Normal file
38
pkg/tilt/main.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package tilt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func PollSensors(ctx context.Context, wg *sync.WaitGroup, interval time.Duration, scanDuration time.Duration) {
|
||||
if interval < scanDuration {
|
||||
log.Fatal("Unable to use interval < scanDuration")
|
||||
}
|
||||
|
||||
defer wg.Done()
|
||||
ticker := time.NewTicker(interval)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
|
||||
case <-ticker.C:
|
||||
scan(ctx, scanDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func scan(ctx context.Context, timeout time.Duration) {
|
||||
scanner := NewScanner()
|
||||
scanner.Scan(ctx, timeout)
|
||||
|
||||
for _, t := range scanner.Tilts() {
|
||||
color := string(t.Color())
|
||||
metricGravity.WithLabelValues(color).Set(t.Gravity())
|
||||
metricTemp.WithLabelValues(color).Set(t.Celsius())
|
||||
}
|
||||
}
|
30
pkg/tilt/metrics.go
Normal file
30
pkg/tilt/metrics.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
package tilt
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
metricGravity = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: "fermentord",
|
||||
Subsystem: "tilt",
|
||||
Name: "specific_gravity",
|
||||
Help: "Wort specific gravity",
|
||||
},
|
||||
[]string{
|
||||
"color",
|
||||
},
|
||||
)
|
||||
|
||||
metricTemp = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: "fermentord",
|
||||
Subsystem: "tilt",
|
||||
Name: "temperature_degrees_celcius",
|
||||
Help: "Wort temperature in degrees celcius",
|
||||
},
|
||||
[]string{
|
||||
"color",
|
||||
},
|
||||
)
|
||||
)
|
108
pkg/tilt/scanner.go
Normal file
108
pkg/tilt/scanner.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Alex Howarth
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package tilt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/JuulLabs-OSS/ble"
|
||||
"github.com/JuulLabs-OSS/ble/examples/lib/dev"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Scanner for Tilt devices
|
||||
type Scanner struct {
|
||||
devices Devices
|
||||
}
|
||||
|
||||
// Devices stores discovered devices
|
||||
type Devices map[Color]Tilt
|
||||
|
||||
// NewScanner returns a Scanner
|
||||
func NewScanner() *Scanner {
|
||||
return &Scanner{}
|
||||
}
|
||||
|
||||
// Scan finds Tilt devices and times out after a duration
|
||||
func (s *Scanner) Scan(ctx context.Context, timeout time.Duration) error {
|
||||
s.devices = make(map[Color]Tilt)
|
||||
|
||||
d, err := dev.NewDevice("go-tilt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ble.SetDefaultDevice(d)
|
||||
|
||||
ctx2 := ble.WithSigHandler(context.WithTimeout(ctx, timeout))
|
||||
err = ble.Scan(ctx2, false, s.advHandler, advFilter)
|
||||
if err != nil {
|
||||
switch errors.Cause(err) {
|
||||
case nil:
|
||||
case context.DeadlineExceeded:
|
||||
// Finished scanning
|
||||
break
|
||||
|
||||
case context.Canceled:
|
||||
break
|
||||
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func advFilter(a ble.Advertisement) bool {
|
||||
return IsTilt(a.ManufacturerData())
|
||||
}
|
||||
|
||||
func (s *Scanner) advHandler(a ble.Advertisement) {
|
||||
// create iBeacon
|
||||
b, err := NewIBeacon(a.ManufacturerData())
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// create Tilt from iBeacon
|
||||
t, err := NewTilt(b)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
s.HandleTilt(t)
|
||||
}
|
||||
|
||||
// HandleTilt adds a discovered Tilt to a map
|
||||
func (s *Scanner) HandleTilt(t Tilt) {
|
||||
s.devices[t.col] = t
|
||||
}
|
||||
|
||||
// Tilts contains the found devices
|
||||
func (s *Scanner) Tilts() Devices {
|
||||
return s.devices
|
||||
}
|
99
pkg/tilt/tilt.go
Normal file
99
pkg/tilt/tilt.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Alex Howarth
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package tilt
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"log"
|
||||
"math"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// tiltIdentifier iBeacon identifier portion (4c000215) as well as Tilt specific uuid preamble (a495)
|
||||
const tiltIdentifier = "4c000215a495"
|
||||
|
||||
// ErrNotTilt - the BLE device does not match anything in tiltType
|
||||
var ErrNotTilt = errors.New("Not a Tilt iBeacon")
|
||||
|
||||
// Color of the Tilt
|
||||
type Color string
|
||||
|
||||
var tiltType = map[string]Color{
|
||||
"a495bb10c5b14b44b5121370f02d74de": "Red",
|
||||
"a495bb20c5b14b44b5121370f02d74de": "Green",
|
||||
"a495bb30c5b14b44b5121370f02d74de": "Black",
|
||||
"a495bb40c5b14b44b5121370f02d74de": "Purple",
|
||||
"a495bb50c5b14b44b5121370f02d74de": "Orange",
|
||||
"a495bb60c5b14b44b5121370f02d74de": "Blue",
|
||||
"a495bb70c5b14b44b5121370f02d74de": "Yellow",
|
||||
"a495bb80c5b14b44b5121370f02d74de": "Pink",
|
||||
}
|
||||
|
||||
// Tilt struct
|
||||
type Tilt struct {
|
||||
col Color
|
||||
temp uint16
|
||||
sg uint16
|
||||
}
|
||||
|
||||
// NewTilt returns a Tilt from an iBeacon
|
||||
func NewTilt(b *IBeacon) (t Tilt, err error) {
|
||||
if col, ok := tiltType[b.UUID]; ok {
|
||||
t = Tilt{col: col, temp: b.Major, sg: b.Minor}
|
||||
return
|
||||
}
|
||||
err = ErrNotTilt
|
||||
return
|
||||
}
|
||||
|
||||
// IsTilt tests if the data is from a Tilt
|
||||
func IsTilt(d []byte) bool {
|
||||
if len(d) >= 25 && hex.EncodeToString(d)[0:12] == tiltIdentifier {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Tilt) Celsius() float64 {
|
||||
return math.Round(float64(t.temp-32)/1.8*100) / 100
|
||||
}
|
||||
|
||||
func (t *Tilt) Fahrenheit() uint16 {
|
||||
return t.temp
|
||||
}
|
||||
|
||||
func (t *Tilt) Gravity() float64 {
|
||||
return float64(t.sg) / 1000
|
||||
}
|
||||
|
||||
func (t *Tilt) Color() Color {
|
||||
return t.col
|
||||
}
|
||||
|
||||
func (t *Tilt) Print() {
|
||||
log.Printf("Tilt: %v", t.Color())
|
||||
log.Printf("Fahrenheit: %v\n", t.Fahrenheit())
|
||||
log.Printf("Specific Gravity: %v\n", t.Gravity())
|
||||
log.Printf("Celsius: %v\n", t.Celsius())
|
||||
}
|
92
pkg/tilt/tilt_test.go
Normal file
92
pkg/tilt/tilt_test.go
Normal file
|
@ -0,0 +1,92 @@
|
|||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Alex Howarth
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package tilt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewTilt(t *testing.T) {
|
||||
tt := []struct {
|
||||
name string
|
||||
ibeacon *IBeacon
|
||||
color Color
|
||||
celsius float64
|
||||
fahrenheit uint16
|
||||
gravity float64
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Red iBeacon",
|
||||
ibeacon: &IBeacon{UUID: "a495bb10c5b14b44b5121370f02d74de", Major: 70, Minor: 1035},
|
||||
color: Color("Red"),
|
||||
fahrenheit: 70,
|
||||
celsius: 21.11,
|
||||
gravity: 1.035,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "Black iBeacon",
|
||||
ibeacon: &IBeacon{UUID: "a495bb30c5b14b44b5121370f02d74de", Major: 69, Minor: 1065},
|
||||
color: Color("Black"),
|
||||
fahrenheit: 69,
|
||||
celsius: 20.56,
|
||||
gravity: 1.065,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "Not an iBeacon",
|
||||
ibeacon: &IBeacon{UUID: "a495bb99c5b14b44b5121370f02d74de", Major: 1, Minor: 2},
|
||||
err: ErrNotTilt,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := NewTilt(tc.ibeacon)
|
||||
|
||||
if tc.err != nil {
|
||||
// expecting an error
|
||||
if !errors.Is(err, tc.err) {
|
||||
t.Fatalf("Expected '%v' error, got '%v' error", tc.err, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if got.Color() != tc.color {
|
||||
t.Errorf("Expected %v, got %v", tc.color, got.Color())
|
||||
}
|
||||
if got.Celsius() != tc.celsius {
|
||||
t.Errorf("Expected %v, got %v", tc.celsius, got.Celsius())
|
||||
}
|
||||
if got.Fahrenheit() != tc.fahrenheit {
|
||||
t.Errorf("Expected %v, got %v", tc.fahrenheit, got.Fahrenheit())
|
||||
}
|
||||
if got.Gravity() != tc.gravity {
|
||||
t.Errorf("Expected %v, got %v", tc.gravity, got.Gravity())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue