Switching themes from custom.html

Posted on
  • Hi,

    I could use a little guidance on how to implement setting the system theme to either Light BW or Dark BW through both a custom.html and a settings.js file for my app. Does anyone have a link to the code where the Settings/System/Theme menu is from?

    I did find this example by @rigrig that switches to the two themes i want to use. Should I try to incorporate this into my code? - Or is there an existing system function for doing this that could be called from anywhere globally?

    If anyone feels like taking a look at what i have so far, it's right here. Right now the settings menu only works to set the color on the watch face. Edit: Settings.js is now fixed

  • I was able to use this reference as a starting point and then added the code from rigrig to my settings.js file and I'm happy to report that it now switches properly :)

    Thank you! @rigrig

  • I got the upload button to work. I forgot to add the <script src="../../core/lib/customize.js"></scri­pt> line. Now it uploads but it doesn't set it as the default clock for some reason.

    I also had to change {name:"myapp.app.js", url:"app.js", content:app}, to {name:"myapp.app.js", url:"app.js"}, - That was from the example code for customizing an app. I noticed it when I started looking at the files being sent through the web IDE storage function and saw my app.js file was just a couple lines of json.

    Now it sends the right app code, but it still doesn't load the new app after rebooting and goes back to the Anton clock. I'm guessing this is a conflict with the theme setting I'm trying to pass from the app loader.

  • It looks like my main issue is it's not sending or creating a shadowclk.json file when uploading the app to the watch

  • I got it to create the json file correctly now, but it won't load my clock automatically after uploading and the theme setting in the json file isn't being read or applied automatically (works through the settings menu though)

  • I've just realized that installing a new clock does not automatically set it as your default or run it immediately. Is there a way to autorun the app after installing from the app loader?

    If I can fix that and have it apply the system theme automatically then it'll function exactly as I had envisioned.

    Any suggestions would be much appreciated.

  • Is there a way to autorun the app after installing from the app loader?

    Not at the moment, no. If it's the only clock installed it'll be used, but while there are other clocks they will likely be used first.

    However you could:

    • use an interface.html which doesn't install an app but instead communicates direct with the Bangle - in that case it could just send a command to write the file and even load your app.
    • if your app literally only sets the theme (and there's no actual app part) you could make it a 'RAM' app like assistedgps or fwupdate - it's kind of the same as interface.html but there's nothing to install - you just click the button and again you can communicate with the bangle
  • use an interface.html

    That sounds perfect for installing the app with the default values and then having the interface.html set up for configuring the color and theme. I'll definitely check this out, thanks!

    Could you elaborate on how to start my app on the bangle.js from an interface.html? That's really all I have left to implement, I think.

  • I've got my interface.html file somewhat set up now and it works decent for sending the settings (still have to hold the button for 3 sec to reset the watch), but I could not figure out how to get the values from an existing shadowclk.json file on the watch to be used for the preview.

    I'm sure there are a number of ways it could be better and I'm open to all suggestions. If anyone knows how I can set the preview to show what's in the watch settings already, please let me know. Thanks. I did use some AI assistance with the coding since I'm still very new to programming and trying to learn as I go so there may be some glaringly obvious issues that I don't see.

    You can try it from here.

  • EDIT: Sorry, I didn't read your question properly. I don't know how to do what you're asking for 🙃

    You can upload the shadowclk.json file to the bangle by specifying it in the metadata.

    One example where I upload JSON files on app install is here: https://github.com/thyttan/BangleApps/tr­ee/my-setup/apps%2Fmysetup

  • I'm able to write to setting.json and shadowclk.json no problem and that handles all the settings on the watch side, but I want to be able to set the preview in my HTML code to match the data in the watch. I've tried using puck.eval() and Util.read but it's possible I was using them wrong. At one point I was inspecting the HTML code while it was running with breakpoints and I could see it read the file correctly while it was being parsed by interface.js but then it stepped into puck.js and the data never made it back into the HTML script for the preview to use.

  • Reading this: https://github.com/espruino/BangleApps#m­etadatajson-interface-element

    ... makes me think you need to do something like this:
    https://github.com/espruino/BangleApps/b­lob/master/apps/gpsrec/interface.html#L9­7-L104

    ... where Bluetooth.println() seems essential to me. It's also used for sending messages from the bangle triggering intents on android devices, like this: https://www.espruino.com/Gadgetbridge#in­tents

    Again, I don't know exactly though.

    EDIT: For another example, 'Sleep Log' app also uses interface.html.

  • Thank you, I'll have a look at those examples. I did look at gpsrec earlier as suggested by the readme for a full example, but it's not clear how to get the key value pair data out of the JSON to use as a variable in the HTML code

  • I got it to read the settings and it sets the preview accordingly, but only after opening the interface window a second time. Not sure why yet.

    Also, it would be awesome if it was able to send a Bangle.load(shadowclk.app.js) or Bangle.setUI(clock) or similar command that would load up the new settings on the watch automatically without having to hold the button to reset it. I looked in https://www.espruino.com/Reference#Bangl­e and didn't see anything obvious to reset the watch. Is there such a command?

  • Literally just sending load("shadowclk.app.js") should do it I think...

    In many ways it might actually be better to just add a global option in the app loader that said "load app after upload" or something?

  • I was trying Bangle.load(shadowclk.app.js) from the REPL, but I will try load("shadowclk.app.js") as well.

    In many ways it might actually be better to just add a global option in the app loader that said "load app after upload" or something?

    I'm sure some people would appreciate the option. For now I just have a message displayed saying "Hold the button on Bangle.js" after they upload the config they want

  • Something in my loadSettings function in 'interface.html' to read the json file on the bangle is sometimes causing the code execution to get stuck bouncing back and forth between this function in 'puck.js'

    // Did `write` and `eval` manage to create a connection?
        isConnected : function() {
          return connection!==undefined;
        },
    

    and this function in 'comms.js'

    //TODO Switch to an event listener when Puck will support it
        let interval = setInterval(() => {
          if (connected === Puck.isConnected()) return;
    
          connected = Puck.isConnected();
          cb(connected);
        }, 1000);
    

    I think this is why the settings don't always load properly for the preview, it was around every third try opening the interface that it would load them and it would stay working for a while but then it would go back to using my default values.

  • The communication mostly works now and I've added some better error handling in my code.

    In gadgetbridge it shows an error whenever I read from or write to the bangle. Chrome doesn't show any errors. Both still have communication issues sometimes when using my interface.

    The error gadgetbridge shows is "Malformed JSON from Bangle.js: No value for t". The logs show my shadowclk.json data being fragmented. Do I just need to send it a "t" value first so it doesn't throw that error?

    22:58:14.438 [Binder:6136_2] INFO  n.f.g.s.d.b.BangleJSDeviceSupport - UART RX LINE: {"color":"#0ff","theme":"light"}
    22:58:14.438 [Binder:6136_2] INFO  n.f.g.s.d.b.BangleJSDeviceSupport - UART RX JSON parsed successfully
    22:58:14.439 [Binder:6136_2] INFO  n.f.g.s.d.b.BangleJSDeviceSupport - UART RX JSON parse failure: No value for t
    22:58:14.439 [Binder:6136_2] ERROR n.f.g.u.GB - Malformed JSON from Bangle.js: No value for t
    22:58:14.455 [main] INFO  n.f.g.d.b.AppsManagementActivity - WebView TX: me":"light"}
    (482)
    22:58:14.456 [main] INFO  n.f.g.d.b.AppsManagementActivity - WebView TX cmd: bangleRx("me\":\"light\"}\r\n");
    22:58:19.266 [JavaBridge] INFO  n.f.g.d.b.AppsManagementActivity - WebView RX: Bluetooth.println(JSON.stringify(require­("Storage").readJSON("setting.json", true)))
    
  • I realise this was posted both on GitHub and here, but just for others, the No value for t error occurs because Gadgetbridge expects any JSON object it receives on a line (eg {"a":42}) to be for it - and if it doesn't see a t field there it complains: https://www.espruino.com/Gadgetbridge#ba­ngle-android

    To work around it instead of using Puck.eval to read the file directly, you just do Puck.eval([...]) which then returns what you requested as an array ([{"a":42}]) and then Gadgetbridge doesn't care

  • Yep, I meant to come back here and clarify that as well.

    Exactly as Gordon suggested, I had to add square brackets [...] around the require("Storage").readJSON("shadowclk.j­son", true) in the Puck.eval() call to wrap the resulting data in an array. Then extract the actual data from the array using let data = dataArray ? dataArray[0] : null;. like this:"

    Puck.eval('[require("Storage").readJSON(­"shadowclk.json", true)]', (dataArray) => {
        let data = dataArray ? dataArray[0] : null;
    
  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Switching themes from custom.html

Posted by Avatar for stweedo @stweedo

Actions