Add Tilt support

This commit is contained in:
Søren Rasmussen 2022-04-29 21:02:33 +02:00
parent 488508a431
commit d28db0788f
10 changed files with 553 additions and 1 deletions

View file

@ -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
View file

@ -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
View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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())
}
})
}
}