How to use sensor.delegate class? Where can i find examples?

Garmin's System8 announcement includes:

"Sensor Pairing

If your device application or data field communicates wirelessly with a sensor or peripheral using ANT, ANT plus, or Bluetooth Low Energy (BLE), then you will need to implement a pairing process. Connect IQ allows you to implement a Sensors.SensorDelegate that allows you to have your device pair as part of the device’s sensor paring UI flow. See the Sensors.SensorDelegate class in the API docs for details."

Is there any further information on this? The API docs are short... - i would like to see an example how to program a sensor delegate.

  • Hmm, before i invest time to dive into this new sensor.delegate rabbit hole: what could be the advantage, compared to my datafield which does scanning and pairing without using sensor.delegate class? (as does the old nordicthingy52 example)

    i think the intent of this feature is to provide an improved user experience for pairing sensors with CIQ apps.

    e.g. If you have a data field which pairs with a sensor:

    - the old way is that they have to read the store page / manual which tells them to "somehow" find the ANT+ ID of the sensor and type it into the settings, and/or the data field just pairs with the first sensor it finds of the correct type

    - the new way is that when they install the data field, the native UI tells them that sensor pairing is necessary, and the user gets to use the native sensor pairing UI to find and pair their sensor

    I'm also going to guess that this might be part of complying with the new EU rules about notifying the user of insecure wireless connections (in this case I mean short-range wireless like bluetooth, ant, etc.) Notice how every part of the native UI which deals with sensors now has information about whether wireless connections are secure, including the native sensor menu and the HR broadcasting feature.

    Could be Garmin really wants CIQ app devs to use the native pairing UI so CIQ users see the same wireless security information.

  • Did anyone ever get this working? I'm trying to solve access to FTMS fitness machines that implement the new EU rules requiring secure connections with my FTMS datafield apps. I've implements the SensorDelegate using the NordicThingy template as an example. I get my app to show up in the simulator Manage Sensors page. (I haven't got around to buying a NRF dongle yet and my watch doesn't support system 5.1.0+ so I have no way to test) 

    so far my user's aren't seeing any indication that native pairing exists. I was expecting there to be a Connect IQ type of device in Sensors and Accessories, but that doesn't seem to exist. Anyone know how the user would trigger a scan and pairing for a Connect IQ supported sensor?

  • It is very not intuitive both as a developer and as a user. I think I managed to add it (at least on the level that the user sees what they are supposed to). It does help a bit that I have a fr965 so I could also test it on a real device, because it's not like in the simulator (from the user experience point of view).

    However I think you shouldn't be bothered by this (as I was), 'cause everything seems to work without it as well. The only difference is the popup about the open connection. Not a good user experience, but doesn't break functionality. And anyway both as a user and as a developer I think Garmin is to blame for the bad user experience, not some CIQ app developer.

  • I was expecting there to be a Connect IQ type of device in Sensors and Accessories, but that doesn't seem to exist

    I haven't seen it myself (as I don't have any apps that use this feature), but others have mentioned this category exists, as in this bug report:


    Native sensors appearing under Sensors => Connect IQ menu (CIQ System 8)  

    I'm guessing it's only shown if you've already paired CIQ sensors? (Which would be a shame.)

    What happens if your users select Sensors > Add New > Search All?

  • The only difference is the popup about the open connection. Not a good user experience, but doesn't break functionality.

    Yeah I should've added in my previous comment that I think another benefit of using native pairing in CIQ is to (presumably) avoid the annoying open connection prompt.

    I think this prompt is extremely annoying though, as it pops up even if you simply start editing an activity data page and you just scroll past a CIQ field that uses ANT+. It's intrusive enough that it makes it hard to scroll through the list of CIQ fields.

    I understand why Garmin has that prompt, and I bet that it's intentionally annoying in part to nudge devs to use native pairing.

  • so far my user's aren't seeing any indication that native pairing exists

    You mentioned the "NordicThingy template", so I assume you mean the NordicThingy52 sample, which is a device app? Which suggests that your app is a device app, right?

    The docs on native pairing have this to say:

    [https://developer.garmin.com/connect-iq/core-topics/pairing-wireless-devices/]

    Pairing with Your Data Field or Application

    If you add support for the native pairing flow, users will be prompted to pair with your sensor when the data field is installed. Applications typically want to pair devices as part of a setup flow. You can use System.exitTo(new Intent("system://pairing", {})) to exit the user to the native sensor scanning process.

    What this is telling me is:

    - if your app is a data field, the system will prompt the user to pair sensors on installation (but what if they skip this process - is there any easy way for them to trigger it after the fact without going to the Sensors menu?)

    - if your app is a device app, your app can trigger the pairing process itself (as described above)

  • Do you mind sharing some code? I went through the "docs" and the Core Topic and while i was able to come up with these ideas:

    class SensorDelegateCustom extends Sensor.SensorDelegate {
    
        function initialize() {
            Sensor.SensorDelegate.initialize();
        }
    
        // Start the sensor scan process for BLE devices
        // If suitable device found, create a Sensor.SensorInfo 
        // Call Sensor.notifyNewSensor() with it
        // realistic timeout to not get stuck
        function onScan(){
    
            // just scan assuming this is only called during the initial setup TODO verify
            Ble.setScanState(Ble.SCAN_STATE_SCANNING);
            return false;
        }
    
        // call Sensor.notifyPairComplete() to be done
        // BLE it could involve persisting the ScanResult or establishing a bonded connection
        function onPair( sensorInfo){
            return false;
        }
    
        // remove the persisted scan result
        //  call Sensor.notifyUnpairComplete() to finish
        function onUnpair(sensorInfo ){
            return false;
        }
    
        function pairingRequired(){
            return true;
        }
    
    }

    It is still a mistery how i should implement a BLE pairing process. I used to have a BleDelegate class with the function

    onScanResults
    

    And i am wondering how i should even notice that a BLE device is found and ready for inspection.

  • For anybody wondering. I got it working by adding this to my AppBase:

        function getSensorDelegate(){
            if (Toybox has :BluetoothLowEnergy and Sensor has :SensorDelegate) {
                if (_profilesBLE == null) {
                    _profilesBLE = new BleProfile();
                    BluetoothLowEnergy.setDelegate(new PairingBleDelegate(_profilesBLE)); 
                    _profilesBLE.registerProfiles();
                }
                return new SensorDelegateCustom();
            }
            return null;
        }


    and implementing a small additional BLEDelegate that handles the special case of a connection via the SensorDelegate

    class PairingBleDelegate extends Ble.BleDelegate {
        private var _profiles;
    
        function initialize(profiles) {
            Ble.BleDelegate.initialize();
            _profiles = profiles;
        }
    
        function onScanResults(scanResults as Ble.Iterator) as Void {
            for (var result = scanResults.next(); result != null; result = scanResults.next()) {
                if (result instanceof Ble.ScanResult) {
                    if ( "Your condition" ) {
                        
                        
                        // Wrap the BLE scan result inside a native Garmin SensorInfo container
                        var sensorInfo = new Sensor.SensorInfo();
        
                        // Safe Name extraction
                        var devName = result.getDeviceName();
                        sensorInfo.name = (devName != null) ? devName : "My Sensor";
    
                        // Set SensorType
                        if (Sensor has :SENSOR_TYPE_GENERIC_BLE) {
                            sensorInfo.type = Sensor.SENSOR_GENERIC;
                        }
    
                        sensorInfo.technology = Sensor.SENSOR_TECHNOLOGY_BLE;
    
                        // Attach the scan result via the data dictionary using the :bleScanResult symbol
                        sensorInfo.data = {
                            :bleScanResult => result
                        };
    
                        
                        // Notify native UI that a candidate target was found
                        Sensor.notifyNewSensor(sensorInfo, false);
                    }
                }
            }
        }
    }

    Finally, the Sensor Delegate

    class SensorDelegateCustom extends Sensor.SensorDelegate {
    
        function initialize() {
            Sensor.SensorDelegate.initialize();
        }
    
        // Called when user opens Native Sensor search screen
        function onScan() {
            Ble.setScanState(Ble.SCAN_STATE_SCANNING);
            return true;
        }
    
        // Called when user taps "Pair" on the discovered device
        function onPair(sensorInfo as Toybox.Sensor.SensorInfo) {
            if (sensorInfo != null) {
    
                var scanResult = sensorInfo.data[:bleScanResult] as Ble.ScanResult;
    
                
    
                if (scanResult != null) {
                    Ble.setScanState(Ble.SCAN_STATE_OFF);
                    
                    Sensor.notifyPairComplete(sensorInfo);
                    return true;
                }
            }
        }
    
        // Called when user deletes the sensor from native menu
        function onUnpair(sensorInfo as Sensor.SensorInfo) {
            Sensor.notifyUnpairComplete(sensorInfo);
            return true;
        }
    
        function pairingRequired() {
            return true;
        }
    }

    Probably not the prettiest, and I also erased some custom code, but this minimal example should get most people going