diff --git a/cmd/miflorad/main.go b/cmd/miflorad/main.go index 28c20c2..fc2016d 100644 --- a/cmd/miflorad/main.go +++ b/cmd/miflorad/main.go @@ -20,8 +20,8 @@ import ( ) var ( - scanTimeout = flag.Duration("scantimeout", 6*time.Second, "timeout after that a scan per peripheral will be aborted") - interval = flag.Duration("interval", 15*time.Second, "metrics collection interval") + scanTimeout = flag.Duration("scantimeout", 10*time.Second, "timeout after that a scan per peripheral will be aborted") + interval = flag.Duration("interval", 25*time.Second, "metrics collection interval") readRetries = flag.Int("readretries", 2, "number of times reading will be attempted per peripheral") ) @@ -40,7 +40,8 @@ var ( func checkTooShortInterval() error { numPeripherals := int64(len(flag.Args())) - if (*scanTimeout).Nanoseconds()*int64(*readRetries)*numPeripherals >= (*interval).Nanoseconds() { + numReadRetries := int64(*readRetries) + if (*scanTimeout).Nanoseconds()*numReadRetries*numPeripherals >= (*interval).Nanoseconds() { return errors.New(fmt.Sprintf( "The interval of %s is too short given the scan timeout of %s for %d peripheral(s) with %d retries each! Exiting...\n", *interval, *scanTimeout, numPeripherals, *readRetries)) @@ -49,8 +50,10 @@ func checkTooShortInterval() error { } func readData(peripheral *peripheral, client ble.Client) error { + // re-request meta data (for battery level) if last check more than 24 hours ago + // Source: https://github.com/open-homeautomation/miflora/blob/ffd95c3e616df8843cc8bff99c9b60765b124092/miflora/miflora_poller.py#L92 if time.Since(peripheral.lastMetaDataFetch) >= 24*time.Hour { - metaData, err := impl.RequestVersionBattery(client, client.Profile()) + metaData, err := impl.RequestVersionBattery(client) if err != nil { return errors.Wrap(err, "can't request version battery") } @@ -59,13 +62,13 @@ func readData(peripheral *peripheral, client ble.Client) error { } if peripheral.metaData.RequiresModeChangeBeforeRead() { - err2 := impl.RequestModeChange(client, client.Profile()) + err2 := impl.RequestModeChange(client) if err2 != nil { return errors.Wrap(err2, "can't request mode change") } } - sensorData, err3 := impl.RequestSensorData(client, client.Profile()) + sensorData, err3 := impl.RequestSensorData(client) if err3 != nil { return errors.Wrap(err3, "can't request sensor data") } @@ -78,8 +81,15 @@ func readData(peripheral *peripheral, client ble.Client) error { func connectPeripheral(peripheral *peripheral) error { fmt.Fprintf(os.Stderr, "Scanning for %s...\n", peripheral.id) + // only way to get back the found advertisement, must be buffered! + foundAdvertisementChannel := make(chan ble.Advertisement, 1) + filter := func(adv ble.Advertisement) bool { - return strings.ToUpper(adv.Addr().String()) == strings.ToUpper(peripheral.id) + if strings.ToUpper(adv.Addr().String()) == strings.ToUpper(peripheral.id) { + foundAdvertisementChannel <- adv + return true + } + return false } timeConnectStart := time.Now() @@ -94,6 +104,9 @@ func connectPeripheral(peripheral *peripheral) error { fmt.Println(timeConnectTook) // fmt.Fprintf(os.Stdout, "%s.miflora.%s.connect_time %.2f %d\n", prefix, id, timeConnectTook, time.Now().Unix()) + // foundAdvertisement := <-foundAdvertisementChannel + // fmt.Fprintf(os.Stdout, "%s.miflora.%s.rssi %d %d\n", prefix, id, foundAdvertisement.RSSI(), time.Now().Unix()) + // Source: https://github.com/go-ble/ble/blob/master/examples/basic/explorer/main.go#L53 // Make sure we had the chance to print out the message. done := make(chan struct{}) @@ -138,6 +151,7 @@ func readPeripheral(peripheral *peripheral) error { func readAllPeripherals(quit chan struct{}) { for _, peripheral := range allPeripherals { + // check for quit signal (non-blocking) and terminate select { case <-quit: return @@ -180,14 +194,16 @@ func main() { go func() { fmt.Fprintf(os.Stderr, "Starting miflorad loop with %s interval...\n", *interval) + // populate all peripherals data structure allPeripherals = make([]*peripheral, len(flag.Args())) for i, peripheralID := range flag.Args() { allPeripherals[i] = &peripheral{ id: peripheralID, - lastMetaDataFetch: time.Unix(0, 0), + lastMetaDataFetch: time.Unix(0, 0), // force immediate 1st request } } + // main loop readAllPeripherals(quit) for range intervalTicker.C { readAllPeripherals(quit) diff --git a/cmd/munin-miflora/main.go b/cmd/munin-miflora/main.go index b060e64..980e4a2 100644 --- a/cmd/munin-miflora/main.go +++ b/cmd/munin-miflora/main.go @@ -17,11 +17,11 @@ import ( const discoveryTimeout = 10 * time.Second -func readData(client ble.Client, profile *ble.Profile) { +func readData(client ble.Client) { prefix := flag.Args()[0] id := common.MifloraGetAlphaNumericID(flag.Args()[1]) - metaData, err := impl.RequestVersionBattery(client, profile) + metaData, err := impl.RequestVersionBattery(client) if err != nil { fmt.Fprintf(os.Stderr, "Failed to request version battery, err: %s\n", err) fmt.Fprintf(os.Stdout, "%s.miflora.%s.failed 1 %d\n", prefix, id, time.Now().Unix()) @@ -34,7 +34,7 @@ func readData(client ble.Client, profile *ble.Profile) { fmt.Fprintf(os.Stdout, "%s.miflora.%s.firmware_version %d %d\n", prefix, id, metaData.NumericFirmwareVersion(), time.Now().Unix()) if metaData.RequiresModeChangeBeforeRead() { - err2 := impl.RequestModeChange(client, profile) + err2 := impl.RequestModeChange(client) if err2 != nil { fmt.Fprintf(os.Stderr, "Failed to request mode change, err: %s\n", err2) fmt.Fprintf(os.Stdout, "%s.miflora.%s.failed 1 %d\n", prefix, id, time.Now().Unix()) @@ -42,7 +42,7 @@ func readData(client ble.Client, profile *ble.Profile) { } } - sensorData, err3 := impl.RequestSensorData(client, profile) + sensorData, err3 := impl.RequestSensorData(client) if err3 != nil { fmt.Fprintf(os.Stderr, "Failed to request sensor data, err: %s\n", err3) fmt.Fprintf(os.Stdout, "%s.miflora.%s.failed 1 %d\n", prefix, id, time.Now().Unix()) @@ -118,14 +118,13 @@ func main() { timeReadoutStart := time.Now() - profile, err := client.DiscoverProfile(true) - if err != nil { + if _, err := client.DiscoverProfile(true); err != nil { fmt.Fprintf(os.Stderr, "Failed to discover profile, err: %s\n", err) fmt.Fprintf(os.Stdout, "%s.miflora.%s.failed 1 %d\n", prefix, id, time.Now().Unix()) os.Exit(1) } - readData(client, profile) + readData(client) timeReadoutTook := time.Since(timeReadoutStart).Seconds() fmt.Fprintf(os.Stdout, "%s.miflora.%s.readout_time %.2f %d\n", prefix, id, timeReadoutTook, time.Now().Unix()) diff --git a/common/ble/impl.go b/common/ble/impl.go index 94eee27..eeace73 100644 --- a/common/ble/impl.go +++ b/common/ble/impl.go @@ -7,7 +7,8 @@ import ( "github.com/pkg/errors" ) -func FindServiceByUUID(services []*ble.Service, u ble.UUID) *ble.Service { +func FindServiceByUUID(services []*ble.Service, uuid string) *ble.Service { + u := ble.MustParse(uuid) for _, service := range services { if service.UUID.Equal(u) { return service @@ -16,7 +17,8 @@ func FindServiceByUUID(services []*ble.Service, u ble.UUID) *ble.Service { return nil } -func FindCharacteristicByUUID(characteristics []*ble.Characteristic, u ble.UUID) *ble.Characteristic { +func FindCharacteristicByUUID(characteristics []*ble.Characteristic, uuid string) *ble.Characteristic { + u := ble.MustParse(uuid) for _, characteristic := range characteristics { if characteristic.UUID.Equal(u) { return characteristic @@ -25,13 +27,13 @@ func FindCharacteristicByUUID(characteristics []*ble.Characteristic, u ble.UUID) return nil } -func RequestVersionBattery(client ble.Client, profile *ble.Profile) (common.VersionBatteryResponse, error) { - mifloraService := FindServiceByUUID(profile.Services, ble.MustParse(common.MifloraServiceUUID)) +func RequestVersionBattery(client ble.Client) (common.VersionBatteryResponse, error) { + mifloraService := FindServiceByUUID(client.Profile().Services, common.MifloraServiceUUID) if mifloraService == nil { return common.VersionBatteryResponse{}, errors.New("Failed to get the miflora service") } - mifloraVersionBatteryChar := FindCharacteristicByUUID(mifloraService.Characteristics, ble.MustParse(common.MifloraCharVersionBatteryUUID)) + mifloraVersionBatteryChar := FindCharacteristicByUUID(mifloraService.Characteristics, common.MifloraCharVersionBatteryUUID) if mifloraVersionBatteryChar == nil { return common.VersionBatteryResponse{}, errors.New("Failed to get the version battery characteristic") } @@ -44,13 +46,13 @@ func RequestVersionBattery(client ble.Client, profile *ble.Profile) (common.Vers return common.ParseVersionBattery(bytes), nil } -func RequestModeChange(client ble.Client, profile *ble.Profile) error { - mifloraService := FindServiceByUUID(profile.Services, ble.MustParse(common.MifloraServiceUUID)) +func RequestModeChange(client ble.Client) error { + mifloraService := FindServiceByUUID(client.Profile().Services, common.MifloraServiceUUID) if mifloraService == nil { return errors.New("Failed to get the miflora service") } - mifloraModeChangeChar := FindCharacteristicByUUID(mifloraService.Characteristics, ble.MustParse(common.MifloraCharModeChangeUUID)) + mifloraModeChangeChar := FindCharacteristicByUUID(mifloraService.Characteristics, common.MifloraCharModeChangeUUID) if mifloraModeChangeChar == nil { return errors.New("Failed to discover the mode change characteristic") } @@ -63,13 +65,13 @@ func RequestModeChange(client ble.Client, profile *ble.Profile) error { return nil } -func RequestSensorData(client ble.Client, profile *ble.Profile) (common.SensorDataResponse, error) { - mifloraService := FindServiceByUUID(profile.Services, ble.MustParse(common.MifloraServiceUUID)) +func RequestSensorData(client ble.Client) (common.SensorDataResponse, error) { + mifloraService := FindServiceByUUID(client.Profile().Services, common.MifloraServiceUUID) if mifloraService == nil { return common.SensorDataResponse{}, errors.New("Failed to get the miflora service") } - mifloraSensorDataChar := FindCharacteristicByUUID(mifloraService.Characteristics, ble.MustParse(common.MifloraCharReadSensorDataUUID)) + mifloraSensorDataChar := FindCharacteristicByUUID(mifloraService.Characteristics, common.MifloraCharReadSensorDataUUID) if mifloraSensorDataChar == nil { return common.SensorDataResponse{}, errors.New("Failed to discover the sensor data characteristic") }