I have recently switched my electricity provider to tibber because they provide fair electricity prices (actually they re-sell with real market prices which is way below what other electricity provider charges these days).
They have two price models. In the basic model, they charge a monthly average price. Customers have to enter their monthly meter readings manually into the app. For the advanced pricing model, a device called the Tibber Pulse is required. This device reads out an electricity meter and sends hourly readings to tibber. This enabled per-hour prices which opportunities like charging one’s electrical vehicle during cheap pricing hours.
I started with the monthly pricing model and while I was waiting for my Tibber Pulse to arrive, I wrote a small tool to upload my daily consumtion automatically to tibber.
Today I received my Pulse and wanted to install it. Unfortunately it got stuck on a firmware update loop. According to my router, it keeps downloading stuff from an AWS bucket. While I’m waiting for a solution from tibber’s support, I use the time to have a closer look to that little device.
Actually, I got two devices. One is an small battery powered device that is attached to my electricity meter. It reads the meter via it’s infrared interface and sends out the readings on 868 MHz with a (yet unknown to me) protocol. Therefore it is called Tibber Pulse IR.
The second device, the Tibber Bridge, is a wall-plugged device that connects to a wifi and acts as bridge between the pulse and the internet. In this article I will have a deeper look onto this.
I started with some network analysis to see what is happening when the device got into the firmware update loop. So I started tcpdump on my router and inspected the traffic from/to the device. The results where quite unspectacular:
- Traffic between the Bridge and an WAS S3 Bucket, HTTPS – repeating all the time, probably a failed fimware download attempt
- An MQTT connection to something.iot.eu-west-1.amazonaws.com, also TLS encrypted. That seems to be the communication channel to the bridge.
- Absolutely no communication between the Bridge and the App. So I guess, everything is relayed through Tibber’s servers at AWS.
No much to figure out so far. I also did a full port scan to the device, but no port is open. At least I could lookup the vendor of the device’s MAC address – it’s Espressif, so the device is very likely powered by an ESP32.
Back to setup mode
Since the device has no port available in normal mode and all traffic is encrypted, I went back to the setup mode. This mode can easily be entered by unplugging the device, plug it and repeat this once. As soon as the green LED lights up, the device opens an access point named „Tibber Bridge“. The password is printed on the QR-Code on the device itself.
This time I did not use the tibber app to connect but my laptop instead. It gets a connection and an ip address (10.133.70.10). The Tibber Pulse Bridge itself has the IP 10.133.70.1 and provice a little nice web interface. The credentials are „admin“ and the password printed on the device.
I had expected some prompt for wifi credentials but surprisingly, the webui reveals a lot more information:
- name and key of the current stored wifi AP
- hostname, client certificate and topics of the MQTT endpoint to connect to
- URLs for firmware updates
- Details to firmware files. It confirms that this is an ESP32.
- and lot more
All of those settings can be changed in the UI. So there’s everything available to MITM-intercept the traffic and see what happens.
The setting „webserver_force_enable“ got my attention. Let’s see what happens if I update it to „true“ and boot into normal operation mode. Before that, I used the web browser to save the „params.json“ – this is the response that contains all the settings seen in the webui. If I mess anything up, I can use it to easily restore the original state.
Normal mode plus
After unplugging and re-plugging the device, it boots again in normal operation mode (and immediately begins with the endless firmware download). The main difference now is that I can still access the web interface.
What I also saw now (it was already there before but I did not see it) is that there is a „console“ tab. This console provides kind of „shell access“ to the device with tons of options.
According to the logs (using the new console with the command „dmesg“), the OTA that fails is „tibber-pulse-ir-hub-esp32“. I ran „ota_clean“ and forced the reload of the manifest. Nothing helps, I got still stuck on OTA.
Before I continue to explore the device, let’s see what is possible with the MQTT credentials the devices revealed. I created the files ca.pem, client-cert.pem and client-key.pem from the cert/key data in the params. With this it should be possible to connect to the broker.
mosquitto_sub -h ****.iot.eu-west-1.amazonaws.com --cafile ca.pem --cert client-cert.pem --key client-key.pem -t '#' -v -d Client null sending CONNECT Client null received CONNACK (0) Client null sending SUBSCRIBE (Mid: 1, Topic: #, QoS: 0, Options: 0x00) Client null sending CONNECT Client null received CONNACK (0) Client null sending SUBSCRIBE (Mid: 2, Topic: #, QoS: 0, Options: 0x00) Client null sending CONNECT Client null received CONNACK (0) Client null sending SUBSCRIBE (Mid: 3, Topic: #, QoS: 0, Options: 0x00)
Ok, that did not work. The connection terminates as soon as the client runs a subscribe. But at least the server CA was verified correctly and my client certificate was accepted. The behaviour is quite common if some ACL prevents an arbitrary client to subscribe to everything. So let’s try again by subscribing to my own topic:
mosquitto_sub -h ****.iot.eu-west-1.amazonaws.com --cafile ca.pem --cert client-cert.pem --key client-key.pem -t 'tibber-bridge/a******c/receive' -v -d Client null sending CONNECT Client null received CONNACK (0) Client null sending SUBSCRIBE (Mid: 1, Topic: tibber-bridge/ac41fdc01b3647a1953c1c0721e7711c/receive, QoS: 0, Options: 0x00) Client null received SUBACK Subscribed (mid: 1): 0
Way better. I have got a subscription and can check what happens when I try to access the device using the Tibber Android App. Unfortunately (but not unexpected), I cannot subscribe to the topic that is used to send data to AWS. So to analyse this traffic too, I’d need to setup an TLS- or MQTT-Proxy with the original certificate to communicate to AWS and set up my own certs on the device to trust my proxy for communication. Maybe I do it when my pulse works properly and report here later.
The Tibber Pulse Bridge allows unexpected insights in how that stuff works. I did not expect to get so easy access to all that information. Nevertheless, the system itself seems to be reasonable secure. I can subscribe only to my device’s subscription topic. The client certificate seems to be individually created for my device (but I cannot verify it unless I get access to another device’s certificate).
The credentials I got would allow me to intercept the treffic between the device and tibber. But since I can use their API anyway to get my meter readings, there is next to no advantage in doing so.
Unfortunately I was not able to fix my OTA issue but at least I got a log that I can send to the Tibber technical support.
In the logs I found a lot of unique keywords but none of those led me to a firmware construction kit. So I guess the firmware was developed specific for that devices, based directly on Espressif’s ESP-IDF framework.
Making it work
After one week Tibber has not yet responded to my ticket about the firmware update loop. So I checked the web UI again and tried the „Enabled Pairing“ button. That way the Pulse IR was detected and showed up in the Tibber App. I can see my consumption as well as my current power usage – so all works as expected now.
Accessing the data locally
Through a mention in a github issue comment I found a documentation how to proxy all MQTT traffic of the bridge through a local MQTT server. This is great and should also work on the german version of Tibber Pulse IR with the Tibber Bridge.