On coding, tech, privacy and whatever else comes to mind.

  • Swift’s tooling for non-app work still has some way to go. I attempted to write a script for analysing API data, but ended up fighting swift-sh, Marathon, Xcode’s integration of those, and cryptic compiler errors. Switched to Jupyter and it was fine. Might try again in 2020.

  • 🎉 Just published my first Ruby gem: A simple agent for Huginn to filter events based on time. I’m using it to filter ISS flyover alerts to times I’m awake and it’s dark.

  • Tech Note: Getting push notifications for fastlane errors
  • Bad Apple Support: Called them as my iPad stopped uploading changes to iCloud Drive1. Support told me to do an iCloud Backup and restore the iPad, saying everything will be backed up. Guess what, it didn’t. Good thing I expected that and copied the files to Dropbox first.

    1. Just kept saying “Waiting to upload”. Restart didn’t help. It still received changes from other devices, and new (empty) folders did get uploaded, too. 

  • After a while corespeechd is constantly using 100% of one CPU core on my new Mac Mini. Annoying. Haven’t found another way than to kill it on a schedule.

  • Hey Siri, pre-heat my car

    When I got my Renault Zoe, I was excited to see that it came with an accompanying app, which let’s you remotely pre-heat (or cool) the car and also check the charge status of the battery. Being able to pre-condition the car is great as the process takes a few minutes and it’s nice when it’s finished by the time you’re getting into the car. And having remote access to the charging status is especially handy on road trips: Firstly, it’s annoying to go back to the car and find that it still needs to charge for another half hour and you could have gotten a coffee or went for a nice walk. Secondly, you might be wasting money by remaining plugged in when the car is already full, if you’re getting charged by the minute. So there’s real utility to such an app.

    The excitement about the app didn’t last too long. It feels neglected, is slow to use, and routinely forgets my login credentials. It doesn’t support current OS features such as the iPhone X-sized screen, Siri or Shortcuts either.

    However, luckily the app is built using an API that other curious minds have since reverse-engineered – Kudos in particular to Terence Eden and aironaut.ch for sharing their insights. This allows you to replicate the features of the app in a different package.

    What I wanted to do was this: Be able to use Siri on my phone or watch to ask “How’s my car” to get the charging status, and “Pre-heat my car” to start pre-heating it.

    Knowing the API is the first half, the second half is picking a tool that let’s me query the API from Siri. This should be possible with Apple’s Shortcuts app, but I prefer to use a textual rather than visual programming language. The right tool for me was the excellent Pythonista which has a beta version available that adds support for running scripts via Siri and the Shortcuts app.

    The result: I can now say “Hey Siri, pre-heat Moritz” and a few seconds later it’ll start pre-heating, or say “How’s Moritz?” and get the current charge, current range, and when it’ll reach 100% charge. I can also trigger both of those without saying a word through the Shortcuts widget on my lock screen. Works like a charm!


    If anyone wants to replicate this, below is the script. Copy it into Pythonista, adjust your username and password, optionally set a custom name for the car. Then use Pythonista’s built-in feature to add Siri Shortcut to use it with Siri. By default this will run the “battery status” part, for the pre-conditioning use a single argument precondition. Once you’ve added the command to Siri, you can also use it in the Shortcuts app.1

    #!python3
    
    import datetime
    import requests
    import shortcuts
    import sys
    
    username = "you@domain.com"
    password = "YourPassw0rd"
    carname = "Zoe"
    
    ### Fetches auth token and VIN
    def login():
      headers = {"Content-Type": "application/json"}
      body = {"username": username, "password": password}
      url = f"https://www.services.renault-ze.com/api/user/login"
      
      result = requests.post(url, json=body, headers=headers).json()
      token = result['token']
      vin = result['user']['vehicle_details']['VIN']
      return {"token": token, "vin": vin}
      
    def get_battery():
      creds = login()
      headers = {"Authorization": f"Bearer {creds['token']}"}
      url = f"https://www.services.renault-ze.com/api/vehicle/{creds['vin']}/battery"
      
      result = requests.get(url, headers=headers).json()
      if result['charging']:
        minutes = result['remaining_time']
        remaining = fuzzy_duration(minutes)
        status = f"done charging in {remaining}"
      else:
        status = "is not charging"
      return f"{carname} can go {result['remaining_range']}km. Battery is {result['charge_level']}% full and {status}."
      
    def fuzzy_duration(minutes):
      if minutes < 90:
        return f"{minutes} minutes"
      if minutes < 3 * 60:
        half_hours = round(minutes / 30) / 2
        return f"{half_hours} hours"
      else:
        hours = round(minutes / 60)
        return f"{hours} hours"
        
    def precondition():
      creds = login()
      headers = {"Authorization": f"Bearer {creds['token']}"}
      url = f"https://www.services.renault-ze.com/api/vehicle/{creds['vin']}/air-conditioning"
      result = requests.post(url, headers=headers)
      if 200 <= result.status_code < 300:
        return f"{carname} is warming up"
      else:
        return f"{carname} is not responding"
      
    def main():
      if len(sys.argv) > 1 and sys.argv[1] == "precondition":
        text = precondition()
      else:
        text = get_battery()
        
      if shortcuts.is_shortcut():
        shortcuts.set_spoken_output(text)
      else:
        # For debugging in the main app (normally, this script would run in a Siri shortcut):
        print(text)
          
    if __name__ == '__main__':
      main()
    

    Changelog:

    1. For detailed steps on how to do this, see a related post on the Pythonista forum


  • Tech Note: Sharing iCloud Drive documents from your app
  • Switching Mojave's dark and light themes using touch bar

    Mac OS Mojave introduces both a pretty dark mode and a way to trigger Automator scripts right from the Touch Bar. The two go together nicely:

    1. Open Automator and create a “Quick Action”. This should receive “no input” and have a single “Run AppleScript” action with the following content:
    tell application "System Events"
      tell appearance preferences
        set dark mode to not dark mode
      end tell
    end tell
    
    1. Save it, which will then land in your ~/Library/Services folder.
    2. In your System Preferences > Keyboard > “Customise Touch Bar…”, drag the “Quick Actions” to your Touch Bar.

    And – bam! – two taps on the Touch Bar, and you toggle between light and dark.


    I also tried (desperately) to switch the accent colour at the same time, but to no avail. The script below shows that there’s highlight color: orange, but I couldn’t get this to work. I’d appreciate any help from AppleScript pros.

    tell application "System Events"
      tell appearance preferences
        get properties
        set highlight color to orange -- does nothing visually, though it does change that property :(
      end tell
    end tell
    

  • Tech Note: RxSwift in custom views, the clean way
  • Just came across Mirrorshades which is as minimalist as web analytics can get. Adorable simplicity!

subscribe via RSS or via JSON