ble-sensors/lib/xiaomi/mijia/sensor.go
2020-08-08 19:00:41 -05:00

188 lines
6.3 KiB
Go

package mijia
import (
"encoding/binary"
"fmt"
"os"
"sync"
"time"
"git.ocjtech.us/jeff/bluelisten/lib/bluetooth"
"github.com/currantlabs/ble"
influxdb2 "github.com/influxdata/influxdb-client-go"
influxdb2_api "github.com/influxdata/influxdb-client-go/api"
)
var debug = false
// XiaomiMijiaHTV1UUID is the Bluetooth UUID for Xiaomi Mijia BLE sensor data
var XiaomiMijiaHTV1UUID ble.UUID = []byte{0x95, 0xfe}
const (
temperatureEvent = 0x1004
humidityEvent = 0x1006
illuminanceEvent = 0x1007
moistureEvent = 0x1008
conductivityEvent = 0x1009
batteryEvent = 0x100a
temperatureAndHumidityEvent = 0x100d
)
// ParseXiaomiMijiaSensorData parses
func ParseXiaomiMijiaSensorData(mux *sync.Mutex, writeAPI *influxdb2_api.WriteAPI, advertisement ble.Advertisement, index int, sd ble.ServiceData) {
timestamp := time.Now()
var sensorData struct {
frameControl uint16
isFactoryNew bool
isConnected bool
isCentral bool
isEncrypted bool
hasMacAddress bool
hasCapabilities bool
hasEvent bool
hasCustomData bool
hasSubtitle bool
hasBinding bool
version uint16
productID uint16
frameCounter uint8
macAddress []byte
capabilities uint8
capabilityData struct {
connectable bool
central bool
secure bool
io uint8
}
eventData struct {
eventType uint16
eventLength uint8
temperature float64
humidity float64
battery uint8
illuminance uint
conductivity int16
moisture int8
}
}
sensorData.frameControl = binary.LittleEndian.Uint16(sd.Data[0:])
sensorData.isFactoryNew = sensorData.frameControl&(1<<0) != 0
sensorData.isConnected = sensorData.frameControl&(1<<1) != 0
sensorData.isCentral = sensorData.frameControl&(1<<2) != 0
sensorData.isEncrypted = sensorData.frameControl&(1<<3) != 0
sensorData.hasMacAddress = sensorData.frameControl&(1<<4) != 0
sensorData.hasCapabilities = sensorData.frameControl&(1<<5) != 0
sensorData.hasEvent = sensorData.frameControl&(1<<6) != 0
sensorData.hasCustomData = sensorData.frameControl&(1<<7) != 0
sensorData.hasSubtitle = sensorData.frameControl&(1<<8) != 0
sensorData.hasBinding = sensorData.frameControl&(1<<9) != 0
sensorData.version = (sensorData.frameControl >> 12) & 0x0f
sensorData.productID = binary.LittleEndian.Uint16(sd.Data[2:])
sensorData.frameCounter = uint8(sd.Data[4])
if sensorData.hasMacAddress {
sensorData.macAddress = sd.Data[5 : 5+6]
for i, j := 0, len(sensorData.macAddress)-1; i < j; i, j = i+1, j-1 {
sensorData.macAddress[i], sensorData.macAddress[j] = sensorData.macAddress[j], sensorData.macAddress[i]
}
}
if sensorData.hasCapabilities {
var capabilityOffset int = 11
if !sensorData.hasMacAddress {
capabilityOffset = 5
}
sensorData.capabilities = uint8(sd.Data[capabilityOffset])
sensorData.capabilityData.connectable = sensorData.capabilities&(1<<0) != 0
sensorData.capabilityData.central = sensorData.capabilities&(1<<1) != 0
sensorData.capabilityData.secure = sensorData.capabilities&(1<<2) != 0
sensorData.capabilityData.io = sensorData.capabilities & ((1 << 3) | (1 << 4)) // != 0
}
if sensorData.isEncrypted {
if debug {
fmt.Printf("Need to decrypt!\n")
}
return
}
if sensorData.hasEvent {
var eventOffset int = 5
if sensorData.hasMacAddress {
eventOffset = 11
}
if sensorData.hasCapabilities {
eventOffset++
}
sensorData.eventData.eventType = binary.LittleEndian.Uint16(sd.Data[eventOffset:])
sensorData.eventData.eventLength = uint8(sd.Data[eventOffset+2])
point := influxdb2.NewPointWithMeasurement("sensor")
point.SetTime(timestamp)
detector, _ := os.Hostname()
if detector != "" {
point.AddTag("detector", detector)
}
point.AddTag("address", advertisement.Address().String())
point.AddTag("product_id", fmt.Sprintf("%x", sensorData.productID))
description := bluetooth.GetDescription(advertisement.Address())
if description != "" {
point.AddTag("description", description)
}
point.AddField("rssi", advertisement.RSSI())
switch sensorData.eventData.eventType {
case temperatureEvent:
sensorData.eventData.temperature = float64(int16(binary.LittleEndian.Uint16(sd.Data[eventOffset+3:]))) / 10.0
point.AddTag("temperature_unit", "°C")
point.AddField("temperature", sensorData.eventData.temperature)
case humidityEvent:
sensorData.eventData.humidity = float64(binary.LittleEndian.Uint16(sd.Data[eventOffset+3:])) / 10.0
point.AddTag("humidity_unit", "%")
point.AddField("humidity", sensorData.eventData.humidity)
case illuminanceEvent:
sensorData.eventData.illuminance = uint(sd.Data[eventOffset+3]) + uint(sd.Data[eventOffset+4])<<8 + uint(sd.Data[eventOffset+5])<<16
point.AddTag("illuminance_unit", "lx")
point.AddField("illuminance", sensorData.eventData.illuminance)
case moistureEvent:
sensorData.eventData.moisture = int8(sd.Data[eventOffset+3])
point.AddTag("moisture_unit", "%")
point.AddField("moisture", sensorData.eventData.moisture)
case conductivityEvent:
sensorData.eventData.conductivity = int16(binary.LittleEndian.Uint16(sd.Data[eventOffset+3:]))
point.AddTag("conductivity_unit", "µS/cm")
point.AddField("conductivity", sensorData.eventData.conductivity)
case batteryEvent:
sensorData.eventData.battery = uint8(sd.Data[eventOffset+3])
if debug {
fmt.Printf("battery: %d\n", sensorData.eventData.battery)
}
point.AddTag("battery_unit", "%")
point.AddField("battery", sensorData.eventData.battery)
case temperatureAndHumidityEvent:
sensorData.eventData.temperature = float64(int16(binary.LittleEndian.Uint16(sd.Data[eventOffset+3:]))) / 10.0
sensorData.eventData.humidity = float64(binary.LittleEndian.Uint16(sd.Data[eventOffset+5:])) / 10.0
point.AddTag("temperature_unit", "°C")
point.AddField("temperature", sensorData.eventData.temperature)
point.AddTag("humidity_unit", "%")
point.AddField("humidity", sensorData.eventData.humidity)
default:
if debug {
fmt.Printf("Unknown event type: %d\n", sensorData.eventData.eventType)
}
}
(*writeAPI).WritePoint(point)
}
if debug {
mux.Lock()
fmt.Printf("*******************************************\n")
fmt.Printf("*******************************************\n")
mux.Unlock()
}
}