That moment when grown men start crying…

OK I admit it. I cried. A little… So there are many emotional moments in a life but seeing the succesful launch of the SpaceX Falcon Heavy is one of those. I cannot not be impressed of the work it must have taken to a) design the thing, b) build the thing, c) launch the thing and d) bring the freaking boosters back to Earth. Oh and the secret e) the social media coverage and small touches from the Tesla Roaster onboard, Space Man,  the “Don’t Panic” text on the screen, the plaque of names and finally but not least Life on Mars by David Bowie playing alongside. Wow! Just wow!

I highly recommend watching the video of the launch with the intro or if you’re not quite to it the actual launch from T-10 seconds below it.

Salesforce Release Notes

So I’m reading through the Salesforce Spring 18 release notes for a customer project starting later this week for specifics on changes related to GDPR. So happy for searching as the release notes are a wopping 459 pages. What! Seriously? Oh yes…

The way I tend to go through the release notes is to get the PDF version as I find it easier to skim and search through. The release notes are also available in HTML which can be good if you know you’re only looking for say Lightning related changes or AppDev related changes.

Only other piece of advise – make sure you’re sitting comfortably and have coffee at the ready!

Salesforce Spring 18 release notes (PDF)
Salesforce Spring 18 release notes (HTML)

Hello WordPress World!

So I’ve finally made the move from the old trusted blogging platform I’ve been using since 2004 (!!) to So no more hosting myself but hosting and paying a provider in the Cloud ( I’ve never really used WordPress besides looking looking a bit so this should be a fun adventure.

The conversion was done by yours truly using a script I wrote to convert from the old proprietary XML format to the WordPress format. The script is written in Typescript and can be found on Github if anyone is interested. To start out with the script would convert both posts, comments, tags, images and files but I was finding the import of images pretty unstable so for the final import I scrapped that part and just uploaded all images and files manually.

While I’ve been testing the support from has been very cool and very helpful. Thank you.

Final stats for the conversion was as follows:
Posts: 1633
Comments: 2542
Files: 142
Images: 402

All this since 2004 with my first post ever on adding additional tuners into my MythTV box to my last on the Pebble platform in 2018 on unsigned integers on Arduino.

Thank you Pebble. I’ll soon be putting the old server to rest in the recycle bin on some old VMWare server somewhere. We had fun. You’ve followed along for the last 14 years – let’s see what the future will bring.

Note to self – subtracting unsigned integers

I was having a small Arduino problem this weekend involving counters so had to do some research on subtracting unsigned integers from one another in the context of counter rollover. Interesting solution to that problem using modulus UINT_MAX+1. Thank you to this answer on (Is unsigned integer subtraction defined behavior?). Below is my test code.

#include <stdio.h>

unsigned char counter = 0;
unsigned char previousCounter = 0;
unsigned int interval=47;

int main () {
   for (int x=0; x= interval) { // check for rollover
         printf("Reached interval at %d (%d), %d n",
            currentCounter, previousCounter, (unsigned char)(currentCounter - previousCounter));
         previousCounter = currentCounter;

Solving my Sonos morning alarm problem

I’ve jumped on the Sonos bandwagon a while back and I have a couple of Sonos players around the house. One in the bathroom, one in the kitchen, in the living room etc. I’ve been using them with alarms in the morning (weekdays only) so that the bathroom one tuned in to local radio (streamed of course) at 5.30am, the kitchen one joined in at 6am and then the upstairs one at 6.30am when it’s time to wake the kids. The problem with this setup is that Sonos does not offer a way to let the players join into the same group meaning that eventually / sometimes they will be slightly apart in the streaming of the audio which is really annoying. I’ve been looking for a solution for a while but didn’t find a solution until now.

My solution has been to use the node-sonos-http-api running on a Raspberry Pi and using cron. Very simple really. Basically the node-sonos-http-api offers up a local HTTP server on port 5005 that accepts GET requests to perform actions on players such as playing, tuning in to Tunein stations, joining groups etc. So I have a couple of cron jobs that first thing makes sure the bathroom player leaves any other group it might be in, sets the volume and then tunes into the radio station. Later the kitchen players volume is set and the player joins the bathroom player and so on. Very cool and works like a charm.

The biggest problem turned out to be getting node and npm installed on the Raspberry Pi v.2 I’m using but downloading the binaries manually and installing manually (unzipping really) instead of using apt-get worked like a charm. Then I simply run the node-sonos-http-api server on startup and have cron do the requests using curl chaining multiple requests together using && where necessary i.e. when I need to change volume and join a group in one cron job.

Fixing Heroku and SFDX CLI after upgrading to macOS High Sierra

macOS was just recently approved by Salesforce IT so I upgraded but only to find that my heroku CLI and SalesforceDX CLI tools had stopped working. I seem to remember that SalesforceDX is basically the same as the Heroku CLI so them failing together made sense. Running the tools only gave strange errors…

$ heroku
    stat /Users/mheisterberg/.local/share/heroku/client/bin/heroku: not a directory
    fork/exec /Users/mheisterberg/.local/share/heroku/client/bin/heroku: not a directory
$ sfdx
    stat /Users/mheisterberg/.local/share/sfdx/client/bin/sfdx: not a directory
panic: fork/exec /Users/mheisterberg/.local/share/sfdx/client/bin/sfdx: not a directory

goroutine 1 [running]:
panic(0x259de0, 0xc420017350)
	/usr/local/go/src/runtime/panic.go:500 +0x1a1
main.must(0x3c03c0, 0xc420017350)
	/home/ubuntu/.go_workspace/src/ +0x5c
main.getExitCode(0x3c03c0, 0xc420017350, 0xc420017350)
	/home/ubuntu/.go_workspace/src/ +0x12b
	/home/ubuntu/.go_workspace/src/ +0x14c

I started thinking it was an issue with node but it turned out to be caused by the command line tools for macOS having been uninstalled or needing to be updated. Below are the steps I used to install the command line tools, upgrade node (I’m using Homebrew) and fix the Heroku CLI and SalesforceDX CLI.

// update / install command line tools for macOS
$ xcode-select --install

// update homebrew
$ brew update

// verify my node version
$ node --version

// upgrade node
$ brew upgrade node
==> Upgrading 1 outdated package, with result:
node 9.3.0_1

// now to the real magic - fix heroku cli
$ rm -rf ~/.local/share/heroku/client
$ heroku update
heroku-cli: Updating to 6.14.43-73d5876... 12.7 MB/12.7 MB
heroku-cli: Updating CLI... already on latest version: 6.14.43-73d5876
heroku-cli: Updating plugins... done
$ heroku --version
heroku-cli/6.14.43-73d5876 (darwin-x64) node-v9.2.0

// rinse and repeat for SalesforceDX cli
$ rm -rf ~/.local/share/sfdx/client
$ sfdx update
sfdx-cli: Updating to 6.0.26-3d23012... 19.3 MB/19.3 MB
Installing dependencies for /Users/mheisterberg/Programming/repos/sfdx-l18n-plugin... done
sfdx-cli: Updating CLI... already on latest version: 6.0.26-3d23012
sfdx-cli: Updating plugins... done
$ sfdx --version
sfdx-cli/6.0.26-3d23012 (darwin-x64) node-v8.6.0

Note to self – use Promise.all instead of simply resp.text()

Was frustrated that when using fetch and the promise chain that once you returned a promise to get the response text (or JSON) you no longer had access to the response object in the next promise without having a state variable. I often return an error message in the response to inform the user what went wrong but I cannot see what happened based on status code without the response object. Well at least as far as I can read the API on MDN. My solution is to resolve using Promise.all instead of simply resp.text() or resp.json() as below. That’s all…

fetch('/foo').then(resp => {
   return Promise.all([Promise.resolve(resp), resp.text()])
}).then(values => {
   const resp = values[0]
   const txt = values[1]
   if (resp.status !== 200) {
      // duh!!!
   } else {
      // oh the joy