188 lines
6.3 KiB
Go
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()
|
|
}
|
|
}
|