ble-sensors/lib/xiaomi/mijia/sensor.go
Jeffrey C. Ollie 523a561015
Some checks failed
continuous-integration/drone/push Build is failing
much work
2020-08-13 00:13:15 -05:00

322 lines
12 KiB
Go

package mijia
import (
"encoding/binary"
"fmt"
"time"
"github.com/go-ble/ble"
influxdb2 "github.com/influxdata/influxdb-client-go"
influxdb2_api "github.com/influxdata/influxdb-client-go/api"
"go.uber.org/zap"
)
// 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
)
const (
isFactoryNewFlag = (1 << iota)
isConnectedFlag
isCentralFlag
isEncryptedFlag
hasMacAddressFlag
hasCapabilitiesFlag
hasEventFlag
hasCustomDataFlag
hasSubtitleFlag
hasBindingFlag
)
// ParseXiaomiMijiaSensorData parses
func ParseXiaomiMijiaSensorData(writeAPI *influxdb2_api.WriteAPI, logger *zap.Logger, timestamp time.Time, detector string, description string, advertisement ble.Advertisement, index int, sd ble.ServiceData) {
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&isFactoryNewFlag != 0
sensorData.isConnected = sensorData.frameControl&isConnectedFlag != 0
sensorData.isCentral = sensorData.frameControl&isCentralFlag != 0
sensorData.isEncrypted = sensorData.frameControl&isEncryptedFlag != 0
sensorData.hasMacAddress = sensorData.frameControl&hasMacAddressFlag != 0
sensorData.hasCapabilities = sensorData.frameControl&hasCapabilitiesFlag != 0
sensorData.hasEvent = sensorData.frameControl&hasEventFlag != 0
sensorData.hasCustomData = sensorData.frameControl&hasCustomDataFlag != 0
sensorData.hasSubtitle = sensorData.frameControl&hasSubtitleFlag != 0
sensorData.hasBinding = sensorData.frameControl&hasBindingFlag != 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
logger.Debug("mijia capabilities",
zap.Bool("connectable", sensorData.capabilityData.connectable),
zap.Bool("central", sensorData.capabilityData.central),
zap.Bool("secure", sensorData.capabilityData.secure),
zap.Uint8("io", sensorData.capabilityData.io))
}
if sensorData.isEncrypted {
logger.Warn("packet is encrypted")
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])
switch sensorData.eventData.eventType {
case temperatureEvent:
sensorData.eventData.temperature = float64(int16(binary.LittleEndian.Uint16(sd.Data[eventOffset+3:]))) / 10.0
point := influxdb2.NewPointWithMeasurement("sensor")
point.SetTime(timestamp)
if detector != "" {
point.AddTag("detector", detector)
}
point.AddTag("address", advertisement.Addr().String())
point.AddTag("product_id", fmt.Sprintf("%x", sensorData.productID))
if description != "" {
point.AddTag("description", description)
}
point.AddTag("measaurement", "temperature")
point.AddTag("measurement_unit", "°C")
point.AddField("value", sensorData.eventData.temperature)
(*writeAPI).WritePoint(point)
logger.Debug("sending sensor reading",
zap.String("source", advertisement.Addr().String()),
zap.String("description", description),
zap.String("measurement", "temperature"),
zap.String("measurement_unit", "°C"),
zap.Float64("value", sensorData.eventData.temperature))
case humidityEvent:
sensorData.eventData.humidity = float64(binary.LittleEndian.Uint16(sd.Data[eventOffset+3:])) / 10.0
point := influxdb2.NewPointWithMeasurement("sensor")
point.SetTime(timestamp)
if detector != "" {
point.AddTag("detector", detector)
}
point.AddTag("address", advertisement.Addr().String())
point.AddTag("product_id", fmt.Sprintf("%x", sensorData.productID))
if description != "" {
point.AddTag("description", description)
}
point.AddTag("measaurement", "humidity")
point.AddTag("measaurement_unit", "%")
point.AddField("value", sensorData.eventData.humidity)
(*writeAPI).WritePoint(point)
logger.Debug("sending sensor reading",
zap.String("source", advertisement.Addr().String()),
zap.String("description", description),
zap.String("measurement", "humidity"),
zap.String("measurement_unit", "%"),
zap.Float64("value", 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 := influxdb2.NewPointWithMeasurement("sensor")
point.SetTime(timestamp)
if detector != "" {
point.AddTag("detector", detector)
}
point.AddTag("address", advertisement.Addr().String())
point.AddTag("product_id", fmt.Sprintf("%x", sensorData.productID))
if description != "" {
point.AddTag("description", description)
}
point.AddTag("measurement", "illuminance")
point.AddTag("measurement_unit", "lx")
point.AddField("value", sensorData.eventData.illuminance)
(*writeAPI).WritePoint(point)
logger.Debug("sending sensor reading",
zap.String("source", advertisement.Addr().String()),
zap.String("description", description),
zap.String("measurement", "illuminance"),
zap.String("measurement_unit", "lx"),
zap.Uint("value", sensorData.eventData.illuminance))
case moistureEvent:
sensorData.eventData.moisture = int8(sd.Data[eventOffset+3])
point := influxdb2.NewPointWithMeasurement("sensor")
point.SetTime(timestamp)
if detector != "" {
point.AddTag("detector", detector)
}
point.AddTag("address", advertisement.Addr().String())
point.AddTag("product_id", fmt.Sprintf("%x", sensorData.productID))
if description != "" {
point.AddTag("description", description)
}
point.AddTag("measurement", "moisture")
point.AddTag("measurement_unit", "%")
point.AddField("value", sensorData.eventData.moisture)
(*writeAPI).WritePoint(point)
logger.Debug("sending sensor reading",
zap.String("source", advertisement.Addr().String()),
zap.String("description", description),
zap.String("measurement", "moisture"),
zap.String("measurement_unit", "%"),
zap.Int8("value", sensorData.eventData.moisture))
case conductivityEvent:
sensorData.eventData.conductivity = int16(binary.LittleEndian.Uint16(sd.Data[eventOffset+3:]))
point := influxdb2.NewPointWithMeasurement("sensor")
point.SetTime(timestamp)
if detector != "" {
point.AddTag("detector", detector)
}
point.AddTag("address", advertisement.Addr().String())
point.AddTag("product_id", fmt.Sprintf("%x", sensorData.productID))
if description != "" {
point.AddTag("description", description)
}
point.AddTag("measurement", "conductivity")
point.AddTag("measurement_unit", "µS/cm")
point.AddField("value", sensorData.eventData.conductivity)
(*writeAPI).WritePoint(point)
logger.Debug("sending sensor reading",
zap.String("source", advertisement.Addr().String()),
zap.String("description", description),
zap.String("measurement", "conducivity"),
zap.String("measurement_unit", "µS/cm"),
zap.Int16("value", sensorData.eventData.conductivity))
case batteryEvent:
sensorData.eventData.battery = uint8(sd.Data[eventOffset+3])
point := influxdb2.NewPointWithMeasurement("sensor")
point.SetTime(timestamp)
if detector != "" {
point.AddTag("detector", detector)
}
point.AddTag("address", advertisement.Addr().String())
point.AddTag("product_id", fmt.Sprintf("%x", sensorData.productID))
if description != "" {
point.AddTag("description", description)
}
point.AddTag("measurement", "battery")
point.AddTag("measurement_unit", "%")
point.AddField("value", sensorData.eventData.battery)
(*writeAPI).WritePoint(point)
logger.Debug("sending sensor reading",
zap.String("source", advertisement.Addr().String()),
zap.String("description", description),
zap.String("measurement", "battery"),
zap.String("measurement_unit", "%"),
zap.Uint8("value", sensorData.eventData.battery))
case temperatureAndHumidityEvent:
sensorData.eventData.temperature = float64(int16(binary.LittleEndian.Uint16(sd.Data[eventOffset+3:]))) / 10.0
point := influxdb2.NewPointWithMeasurement("sensor")
point.SetTime(timestamp)
if detector != "" {
point.AddTag("detector", detector)
}
point.AddTag("address", advertisement.Addr().String())
point.AddTag("product_id", fmt.Sprintf("%x", sensorData.productID))
if description != "" {
point.AddTag("description", description)
}
point.AddTag("measurement", "temperature")
point.AddTag("measurement_unit", "°C")
point.AddField("value", sensorData.eventData.temperature)
(*writeAPI).WritePoint(point)
logger.Debug("sending sensor reading",
zap.String("source", advertisement.Addr().String()),
zap.String("description", description),
zap.String("measurement", "temperature"),
zap.String("measurement_unit", "°C"),
zap.Float64("value", sensorData.eventData.temperature))
sensorData.eventData.humidity = float64(binary.LittleEndian.Uint16(sd.Data[eventOffset+5:])) / 10.0
point = influxdb2.NewPointWithMeasurement("sensor")
point.SetTime(timestamp)
if detector != "" {
point.AddTag("detector", detector)
}
point.AddTag("address", advertisement.Addr().String())
point.AddTag("product_id", fmt.Sprintf("%x", sensorData.productID))
if description != "" {
point.AddTag("description", description)
}
point.AddTag("measurement", "humidity")
point.AddTag("measurement_unit", "%")
point.AddField("value", sensorData.eventData.humidity)
(*writeAPI).WritePoint(point)
logger.Debug("sending sensor reading",
zap.String("source", advertisement.Addr().String()),
zap.String("description", description),
zap.String("measurement", "humidity"),
zap.String("measurement_unit", "%"),
zap.Float64("value", sensorData.eventData.humidity))
default:
logger.Debug("unknown event type",
zap.String("source", advertisement.Addr().String()),
zap.String("description", description),
zap.Uint16("event_type", sensorData.eventData.eventType),
zap.Binary("data", sd.Data),
zap.Binary("event_data", sd.Data[eventOffset+3:]))
}
}
}