1
0
mirror of https://github.com/cmur2/miflorad.git synced 2025-06-26 12:30:23 +02:00

munin-miflora: refactor into two implementations using go-ble and gatt libraries, have common package

This commit is contained in:
cn
2018-12-14 09:45:13 +01:00
parent 51bb5a93c5
commit e75c04785a
11 changed files with 476 additions and 232 deletions

84
common/ble/impl.go Normal file
View File

@ -0,0 +1,84 @@
package ble
import (
"errors"
"miflorad/common"
"github.com/go-ble/ble"
)
func FindServiceByUUID(services []*ble.Service, u ble.UUID) *ble.Service {
for _, service := range services {
if service.UUID.Equal(u) {
return service
}
}
return nil
}
func FindCharacteristicByUUID(characteristics []*ble.Characteristic, u ble.UUID) *ble.Characteristic {
for _, characteristic := range characteristics {
if characteristic.UUID.Equal(u) {
return characteristic
}
}
return nil
}
func RequestVersionBattery(client ble.Client, profile *ble.Profile) (common.VersionBatteryResponse, error) {
mifloraService := FindServiceByUUID(profile.Services, ble.MustParse(common.MifloraServiceUUID))
if mifloraService == nil {
return common.VersionBatteryResponse{}, errors.New("Failed to get the miflora service")
}
mifloraVersionBatteryChar := FindCharacteristicByUUID(mifloraService.Characteristics, ble.MustParse(common.MifloraCharVersionBatteryUUID))
if mifloraVersionBatteryChar == nil {
return common.VersionBatteryResponse{}, errors.New("Failed to get the version battery characteristic")
}
bytes, err := client.ReadCharacteristic(mifloraVersionBatteryChar)
if err != nil {
return common.VersionBatteryResponse{}, err
}
return common.ParseVersionBattery(bytes), nil
}
func RequestModeChange(client ble.Client, profile *ble.Profile) error {
mifloraService := FindServiceByUUID(profile.Services, ble.MustParse(common.MifloraServiceUUID))
if mifloraService == nil {
return errors.New("Failed to get the miflora service")
}
mifloraModeChangeChar := FindCharacteristicByUUID(mifloraService.Characteristics, ble.MustParse(common.MifloraCharModeChangeUUID))
if mifloraModeChangeChar == nil {
return errors.New("Failed to discover the mode change characteristic")
}
err := client.WriteCharacteristic(mifloraModeChangeChar, common.MifloraGetModeChangeData(), false)
if err != nil {
return err
}
return nil
}
func RequestSensorData(client ble.Client, profile *ble.Profile) (common.SensorDataResponse, error) {
mifloraService := FindServiceByUUID(profile.Services, ble.MustParse(common.MifloraServiceUUID))
if mifloraService == nil {
return common.SensorDataResponse{}, errors.New("Failed to get the miflora service")
}
mifloraSensorDataChar := FindCharacteristicByUUID(mifloraService.Characteristics, ble.MustParse(common.MifloraCharReadSensorDataUUID))
if mifloraSensorDataChar == nil {
return common.SensorDataResponse{}, errors.New("Failed to discover the sensor data characteristic")
}
bytes, err := client.ReadCharacteristic(mifloraSensorDataChar)
if err != nil {
return common.SensorDataResponse{}, err
}
return common.ParseSensorData(bytes), nil
}

View File

@ -1,125 +0,0 @@
package common
import (
"encoding/binary"
"errors"
"math"
"strconv"
"strings"
"github.com/currantlabs/gatt"
)
type VersionBatteryResponse struct {
FirmwareVersion string // as "x.y.z"
BatteryLevel uint8 // in percent 0-100
}
type SensorDataResponse struct {
Temperature float64 // in degree C
Brightness uint32 // in lux
Moisture uint8 // in percent 0-100
Conductivity uint16 // in µS/cm
}
func MifloraGetModeChangeData() []byte {
return []byte{0xa0, 0x1f}
}
var MifloraServiceUUID = gatt.MustParseUUID("00001204-0000-1000-8000-00805f9b34fb")
var MifloraCharModeChangeUUID = gatt.MustParseUUID("00001a00-0000-1000-8000-00805f9b34fb")
var MifloraCharReadSensorDataUUID = gatt.MustParseUUID("00001a01-0000-1000-8000-00805f9b34fb")
var MifloraCharVersionBatteryUUID = gatt.MustParseUUID("00001a02-0000-1000-8000-00805f9b34fb")
func FindServiceByUUID(services []*gatt.Service, u gatt.UUID) *gatt.Service {
for _, service := range services {
if service.UUID().Equal(u) {
return service
}
}
return nil
}
func FindCharacteristicByUUID(characteristics []*gatt.Characteristic, u gatt.UUID) *gatt.Characteristic {
for _, characteristic := range characteristics {
if characteristic.UUID().Equal(u) {
return characteristic
}
}
return nil
}
func MifloraRequestVersionBattery(p gatt.Peripheral) (VersionBatteryResponse, error) {
mifloraService := FindServiceByUUID(p.Services(), MifloraServiceUUID)
if mifloraService == nil {
return VersionBatteryResponse{}, errors.New("Failed to get the miflora service")
}
mifloraVersionBatteryChar := FindCharacteristicByUUID(mifloraService.Characteristics(), MifloraCharVersionBatteryUUID)
if mifloraVersionBatteryChar == nil {
return VersionBatteryResponse{}, errors.New("Failed to get the version battery characteristic")
}
bytes, err := p.ReadCharacteristic(mifloraVersionBatteryChar)
if err != nil {
return VersionBatteryResponse{}, err
}
return VersionBatteryResponse{string(bytes[2:]), uint8(bytes[0])}, nil
}
func MifloraRequestModeChange(p gatt.Peripheral) error {
mifloraService := FindServiceByUUID(p.Services(), MifloraServiceUUID)
if mifloraService == nil {
return errors.New("Failed to get the miflora service")
}
mifloraModeChangeChar := FindCharacteristicByUUID(mifloraService.Characteristics(), MifloraCharModeChangeUUID)
if mifloraModeChangeChar == nil {
return errors.New("Failed to discover the mode change characteristic")
}
err := p.WriteCharacteristic(mifloraModeChangeChar, MifloraGetModeChangeData(), false)
if err != nil {
return err
}
return nil
}
func MifloraRequstSensorData(p gatt.Peripheral) (SensorDataResponse, error) {
mifloraService := FindServiceByUUID(p.Services(), MifloraServiceUUID)
if mifloraService == nil {
return SensorDataResponse{}, errors.New("Failed to get the miflora service")
}
mifloraSensorDataChar := FindCharacteristicByUUID(mifloraService.Characteristics(), MifloraCharReadSensorDataUUID)
if mifloraSensorDataChar == nil {
return SensorDataResponse{}, errors.New("Failed to discover the sensor data characteristic")
}
bytes, err := p.ReadCharacteristic(mifloraSensorDataChar)
if err != nil {
return SensorDataResponse{}, err
}
return SensorDataResponse{
Temperature: float64(binary.LittleEndian.Uint16(bytes[0:2])) / 10.0,
Brightness: binary.LittleEndian.Uint32(bytes[3:7]),
Moisture: uint8(bytes[7]),
Conductivity: binary.LittleEndian.Uint16(bytes[8:10]),
}, nil
}
func (res VersionBatteryResponse) NumericFirmwareVersion() int {
// turns "2.3.4" into 20304
version := 0
parts := strings.Split(res.FirmwareVersion, ".")
for i, part := range parts {
partNumber, err := strconv.Atoi(part)
if err != nil {
version += int(math.Pow10((len(parts)-(i+1))*2)) * partNumber
}
}
return version
}

33
common/datatypes.go Normal file
View File

@ -0,0 +1,33 @@
package common
import (
"math"
"strconv"
"strings"
)
type VersionBatteryResponse struct {
FirmwareVersion string // as "x.y.z"
BatteryLevel uint8 // in percent 0-100
}
type SensorDataResponse struct {
Temperature float64 // in degree C
Brightness uint32 // in lux
Moisture uint8 // in percent 0-100
Conductivity uint16 // in µS/cm
}
func (res VersionBatteryResponse) NumericFirmwareVersion() int {
// turns "2.3.4" into 20304
version := 0
parts := strings.Split(res.FirmwareVersion, ".")
for i, part := range parts {
partNumber, err := strconv.Atoi(part)
if err != nil {
continue
}
version += int(math.Pow10((len(parts)-(i+1))*2)) * partNumber
}
return version
}

86
common/gatt/impl.go Normal file
View File

@ -0,0 +1,86 @@
package gatt
import (
"errors"
"miflorad/common"
"github.com/currantlabs/gatt"
)
var MifloraCharVersionBatteryUUID = gatt.MustParseUUID("00001a02-0000-1000-8000-00805f9b34fb")
func FindServiceByUUID(services []*gatt.Service, u gatt.UUID) *gatt.Service {
for _, service := range services {
if service.UUID().Equal(u) {
return service
}
}
return nil
}
func FindCharacteristicByUUID(characteristics []*gatt.Characteristic, u gatt.UUID) *gatt.Characteristic {
for _, characteristic := range characteristics {
if characteristic.UUID().Equal(u) {
return characteristic
}
}
return nil
}
func MifloraRequestVersionBattery(p gatt.Peripheral) (common.VersionBatteryResponse, error) {
mifloraService := FindServiceByUUID(p.Services(), gatt.MustParseUUID(common.MifloraServiceUUID))
if mifloraService == nil {
return common.VersionBatteryResponse{}, errors.New("Failed to get the miflora service")
}
mifloraVersionBatteryChar := FindCharacteristicByUUID(mifloraService.Characteristics(), MifloraCharVersionBatteryUUID)
if mifloraVersionBatteryChar == nil {
return common.VersionBatteryResponse{}, errors.New("Failed to get the version battery characteristic")
}
bytes, err := p.ReadCharacteristic(mifloraVersionBatteryChar)
if err != nil {
return common.VersionBatteryResponse{}, err
}
return common.ParseVersionBattery(bytes), nil
}
func MifloraRequestModeChange(p gatt.Peripheral) error {
mifloraService := FindServiceByUUID(p.Services(), gatt.MustParseUUID(common.MifloraServiceUUID))
if mifloraService == nil {
return errors.New("Failed to get the miflora service")
}
mifloraModeChangeChar := FindCharacteristicByUUID(mifloraService.Characteristics(), gatt.MustParseUUID(common.MifloraCharModeChangeUUID))
if mifloraModeChangeChar == nil {
return errors.New("Failed to discover the mode change characteristic")
}
err := p.WriteCharacteristic(mifloraModeChangeChar, common.MifloraGetModeChangeData(), false)
if err != nil {
return err
}
return nil
}
func MifloraRequstSensorData(p gatt.Peripheral) (common.SensorDataResponse, error) {
mifloraService := FindServiceByUUID(p.Services(), gatt.MustParseUUID(common.MifloraServiceUUID))
if mifloraService == nil {
return common.SensorDataResponse{}, errors.New("Failed to get the miflora service")
}
mifloraSensorDataChar := FindCharacteristicByUUID(mifloraService.Characteristics(), gatt.MustParseUUID(common.MifloraCharReadSensorDataUUID))
if mifloraSensorDataChar == nil {
return common.SensorDataResponse{}, errors.New("Failed to discover the sensor data characteristic")
}
bytes, err := p.ReadCharacteristic(mifloraSensorDataChar)
if err != nil {
return common.SensorDataResponse{}, err
}
return common.ParseSensorData(bytes), nil
}

32
common/proto.go Normal file
View File

@ -0,0 +1,32 @@
package common
import (
"encoding/binary"
)
const (
MifloraServiceUUID = "00001204-0000-1000-8000-00805f9b34fb"
MifloraCharModeChangeUUID = "00001a00-0000-1000-8000-00805f9b34fb"
MifloraCharReadSensorDataUUID = "00001a01-0000-1000-8000-00805f9b34fb"
MifloraCharVersionBatteryUUID = "00001a02-0000-1000-8000-00805f9b34fb"
)
func MifloraGetModeChangeData() []byte {
return []byte{0xa0, 0x1f}
}
func ParseVersionBattery(bytes []byte) VersionBatteryResponse {
return VersionBatteryResponse{
string(bytes[2:]),
uint8(bytes[0]),
}
}
func ParseSensorData(bytes []byte) SensorDataResponse {
return SensorDataResponse{
Temperature: float64(binary.LittleEndian.Uint16(bytes[0:2])) / 10.0,
Brightness: binary.LittleEndian.Uint32(bytes[3:7]),
Moisture: uint8(bytes[7]),
Conductivity: binary.LittleEndian.Uint16(bytes[8:10]),
}
}