package scale import ( "bytes" "encoding/binary" "fmt" "log" "os" "sync" "time" "github.com/currantlabs/ble" influxdb2 "github.com/influxdata/influxdb-client-go" influxdb2_api "github.com/influxdata/influxdb-client-go/api" ) // XiaomiScaleV2UUID is BLE UUID for Xiaomi Scale V2 var XiaomiScaleV2UUID ble.UUID = []byte{0x1b, 0x18} // ParseXiaomiScaleV2 parses V2 scale service data func ParseXiaomiScaleV2(mux *sync.Mutex, writeAPI *influxdb2_api.WriteAPI, advertisement ble.Advertisement, index int, sd ble.ServiceData) { timestamp := time.Now() var scaleData struct { RawUnit uint8 ControlByte uint8 Year uint16 Month uint8 Day uint8 Hour uint8 Minute uint8 Second uint8 Impedance uint16 RawWeight uint16 } err := binary.Read(bytes.NewBuffer(sd.Data), binary.LittleEndian, &scaleData) if err != nil { panic(err) } isStabilized := (scaleData.ControlByte & (1 << 5)) != 0 hasImpedance := (scaleData.ControlByte & (1 << 1)) != 0 var originalUnit string var weight float64 switch scaleData.RawUnit { case 3: originalUnit = "lbs" weight = float64(scaleData.RawWeight) / 100.0 * 0.453592 case 2: originalUnit = "kg" weight = float64(scaleData.RawWeight) / 200.0 default: originalUnit = "unknown" weight = float64(scaleData.RawWeight) } if isStabilized { loc, err := time.LoadLocation("Local") if err != nil { log.Fatal(err) } scaleTimestamp := time.Date( int(scaleData.Year), time.Month(scaleData.Month), int(scaleData.Day), int(scaleData.Hour), int(scaleData.Minute), int(scaleData.Second), 0, loc) difference := timestamp.Sub(scaleTimestamp) point := influxdb2.NewPointWithMeasurement("sensor") point.SetTime(scaleTimestamp) hostname, _ := os.Hostname() if hostname != "" { point.AddTag("detector", hostname) } point.AddTag("address", advertisement.Address().String()) point.AddTag("weight_unit", "kg") point.AddTag("original_weight_unit", originalUnit) point.AddField("rssi", advertisement.RSSI()) point.AddField("weight", weight) if hasImpedance { point.AddField("impedance", scaleData.Impedance) } (*writeAPI).WritePoint(point) mux.Lock() fmt.Printf("*******************************************\n") fmt.Printf("Xiaomi V2 Scale\n") fmt.Printf("[%s] %3d:\n", advertisement.Address(), advertisement.RSSI()) if len(advertisement.LocalName()) > 0 { fmt.Printf(" Name: %s\n", advertisement.LocalName()) } if len(advertisement.Services()) > 0 { fmt.Printf(" Svcs: %v\n", advertisement.Services()) } if len(advertisement.ManufacturerData()) > 0 { fmt.Printf(" MD: %v\n", advertisement.ManufacturerData()) } fmt.Printf(" rawUnit: %d\n", scaleData.RawUnit) fmt.Printf(" Control byte: %d %b\n", scaleData.ControlByte, scaleData.ControlByte) fmt.Printf(" Control byte X: %d\n", scaleData.ControlByte&^(1<<5)&^(1<<1)) fmt.Printf(" Is stabilized: %v\n", isStabilized) fmt.Printf(" Has impedance: %v\n", hasImpedance) fmt.Printf(" Year: %d\n", scaleData.Year) fmt.Printf(" Month: %d\n", scaleData.Month) fmt.Printf(" Day: %v\n", scaleData.Day) fmt.Printf(" Hour: %d\n", scaleData.Hour) fmt.Printf(" Minute: %d\n", scaleData.Minute) fmt.Printf(" Second: %d\n", scaleData.Second) fmt.Printf(" Measurement taken %s ago\n", difference) fmt.Printf(" Impedance: %d\n", scaleData.Impedance) fmt.Printf(" Raw weight: %d\n", scaleData.RawWeight) fmt.Printf(" Weight: %f %s\n", weight, originalUnit) fmt.Printf("*******************************************\n") mux.Unlock() } }