Integrating my Solarmanager Setup with the Tidbyt device

Tidbyt is a great little device that can display a lot of interesting information on a nice and big retro pixel screen. I’ve been using it for a couple of months and always wanted to also display some energy usage information as well.

My energy management system “Solarmanager” provides an external API where a lot of this information is readily available, so I decided to dive in and try to implement this even though I do not have any programming experience whatsoever.

Step 1: Get the necessary API information

Solarmanager provides API information on their website at https://external-web.solar-manager.ch/swagger. You can choose between many different commands directly on their website, depending on the information you need. As I wanted the values for power generation and usage, I used the commands provided in the “Data” section to generate a curl script that looks like this:

url -X 'GET' \
  'https://cloud.solar-manager.ch/v1/stream/gateway/MYSMID' \
  -H 'accept: application/json' \
  -H 'authorization: Basic MYAPIKEY'

In the script above, “MYSMID” needs to be replaced with the ID of your Solarmanger device. You can find this at the back of your Solarmanager device. The “MYAPIKEY value needs to be copied from the API interface as I did not want to share mine here.

Step 2: Writing the script that collects the data

As I have no idea how to program, I used ChatGPT and basically just asked how I could create a python script that would use the code above to download the values for “currentBatteryDischarge”, “currentPowerConsumption” and “currentPvGeneration”. So this is what it came up with:

#!/bin/sh

curl -X 'GET' https://cloud.solar-manager.ch/v1/stream/gateway/MYSMID \
-H 'accept: application/json' \
-H 'authorization: Basic MYAPIKEY'> /Users/marcbaier/Tidbyt/tmp.json

/usr/local/bin/jq '{currentBatteryChargeDischarge: .currentBatteryChargeDischarge | tonumber | floor, currentPowerConsumption: .currentPowerConsumption | tonumber | floor, currentPvGeneration: .currentPvGeneration | tonumber | floor}' /users/marcbaier/Tidbyt/tmp.json > /Users/marcbaier/Tidbyt/data.json

rm /Users/marcbaier/Tidbyt/tmp.json

So this basically only takes the data from the values I am interested in, gets rid of all the decimal points and saves the output in a “data.json” file. So far so goo – now we have the data we need, in order to generate a file that can be sent to the Tidbyt.

And by the way – ChatGPT was SO VERY helpful with all of this. The code it suggested did not immediately work but once I fed all the error messages back to it, the AI corrected all the problems and I ended up with a working script. That by itself is amazing.

Step 3: Create the files that need to be pushed to the Tidbyt

So on the Tidbyt webpage as well as on Github you can find basic information how to do this. What I had to do:

  • Install the Pixlet toolkit which is needed to push content to the Tidbyt.
brew install tidbyt/tidbyt/pixlet
  • Write two .star files based on the Starlark programming language. One for the Energey Production Screen and one for the Energy Usage screen. This took me a full day of trial and error and I also got help from the great Tidbyt community on discord.
  • This file does the following:
    • Defines the graphics for the yellow sun that I wanted to be displayed next to the production value
    • Gets the necessary data from the value “currentPvGeneration”
    • “Prints” the SUN icon and the value for “currentPvGeneration”
load("render.star", "render")
load("http.star", "http")
load("encoding/base64.star", "base64")
load("encoding/json.star", "json")


SUN = base64.decode("""
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAfQAAAH0Bx0gPAAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHLSURBVDiNfVMxaxRREP5mdkk89wqDEkiRqNUigVSKKCbY2AdCuD3LlDZ3hVpZWUkOIVb+gYCRCJI6RWQ3pQdnCEnKnAgW1wS8fXt7u2/HYrNx91h3uvfNfN/M+948oCKU63SU63SqasyqJAh3KvN5gcB1PgrJ2fXlzx8yTMDvIFoKU3mNFhHZtSc7LwoCQnIG0NbQbR5Y2j7Rs/1VEX4MiMTHG3eNwcKeb54uArQlIu1/Q+Zi6DaXrBu1YcL4KsBSPqeD8W+GsR4G4z/1lU9HpR5Y2j5J+Gd3knzZay6OxvvW8NZMHuVCl9n+ajk5nVW01MKbF2/zsKkOm5sQuS3gTRF6RKVsXHkpkjz1D537JHgFon46gUAm3S6yBRLp7MAQlpSTSKFhfLyxBuDLJF+PIuggSu88ZXauPdx+XeqBMVjYI1AvjyVjDR3EabHBarofvsnnCwK+ebrIiaxlInoUI1YhAAEbrAzwM3+O75UKKK/RYlDPvwjqPJh/EI+ilxLHXTL5O0+Znelf0UyoI59BPeU1Whnvag+IyBaRdn1lJ1uS9773/BsAWMvb3Uvsh/IabSKyJ30qDeU5u8pzdqtqqn+j4BxM/39eAH8BTcfLhvUo2YMAAAAASUVORK5CYII=
""")

def main(args):
    data = json.decode(args['data'])  # json is passed in on the command line as string
    #data = json.decode("""{"currentBatteryChargeDischarge": 0,"currentPowerConsumption": 457.73999786376953,"currentPvGeneration": 0}""")  # json is passed in on the command line as string

    return render.Root(
        child = render.Box(
            render.Row(
                expanded=True,
		cross_align="center",
                main_align = "space_evenly",
                children = [
                      render.Image(src=SUN),                   
		      render.Text(
                        content = str(data["currentPvGeneration"]) + "W",
                        font = "6x13",
                        color = "#00FF00"
                    ),

                ],
            ),

        ),
    )

The code above is saved as “produktion.star” and I have another one that uses the power usage value as well as a different icon and which is called “verbrauch.star”. See below:

oad("render.star", "render")
load("http.star", "http")
load("encoding/base64.star", "base64")
load("encoding/json.star", "json")


PLUG = base64.decode("""
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAJhSURBVDiNrZJLSFRhGIaf/5wZnUnTHC8kqdgYaUThJiVoIQRBmYuwpLTLxloE0SJCcpNElLQLWnZZ5SIIggxqFlFSlrlpAh2dKG95wZy7M3PmnDnnb2HFjBO16dv8fO/7Xd7v54X/Eb75yIl3/uWpTOzTXNA35Fvq/VevsvZY9d+jydpMQtPNhtWkfuBXHruyrTxywd26fH5n4R8G/D0C8WS32O72CVUOGk0dxzI5W5dP7vIkU1uqHLq/c1z2AISiqc3eeGwuYm2oCmtGxKmHizDH0Ix84s3H708sXDxqd1iddS5XxCahfcTI7x5RKwD6ARKaTv+swdDBMjAtYs97KbJZSENgFWxCwiFdEw+BwzYBjxO+Wy2ZsgotW8G5mkZ3ubPF9XUpQG34BVIBK28j0pb/s0q0Ti5GmgTA7ff75Pq7d5fcoLGqGe3ldUoDd8ECM2bDENVEy44QazkLyEu/PzGWEHyeV4klBADTgVlCcR1HeBwjVYIE1OI0Yn4Bo8INgASpAEgre7slYXT2Dam0ydL+e8y2DYMlkJqCTjVaw9rFqlDeKgDRpAhmDgitKswEJ5kOTqwAKOkEQpUY8yrxvadBUUHKwfrK4g8KQDgm2qQUga2VJnpaMDajGBI8w988LSCfqeEppAlGtJREYxtIOZjnlCdzDNPuTevNnpCse7TgzzLSg46++LUa6bncJScXI3uyjJSZaD2nBlxp40yxpg18ycDtMx/v3PTWX/WuhHhaWTya2ZNlZbvDWWvPd+QoK+rzr0w6d5CSIofLUmCa1hPLNKcVeLW+0EisvgaRg/8Av08REqLHlJ8AAAAASUVORK5CYII=
""")

def main(args):
    data = json.decode(args['data'])  # json is passed in on the command line as string
    #data = json.decode("""{"currentBatteryChargeDischarge": 0,"currentPowerConsumption": 457.73999786376953,"currentPvGeneration": 0}""")  # json is passed in on the command line as string

    return render.Root(
        child = render.Box(
            render.Row(
                expanded=True,
		cross_align="center",
                main_align = "space_evenly",
                children = [
                      render.Image(src=PLUG),
#		      render.Text(
#                        content = "Verbrauch",
#                        font = "tb-8"
#                    ),
                   
		      render.Text(
                        content = str(data["currentPowerConsumption"]) + "W",
                        font = "6x13",
                        color = "#FF0000"
                    ),
#                     render.Text(
#                         content = "%s " % wind_dir,
#
#                     ),
                    # render.Text(
                    #     content = "%s %d°" % (wind_dir,wind_dir_degrees),
                    #     color = "#FFAA00",


                    # ),

                ],
            ),

        ),
    )

Step 4: Push the data to the Tidbyt

This is done using the “pixlet render” command where you render the contenst of the .star files into a .webp file which can then be displayed on the Tidbyt retro pixel screen. I used ChatGPTs help again to create to python scripts that do this:

Produktion.py:

#!env python3
import json
import os

with open("data.json", 'r') as f:
        contents = json.load(f)
command = '/usr/local/bin/pixlet render /users/marcbaier/tidbyt/produktion.star data=\'' + json.dumps(contents) + '\' -o /users/marcbaier/tidbyt/produktion.webp'
print(command)
os.system(command)

Verbrauch.py:

#!env python3
import json
import os

with open("data.json", 'r') as f:
        contents = json.load(f)
command = '/usr/local/bin/pixlet render /users/marcbaier/tidbyt/verbrauch.star data=\'' + json.dumps(contents) + '\' -o /users/marcbaier/tidbyt/verbrauch.webp'
print(command)
os.system(command)

So now we have the following components:

  • get_solardata.sh – this gets the data from Solarmanger once every minute (more on how to set that up later)
  • produktion.star / verbrauch.star- defines what is displayed on screen
  • produktion.py / verbrauch.py – creates a produktion.webp and verbrauch.webp file which then can be pushed to the Tidbyt

The only thing that is missing is the regular “push” of the .webp files to the Tidbyt device. I added this to my get_solardata.sh script which is already running once per minute anyway. So here is the final get_solardata.sh file:

#!/bin/sh

curl -X 'GET' https://cloud.solar-manager.ch/v1/stream/gateway/MYSMID \
-H 'accept: application/json' \
-H 'authorization: Basic MYAPIKEY'> /Users/marcbaier/Tidbyt/tmp.json

/usr/local/bin/jq '{currentBatteryChargeDischarge: .currentBatteryChargeDischarge | tonumber | floor, currentPowerConsumption: .currentPowerConsumption | tonumber | floor, currentPvGeneration: .currentPvGeneration | tonumber | floor}' /users/marcbaier/Tidbyt/tmp.json > /Users/marcbaier/Tidbyt/data.json

#rm /Users/marcbaier/Tidbyt/tmp.json

cd ~/tidbyt/
python3 produktion.py
python3 verbrauch.py

/usr/local/bin/pixlet push --installation-id produktion playfully-parental-vehement-rabbit-a3f /users/marcbaier/Tidbyt/produktion.webp
/usr/local/bin/pixlet push --installation-id verbrauch MYTIDBYTID /users/marcbaier/Tidbyt/verbrauch.webp

So as you can see, this script now does the following:

  • Read the necessary data and save it into a data.json file
  • start the two produktion.py and verbrauch.py files that use the information from the produktion.star and verbrauch.star files to generate the produktion.wepb and verbrauch.webp files
  • these are then pushed to the Tidbyt using the push command

Don’t forget to replace the values MYSMID, MYAPIKEY and MYTIDBYTID with your own values. The Tidbyt ID can be found by using the following command:

pixlet devices

This will reveal the special ID of your own Tidbyt device.

So when I first got this working, my screen looked like this:

At that time I had not added graphics yet. Now the problem was, that I had to manually start the get_solardata.sh file in order to have data pushed to the Tidbyt. I solved this also with ChatGPTs help be editing the “crontab” file on my iMac by typing the following command in the terminal:

crontab -e

This will open an editor where I then saved the following crontab setting:

* * * * * /bin/sh /users/marcbaier/tidbyt/get_solardata.sh

This basically just executes the get_solardata.sh file every minute.

Step 5: Beautify it 🙂

So once I had that working I read the Tidbyt manuals and looked at examples of work other developers had done until i came up with this:

I used icons from the great Flaticon site and converted them to base64 code using this website: https://base64.guru/converter/encode/image

Step 6: Open issues

  • Animating the icons to make it look more interesting
  • Moving the whole setup to either my Synology NAS or an online webserver so that I don’t have to keep my iMac running
  • Hope that the great guys at https://www.solarmanager.ch/ come up with their own app that lets users enter their own SolarmangerID as well as API Key and the rest would just work automatically 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.