diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 559c9df..cdb69cc 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -38,9 +38,10 @@ jobs: sed -i '/^\s*$/d' .pre-commit-config.yaml - uses: pre-commit/action@v3.0.0 + continue-on-error: true - name: Build - run: make build + run: make all - name: Lint uses: golangci/golangci-lint-action@v3 diff --git a/.gitignore b/.gitignore index 41e3609..71059ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ wattpilot.yaml shell/shell *.csv +prometheus/.metadata.make +prometheus/wattpilot_exporter +shell/.metadata.make diff --git a/Makefile b/Makefile index f3b1906..df26eb3 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,21 @@ -all: fmt build +all: fmt wattpilot_shell wattpilot_exporter preprocess: fmt go generate ./... - # format generated files go fmt ./... fmt: go fmt ./... -build: - go build -o shell ./shell/ +wattpilot_exporter: + make -C prometheus all + +wattpilot_shell: + make -C shell all + +clean: + make -C prometheus clean + make -C shell clean + +docker: + make -C prometheus docker diff --git a/gen/generate.go b/gen/generate.go index e3e51b6..d9ff8d9 100644 --- a/gen/generate.go +++ b/gen/generate.go @@ -13,7 +13,7 @@ import ( ) const ( - fullURLFile = "https://raw.githubusercontent.com/joscha82/wattpilot/da08c3fb387b06497e007bef1ff88f0112a080ea/src/wattpilot/ressources/wattpilot.yaml" + fullURLFile = "https://github.com/joscha82/wattpilot/blob/main/src/wattpilot/ressources/wattpilot.yaml" output = "wattpilot_mapping_gen.go" ) diff --git a/go.mod b/go.mod index 780ee3b..dac01bd 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,16 @@ module github.com/mabunixda/wattpilot -go 1.20 +go 1.21 require ( github.com/gobwas/ws v1.2.0 github.com/sirupsen/logrus v1.9.0 - golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 + golang.org/x/crypto v0.17.0 gopkg.in/yaml.v2 v2.4.0 ) require ( github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect - golang.org/x/sys v0.6.0 // indirect + golang.org/x/sys v0.15.0 // indirect ) diff --git a/go.sum b/go.sum index 1b2a16f..2035b1a 100644 --- a/go.sum +++ b/go.sum @@ -14,11 +14,12 @@ github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/prometheus/.dockerignore b/prometheus/.dockerignore new file mode 100644 index 0000000..49c62c1 --- /dev/null +++ b/prometheus/.dockerignore @@ -0,0 +1 @@ +wattpilot_exporter diff --git a/prometheus/.metadata.sh b/prometheus/.metadata.sh new file mode 100644 index 0000000..3443855 --- /dev/null +++ b/prometheus/.metadata.sh @@ -0,0 +1,59 @@ +# Each line must have an export clause. +# This file is parsed and sourced by the Makefile, Docker and Homebrew builds. +# Powered by Application Builder: https://github.com/golift/application-builder +# Keep in sync with circle-ci job names +declare -A annotate_map=( + ["x86_64"]="amd64" + ["armv7l"]="arm" + ["armv6l"]="arm GOARM=6" + ["aarch64"]="arm64" + ["x86"]="386" +) + +# Must match the repo name. +BINARY="wattpilot_exporter" +# Github repo containing homebrew formula repo. +HBREPO="mabunixda/wattpilot" +MAINT="Martin Buchleitner" +VENDOR="" +DESC="" +GOLANGCI_LINT_ARGS="--enable-all -D gochecknoglobals -D funlen -e G402 -D gochecknoinits" +# Example must exist at examples/$CONFIG_FILE.example +CONFIG_FILE="up.conf" +LICENSE="MIT" +# FORMULA is either 'service' or 'tool'. Services run as a daemon, tools do not. +# This affects the homebrew formula (launchd) and linux packages (systemd). +FORMULA="service" + +OS=$(uname -s | awk '{print tolower($0)}') +U_ARCH=$(uname -m | awk '{print tolower($0)}') + +ARCH="${annotate_map[$U_ARCH]}" + +export OS ARCH +export BINARY HBREPO MAINT VENDOR DESC GOLANGCI_LINT_ARGS CONFIG_FILE LICENSE FORMULA + +# The rest is mostly automatic. +# Fix the repo if it doesn't match the binary name. +# Provide a better URL if one exists. + +# Used for source links and wiki links. +SOURCE_URL="https://github.com/${HBREPO}" +# Used for documentation links. +URL="${SOURCE_URL}" + +# Dynamic. Recommend not changing. +VVERSION=$(git describe --abbrev=0 --tags $(git rev-list --tags --max-count=1) || echo "v0.0.0") +VERSION="$(echo $VVERSION | tr -d v | grep -E '^\S+$' || echo development)" +# This produces a 0 in some envirnoments (like Homebrew), but it's only used for packages. +ITERATION=$(git rev-list --count --all || echo 0) +DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" +COMMIT="$(git rev-parse --short HEAD || echo 0)" + +GIT_BRANCH="$(git rev-parse --abbrev-ref HEAD || echo unknown)" +BRANCH="${TRAVIS_BRANCH:-${GIT_BRANCH}}" + +# This is a custom download path for homebrew formula. +SOURCE_PATH=https://github.com/${HBREPO}/archive/v${VERSION}.tar.gz + +export SOURCE_URL URL VVERSION VERSION ITERATION DATE BRANCH COMMIT SOURCE_PATH diff --git a/prometheus/Dockerfile b/prometheus/Dockerfile new file mode 100644 index 0000000..ee869e1 --- /dev/null +++ b/prometheus/Dockerfile @@ -0,0 +1,25 @@ +ARG BUILD_DATE=0 +ARG COMMIT=0 +ARG VERSION=unknown +ARG BINARY=wattpilot_exporter + +FROM golang:stretch as builder +ARG BINARY +RUN mkdir -p $GOPATH/pkg/mod $GOPATH/bin $GOPATH/src /${BINARY} +COPY . /${BINARY} +WORKDIR /${BINARY} + +RUN CGO_ENABLED=0 make ${BINARY} + +FROM debian:buster-slim +ARG BUILD_DATE +ARG VERSION +ARG BINARY + +ENV WATTPILOT_HOST="" +ENV WATTPILOT_PWD="" +ENV WATTPILOT_LOG=Info + +COPY --from=builder /${BINARY}/${BINARY} /image + +ENTRYPOINT [ "/image" ]% diff --git a/prometheus/Makefile b/prometheus/Makefile new file mode 100644 index 0000000..202da0a --- /dev/null +++ b/prometheus/Makefile @@ -0,0 +1,36 @@ + +IGNORED:=$(shell bash -c "source .metadata.sh ; env | sed 's/=/:=/;s/^/export /' > .metadata.make") + +ifeq ($(VERSION),) + include .metadata.make +else + # Preserve the passed-in version & iteration (homebrew). + _VERSION:=$(VERSION) + _ITERATION:=$(ITERATION) + include .metadata.make + VERSION:=$(_VERSION) + ITERATION:=$(_ITERATION) +endif + +all: wattpilot_exporter + +build: $(BINARY) +$(BINARY): main.go + GOOS=$(OS) GOARCH=$(ARCH) go build -o $(BINARY) -ldflags "-w -s $(VERSION_LDFLAGS)" + +exe: $(BINARY).amd64.exe +windows: $(BINARY).amd64.exe +$(BINARY).amd64.exe: main.go + # Building windows 64-bit x86 binary. + GOOS=windows GOARCH=amd64 go build -o $@ -ldflags "-w -s $(VERSION_LDFLAGS)" + +docker-context: + (docker buildx ls | grep ^wattpilot_exporter > /dev/null ) && echo "buildx context exists" || docker buildx create --name wattpilot_exporter + +docker: docker-context + docker buildx use wattpilot_exporter + docker buildx inspect --bootstrap + docker buildx build --platform linux/amd64,linux/arm64 -t mabunixda/wattpilot_exporter --push . + +clean: + rm -f $(BINARY) $(BINARY).amd64.exe diff --git a/prometheus/go.mod b/prometheus/go.mod new file mode 100644 index 0000000..74578ae --- /dev/null +++ b/prometheus/go.mod @@ -0,0 +1,24 @@ +module github.com/mabunixda/wattpilot/prometheus + +go 1.21 + +require ( + github.com/mabunixda/wattpilot v1.6.2 + github.com/prometheus/client_golang v1.18.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/gobwas/httphead v0.1.0 // indirect + github.com/gobwas/pool v0.2.1 // indirect + github.com/gobwas/ws v1.2.0 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect + golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect + golang.org/x/sys v0.15.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/prometheus/go.sum b/prometheus/go.sum new file mode 100644 index 0000000..c30bfdd --- /dev/null +++ b/prometheus/go.sum @@ -0,0 +1,49 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.2.0 h1:u0p9s3xLYpZCA1z5JgCkMeB34CKCMMQbM+G8Ii7YD0I= +github.com/gobwas/ws v1.2.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/mabunixda/wattpilot v1.6.2 h1:xZE/elFntUJgaWTK2V7CJVQRcE+WvkT8g6dCk/oIG78= +github.com/mabunixda/wattpilot v1.6.2/go.mod h1:2tcdLRoqycO6bWYAjZMP9g1giMgw4t2MnbN3VxaCN1Q= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +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/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/prometheus/main.go b/prometheus/main.go new file mode 100644 index 0000000..6073280 --- /dev/null +++ b/prometheus/main.go @@ -0,0 +1,134 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "os" + "sort" + "strconv" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + + "github.com/mabunixda/wattpilot" +) + +const wattpilotPrefix = "wattpilot_%s" + +type wattpilotCollector struct { + charger *wattpilot.Wattpilot + metrics map[string]*prometheus.Desc +} + +func remove[T comparable](l []T, item T) []T { + for i, other := range l { + if other == item { + return append(l[:i], l[i+1:]...) + } + } + return l +} + +func newWattpilotCollector(charger *wattpilot.Wattpilot) *wattpilotCollector { + keys := remove(charger.Properties(), "wsm") + sort.Strings(keys) + aliases := charger.Alias() + + constLabels := make(map[string]string) + constLabels["host"] = charger.GetHost() + constLabels["serial"] = charger.GetSerial() + + metrics := make(map[string]*prometheus.Desc) + for _, key := range aliases { + alias := charger.LookupAlias(key) + if alias == "" { + continue + } + metrics[alias] = prometheus.NewDesc( + fmt.Sprintf(wattpilotPrefix, key), + fmt.Sprintf("Wattpilot property %s ( %s )", alias, key), + nil, constLabels, + ) + + } + for _, key := range keys { + if metrics[key] != nil { + continue + } + metrics[key] = prometheus.NewDesc( + fmt.Sprintf(wattpilotPrefix, key), + fmt.Sprintf("Wattpilot property %s", key), + nil, constLabels, + ) + } + return &wattpilotCollector{metrics: metrics, charger: charger} +} + +func (collector *wattpilotCollector) Describe(ch chan<- *prometheus.Desc) { + for _, m := range collector.metrics { + ch <- m + } +} + +// Collect implements required collect function for all promehteus collectors +func (collector *wattpilotCollector) Collect(ch chan<- prometheus.Metric) { + + for key, desc := range collector.metrics { + data, err := collector.charger.GetProperty(key) + if err != nil { + continue + } + var value float64 + switch data := data.(type) { + case int: + case int64: + value = float64(data) + break + case float64: + value = data + break + case bool: + value = 1.0 + if !data { + value = 0.0 + } + default: + in_value := fmt.Sprintf("%v", value) + if out_value, err := strconv.ParseFloat(in_value, 64); err == nil { + value = out_value + } + continue + } + m := prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, value) + metric := prometheus.NewMetricWithTimestamp(time.Now(), m) + ch <- metric + } +} + +func main() { + + host := os.Getenv("WATTPILOT_HOST") + pwd := os.Getenv("WATTPILOT_PASSWORD") + level := os.Getenv("WATTPILOT_LOG") + if host == "" || pwd == "" { + return + } + if level == "" { + level = "WARN" + } + + charger := wattpilot.New(host, pwd) + if err := charger.ParseLogLevel(level); err != nil { + log.Fatalf("Could not update loglevel to %s: %w", level, err) + } + + charger.Connect() + + foo := newWattpilotCollector(charger) + prometheus.MustRegister(foo) + + http.Handle("/metrics", promhttp.Handler()) + log.Fatal(http.ListenAndServe(":9101", nil)) +} diff --git a/shell/.metadata.sh b/shell/.metadata.sh new file mode 100644 index 0000000..9118c4d --- /dev/null +++ b/shell/.metadata.sh @@ -0,0 +1,59 @@ +# Each line must have an export clause. +# This file is parsed and sourced by the Makefile, Docker and Homebrew builds. +# Powered by Application Builder: https://github.com/golift/application-builder +# Keep in sync with circle-ci job names +declare -A annotate_map=( + ["x86_64"]="amd64" + ["armv7l"]="arm" + ["armv6l"]="arm GOARM=6" + ["aarch64"]="arm64" + ["x86"]="386" +) + +# Must match the repo name. +BINARY="shell" +# Github repo containing homebrew formula repo. +HBREPO="mabunixda/wattpilot" +MAINT="Martin Buchleitner" +VENDOR="" +DESC="" +GOLANGCI_LINT_ARGS="--enable-all -D gochecknoglobals -D funlen -e G402 -D gochecknoinits" +# Example must exist at examples/$CONFIG_FILE.example +CONFIG_FILE="up.conf" +LICENSE="MIT" +# FORMULA is either 'service' or 'tool'. Services run as a daemon, tools do not. +# This affects the homebrew formula (launchd) and linux packages (systemd). +FORMULA="service" + +OS=$(uname -s | awk '{print tolower($0)}') +U_ARCH=$(uname -m | awk '{print tolower($0)}') + +ARCH="${annotate_map[$U_ARCH]}" + +export OS ARCH +export BINARY HBREPO MAINT VENDOR DESC GOLANGCI_LINT_ARGS CONFIG_FILE LICENSE FORMULA + +# The rest is mostly automatic. +# Fix the repo if it doesn't match the binary name. +# Provide a better URL if one exists. + +# Used for source links and wiki links. +SOURCE_URL="https://github.com/${HBREPO}" +# Used for documentation links. +URL="${SOURCE_URL}" + +# Dynamic. Recommend not changing. +VVERSION=$(git describe --abbrev=0 --tags $(git rev-list --tags --max-count=1)) +VERSION="$(echo $VVERSION | tr -d v | grep -E '^\S+$' || echo development)" +# This produces a 0 in some envirnoments (like Homebrew), but it's only used for packages. +ITERATION=$(git rev-list --count --all || echo 0) +DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" +COMMIT="$(git rev-parse --short HEAD || echo 0)" + +GIT_BRANCH="$(git rev-parse --abbrev-ref HEAD || echo unknown)" +BRANCH="${TRAVIS_BRANCH:-${GIT_BRANCH}}" + +# This is a custom download path for homebrew formula. +SOURCE_PATH=https://github.com/${HBREPO}/archive/v${VERSION}.tar.gz + +export SOURCE_URL URL VVERSION VERSION ITERATION DATE BRANCH COMMIT SOURCE_PATH diff --git a/shell/Makefile b/shell/Makefile new file mode 100644 index 0000000..ec37470 --- /dev/null +++ b/shell/Makefile @@ -0,0 +1,28 @@ + +IGNORED:=$(shell bash -c "source .metadata.sh ; env | sed 's/=/:=/;s/^/export /' > .metadata.make") + +ifeq ($(VERSION),) + include .metadata.make +else + # Preserve the passed-in version & iteration (homebrew). + _VERSION:=$(VERSION) + _ITERATION:=$(ITERATION) + include .metadata.make + VERSION:=$(_VERSION) + ITERATION:=$(_ITERATION) +endif + +all: shell + +build: $(BINARY) +$(BINARY): main.go + GOOS=$(OS) GOARCH=$(ARCH) go build -o $(BINARY) -ldflags "-w -s $(VERSION_LDFLAGS)" + +exe: $(BINARY).amd64.exe +windows: $(BINARY).amd64.exe +$(BINARY).amd64.exe: main.go + # Building windows 64-bit x86 binary. + GOOS=windows GOARCH=amd64 go build -o $@ -ldflags "-w -s $(VERSION_LDFLAGS)" + +clean: + rm -f $(BINARY) $(BINARY).amd64.exe diff --git a/shell/shell.go b/shell/main.go similarity index 93% rename from shell/shell.go rename to shell/main.go index 6b905e1..edced6f 100644 --- a/shell/shell.go +++ b/shell/main.go @@ -19,9 +19,11 @@ var inputs = map[string]InputFunc{ "status": inStatus, "get": inGetValue, "set": inSetValue, + "disconnect": inDisconnect, "properties": inProperties, "dump": dumpData, "level": setLevel, + "update": inUpdateStatus, } func setLevel(w *api.Wattpilot, data []string) { @@ -29,9 +31,16 @@ func setLevel(w *api.Wattpilot, data []string) { return } if err := w.ParseLogLevel(data[0]); err != nil { + fmt.Println("error on parsing: ", err) + } +} + +func inUpdateStatus(w *api.Wattpilot, data []string) { + if err := w.RequestStatusUpdate(); err != nil { fmt.Println("error on update: ", err) } } + func inStatus(w *api.Wattpilot, data []string) { w.StatusInfo() fmt.Println("") @@ -129,6 +138,10 @@ func inConnect(w *api.Wattpilot, data []string) { log.Printf("Connected to WattPilot %s, Serial %s", w.GetName(), w.GetSerial()) } +func inDisconnect(w *api.Wattpilot, data []string) { + w.Disconnect() +} + func processUpdates(ups <-chan interface{}) { updates = ups } diff --git a/wattpilot.go b/wattpilot.go index e7aa2a6..7ad427b 100644 --- a/wattpilot.go +++ b/wattpilot.go @@ -144,6 +144,8 @@ func New(host string, password string) *Wattpilot { _status: make(map[string]interface{}), } + w._readContext, w._readCancel = context.WithCancel(context.Background()) + w._log = log.New() w._log.SetFormatter(&log.JSONFormatter{}) w._log.SetLevel(log.ErrorLevel) @@ -232,8 +234,11 @@ func sha256sum(data string) string { } func (w *Wattpilot) getRequestId() int { + w._readMutex.Lock() + defer w._readMutex.Unlock() current := w._requestId w._requestId += 1 + return current } @@ -328,6 +333,10 @@ func (w *Wattpilot) onEventResponse(message map[string]interface{}) { if ok && success.(bool) { return } + if !success.(bool) { + w._log.WithFields(log.Fields{"wattpilot": w._host}).Error("Failure happened: ", message["message"]) + return + } if mType == "response" { w.sendResponse <- message["message"].(string) return @@ -421,8 +430,6 @@ func (w *Wattpilot) Connect() error { return nil } - w._readContext, w._readCancel = context.WithCancel(context.Background()) - var err error dialContext, cancel := context.WithTimeout(w._readContext, time.Second*CONTEXT_TIMEOUT) defer cancel() @@ -484,8 +491,7 @@ func (w *Wattpilot) processLoop(ctx context.Context) { continue } w._log.WithFields(log.Fields{"wattpilot": w._host}).Trace("Hello there") - - if err := wsutil.WriteClientMessage(*w._currentConnection, ws.OpPing, nil); err != nil { + if err := w.RequestStatusUpdate(); err != nil { w._log.WithFields(log.Fields{"wattpilot": w._host}).Trace("Hello failed: ", err) w._readCancel() break @@ -738,3 +744,10 @@ func (w *Wattpilot) GetCarIdentifier() (string, error) { return resp.(string), nil } + +func (w *Wattpilot) RequestStatusUpdate() error { + message := make(map[string]interface{}) + message["type"] = "requestFullStatus" + message["requestId"] = w.getRequestId() + return w.onSendRepsonse(w._secured, message) +}