LightNetwork device battery level

(episode n in my effort to read my connected devices)

I have a Varia UT-800 front light and I'm trying to access the battery level. When the network is formed, I can see the light in the LightNetwork.getBikeLights() list but even though the LightNetwork is a subclass of Device, the getComponentIdentifiers() list is null so I can't use Device.getBatteryStatus(identifier). How is the reading of the battery level done?

Thanks in advance,

  Nik

Top Replies

All Replies

  • I'd take this to mean that the info you want isn't available for the Varia

  • The documentation for Device.getBatteryStatus() says that the identifier parameter is null for a single component device, or a light index for bike lights. Given the Varia UT-800 is a bike light, I'd expect you'd need to pass a light index.

    I've never written any code to use the BikeLight stuff, but it looks like the LightNetwork.getBikeLights() method returns an array of BikeLight. The BikeLight class inherits from AntPlus.CommonData, and AntPlus.CommonData has an identifier field which is documented to be the light index.

  • I'm pretty sure it should work with a little tweaking.

    using Toybox.AntPlus;
    using Toybox.Application;
    using Toybox.Graphics;
    using Toybox.WatchUi;
    
    // forward LightNetworkListener events and a reference to the LightNetwork
    class MyLightNetworkDelegate extends AntPlus.LightNetworkListener {
    
        hidden var mLightNetwork; // weak reference to avoid reference cycles
        hidden var mLightNetworkListener;
    
        function initialize(client) {
            LightNetworkListener.initialize();
            mLightNetworkListener = client;
        }
    
        function onLightNetworkStateUpdate(aNetworkState) {
            var lightNetwork = _getLightNetwork();
            if (lightNetwork != null) {
                mLightNetworkListener.onLightNetworkStateChanged(lightNetwork);
            }
        }
    
        function onBikeLightUpdate(aBikeLight) {
            var lightNetwork = _getLightNetwork();
            if (lightNetwork != null) {
                mLightNetworkListener.onBikeLightChanged(lightNetwork);
            }
        }
    
        function setLightNetwork(lightNetwork) {
            mLightNetwork = lightNetwork.weak();
        }
    
        hidden function _getLightNetwork() {
            if (mLightNetwork == null) {
                return null;
            }
    
            return mLightNetwork.get();
        }
    }
    
    class MyLightNetworkView extends WatchUi.View
    {
        hidden var mCenterX;
        hidden var mCenterY;
        hidden var mFont;
        hidden var mFontHeight;
        hidden var mTextLines;
        hidden var mJustification;
    
        hidden const BATTERY_STATUS_NAMES = [
            "NEW",
            "GOOD",
            "OKAY",
            "LOW",
            "CRITICAL",
            "INVALID",
        ];
    
        function initialize() {
            View.initialize();
        }
    
        function onLayout(dc) {
            mCenterX = dc.getWidth() / 2;
            mCenterY = dc.getHeight() / 2;
            mFont = Graphics.FONT_SMALL;
            mFontHeight = dc.getFontHeight(mFont);
            mJustification = Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER;
        }
    
        function onUpdate(dc) {
            dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLACK);
            dc.clear();
    
            if (mTextLines == null) {
                return;
            }
    
            var x = mCenterX;
            var y = mCenterY;
    
            var textHeight = mTextLines.size() * mFontHeight;
            y = y - textHeight / 2;
    
            for (var i = 0; i < mTextLines.size(); ++i) {
                dc.drawText(x, y, mFont, mTextLines[i], mJustification);
                y += mFontHeight;
            }
        }
    
        function onLightNetworkStateChanged(aLightNetwork) {
            _updateDisplayText(aLightNetwork);
        }
    
        function onBikeLightChanged(aLightNetwork) {
            _updateDisplayText(aLightNetwork);
        }
    
        hidden function _updateDisplayText(aLightNetwork) {
            var networkState = aLightNetwork.getNetworkState();
            if (networkState == AntPlus.LIGHT_NETWORK_STATE_NOT_FORMED) {
                mTextLines = [
                    "No Network"
                ];
            }
            else if (networkState == AntPlus.LIGHT_NETWORK_STATE_FORMING) {
                mTextLines = [
                    "Forming Network"
                ];
            }
            else if (networkState == AntPlus.LIGHT_NETWORK_STATE_FORMED) {
    
                mTextLines = [];
    
                var bikeLights = aLightNetwork.getBikeLights();
                for (var i = 0; i < bikeLights.size(); ++i) {
                    var bikeLightId = bikeLights[i].identifier;
                    var batteryStatus = aLightNetwork.getBatteryStatus(bikeLightId);
    
                    mTextLines.add(Lang.format("$1$: $2$%", [
                        bikeLightId,
                        BATTERY_STATUS_NAMES[batteryStatus.batteryStatus]
                    ]));
                }
            }
    
            WatchUi.requestUpdate();
        }
    }
    
    
    class MyLightNetworkApp extends Application.AppBase
    {
        hidden var mLightNetwork;
        hidden var mLightNetworkDelegate;
    
        function initialize() {
            AppBase.initialize();
        }
    
        function getInitialView() {
            var myLightNetworkView = new MyLightNetworkView();
    
            // configure a delegate to forward events to our view
            var myLightNetworkDelegate = new MyLightNetworkDelegate(myLightNetworkView);
    
    
            // configure a light network to send events to our delegate
            mLightNetwork = new AntPlus.LightNetwork(myLightNetworkDelegate);
    
            // give our delegate a reference to the light network so that
            // it can pass that network along
            myLightNetworkDelegate.setLightNetwork(mLightNetwork);
    
            return [ myLightNetworkView ];
        }
    }
    

  • be careful that this feature doesn't get banned from being listed in the store due to safety issues.
    (eg: you use the batt status and present it to user, and user just follow the level blindly thinking it has enough juice and then you have a code bug and or X and the user is left at 12 midnight 10km from home and no lights)


  • Ah, thanks. I should have read the Fine Manual! ;-)

  • They can sue me if they find their way home! ;-) I'm not yet sure if I'll ever publish it or if I'll just keep sideloading it. I'm trying to work out a mapping from (battery_level, distance_remaining, bike_speed, time_from_sunset) -> light_level currently. Perhps more vibrations -> more light also if I'm bored

  • just letting you know. My App got Banned (received a nice email from Garmin for it tho) and I was really not pleased having had spent so much time on it.

  • A bit of an odd policy, one would expect that since we're talking about third-party-apps, it would be implied that they come with a "if it breaks, you get to keep the pieces"-warranty. They probably have disclaimers like that for their own software.

  • they prev only had 1(one) exception which was for free-diving apps as they do not have watches which are "built-for" these specific use cases and I agree for that case since you're using the watch for what it was not designed for.

    however, for me, (like you) are merely decoding the ANT+ data packats that come out from the devices (light./ Radar / Power Meter etc) but to them it's different and thus banned.