[APP][Pro] Dashboard Studio - A completely free-form dashboard designer

Already implemented opacity settings for every color (in the upcoming version):

And other options.

NEW TEST release 1.8.2

Note: Still in development so things might change…

Changes:

  • New: Added a lightweight Label widget for simple text lines with minimal overhead.
  • New: Redesigned the calendar engine (iCal) to handle massive files with ultra-low memory usage, preventing crashes on Homey Pro.
  • New: “Load & Filter iCal” and “Merge Calendar Events” flow cards for modular and flexible schedule management.
  • Improved: Added a wide range of timeframe presets for calendar filtering (Today, Yesterday, Tomorrow, This/Previous/Next Week & Month). Please let me know if you are missing something.
  • Improved: Native support for webcal:// links and automatic HTTP redirect handling for better compatibility with iCloud and Google calendars.
  • Improved: Text widget table colors and opacity now behave more predictably across table styles.
  • New: Added a Table Cell Padding setting to control spacing inside table cells.
  • New: Table corner radius setting
  • Improved: Updated default table row colors for a cleaner starting look.
  • Added: Scrolling in wide text tables.

New Calendar Flow Cards:

I completely overhauled the flow cards that parse iCal data. Instead of letting Homey download the file and then parsing it in a separate card, there is now a dedicated flow card to load an iCal file and filter it directly while loading. This is way more memory friendly. Since iCal files contain all previous events, some users have massive files, and this change helps manage that overhead.

Example how to combine multiple iCal files:

Sadly this is a “breaking change” this means this setup is not compatible with previous cards and you have to redo it.

@Bert_Onraedt Please let me know if this can handle your calendar files. Can you also tell me how long it approximately takes to download and calendar parse it and display it?

New Label Widget

Back from the dead. The label widget :rofl:
Decided to create a new lightweight label widget. This is just a simple label, no container, no markdown just text (with rotation option). Because it is a bit overkill to use the new text widget for just a label.

Note: The migrator for version 1.8.0 now migrates simple labels to the new label widget, but labels that used a container or markdown, that converts to the text widget. But users that have saved dashboards past 1.8.0 can not be migrated by this. So if you stil want the old label - new label conversion you need to load the previous version inside the load dashboard dialog (it shows the dashboard version it is saved in)

Included. Also it scrolls horizontally with the mouse wheel if there is no vertical scrollbar

Please test this also Bert, I think I fixed it.

First impressions 1.8.2:

Impossible to set the sizes of the new label, for example, make the text screen-wide so that it is centered on the page

Table with custom columns are mixed up; maybe because the columns are now real px values and no ratio anymore, right? The table colors were also partially overwritten.

You are fast.

Great.

You don’t mention improvements in possible performance optimization issues since 1.8.0 and the unexpected local and global values behaviour. This improvement is needed for a good working dash (not that exotic).

I made a massive performance improvement in the code in 1.8.1. You mentioned you noticed the difference right? I did not do any performance related improvements in 1.8.2

The width and height scale automatically by default to the exact label size. Since this is probably most often used to add small text on top of other elements, I figured this would be the best default choice. You can always increase the font size to make it bigger. If you want the container to stay at a specific size and center the text, you just need to disable “Auto Width” and/or “Auto Height.”

Ah, I broke something in the code while working on it. It was always meant to be pixels, but 0 should represent “auto.” When I change one of the other values, the 0 turns into a hard pixel size instead of staying as “auto.” Thanks!

I do not understand this one, though. Can you give me some more details on what you mean? I did some changes in the colors, and how the table presets handle and use those colors.

Ah, forgot to mention. Fixed this problem also.

Two things I can’t figure out regarding variables:

First

3 Objects in snippet below:

  • image
  • vertical menu
  • toggle switch

With a local variable I want to show and hide the image and menu at the same time after pushing the button/switch. Only the image reacts to this switch. I can’t figure out why. Image and menu use the same Visibility local variable.

In versions before 1.8.0 I had defined this same local on/off variable in the State setting of the switch. When I still fill this setting with this variabele, the switch won’t react anymore…
image

{
  "snippetHeader": "Dashboard Studio Snippet",
  "snippetType": "widget-snippet",
  "snippetFormatVersion": 1,
  "version": "1.8.2",
  "source": {
    "dashboardName": "HomeDashV2",
    "page": 42,
    "createdAt": "2026-05-01T06:55:02.674Z"
  },
  "master": {
    "currentPage": 42
  },
  "widgets": {
    "menu_486281970": {
      "type": "menu",
      "overrides": {
        "x": 590,
        "y": 330,
        "width": 270,
        "height": 1090,
        "zIndex": 41,
        "page": "42",
        "label": "Apparaat",
        "menuItems": [
          {
            "label": "Airco huiskamer",
            "mode": "toggle",
            "actionType": "topic",
            "state": false,
            "selectionGroup": "apparaat_instelling",
            "sendOffOnGroupDeselect": true,
            "icons": {
              "on": {
                "source": "phosphor",
                "builtin": "",
                "filled": true,
                "customUrl": "",
                "tintImage": false
              },
              "off": {
                "source": "phosphor",
                "builtin": "",
                "filled": true,
                "customUrl": "",
                "tintImage": false
              }
            },
            "iconColorOverrides": {
              "on": {
                "enabled": false,
                "color": "#ffffff"
              },
              "off": {
                "enabled": false,
                "color": "#94a3b8"
              }
            },
            "outputTopic": "local/toonGroepAircoHuiskamer",
            "onPayload": "1",
            "offPayload": "31",
            "pageTo": 1,
            "urlTo": "https://"
          },
          {
            "label": "Airco slaapkamer",
            "mode": "toggle",
            "actionType": "topic",
            "state": false,
            "selectionGroup": "apparaat_instelling",
            "sendOffOnGroupDeselect": true,
            "icons": {
              "on": {
                "source": "phosphor",
                "builtin": "",
                "filled": true,
                "customUrl": "",
                "tintImage": false
              },
              "off": {
                "source": "phosphor",
                "builtin": "",
                "filled": true,
                "customUrl": "",
                "tintImage": false
              }
            },
            "iconColorOverrides": {
              "on": {
                "enabled": false,
                "color": "#ffffff"
              },
              "off": {
                "enabled": false,
                "color": "#94a3b8"
              }
            },
            "outputTopic": "local/toonGroepAircoSlaapkamer",
            "onPayload": "1",
            "offPayload": "46",
            "pageTo": 1,
            "urlTo": "https://"
          }
        ],
        "menuHAuto": false,
        "metaName": "Apparaat instellingen",
        "cornerRadius": 20,
        "typography": {
          "groupLabel": {
            "fontFamily": "'Exo 2', sans-serif",
            "fontSize": 30,
            "textTransform": "uppercase",
            "color": "#ffffff",
            "offsetY": -10,
            "bold": false
          },
          "menuItem": {
            "fontFamily": "'Exo 2', sans-serif",
            "fontSize": 30,
            "color": "#ffb300",
            "colorOff": "#ffffff",
            "textAlign": "left",
            "offsetX": 10
          }
        },
        "itemBgHover": "#ffb300",
        "menuItemHeight": 50,
        "itemSpacing": 4,
        "itemBgOnOpacity": 20,
        "itemBgHoverOpacity": 10,
        "borderColor": "#3B82F6",
        "borderWidth": 1,
        "bgVisible": true,
        "glowEnabled": true,
        "glowOpacity": 100,
        "glowRadius": 40,
        "visible": false,
        "bgOpacityStart": 0,
        "bgOpacityEnd": 0,
        "glowColor": "@surface",
        "itemBgOn": "#ffb300",
        "itemCornerRadius": 0,
        "item_0_state": false,
        "item_1_state": false,
        "item_2_state": false,
        "item_3_state": false,
        "item_4_state": false,
        "item_5_state": false,
        "item_6_state": false,
        "item_7_state": false,
        "item_8_state": false,
        "item_9_state": false,
        "item_10_state": false,
        "item_11_state": false,
        "item_12_state": false,
        "item_13_state": false,
        "item_14_state": false,
        "item_15_state": true,
        "item_16_state": false,
        "item_17_state": false,
        "item_18_state": false,
        "item_19_state": false,
        "item_20_state": false,
        "item_21_state": false
      },
      "bindings": {
        "menuItems": [
          {
            "state": "local/toonGroepAircoHuiskamer"
          },
          {
            "state": "local/toonGroepAircoSlaapkamer"
          }
        ],
        "visible": "local/Tonen"
      }
    },
    "image_283021209": {
      "type": "image",
      "overrides": {
        "x": 570,
        "y": 320,
        "width": 1520,
        "height": 1110,
        "zIndex": 40,
        "page": "42",
        "sourceType": "svg",
        "svgRaw": "<svg width=\"1520\" height=\"1110\" viewBox=\"0 0 1520 1110\" xmlns=\"http://www.w3.org/2000/svg\">\n  <foreignObject width=\"100%\" height=\"100%\">\n    <div xmlns=\"http://www.w3.org/1999/xhtml\" \n         style=\"\n\t\twidth:100%;\n\t\theight:100%;\n\t\tbackground:rgba(0,0,0,0.5);\n\t\tbackdrop-filter:blur(15px);\n\t\t-webkit-backdrop-filter:blur(15px)\">\n    </div>\n  </foreignObject>\n</svg>\n\n",
        "metaName": "Instellingen blur",
        "bgVisible": true,
        "bgOpacityStart": 0,
        "bgOpacityEnd": 0,
        "glowEnabled": true,
        "glowOpacity": 60,
        "borderColor": "#ffb300",
        "visible": false,
        "borderWidth": 2,
        "cornerRadius": 20,
        "labelFs": 40,
        "labelOffY": 0,
        "glowColor": "#ffb300",
        "colOff": "#878787"
      },
      "bindings": {
        "visible": "local/Tonen"
      }
    },
    "switch_690044903": {
      "type": "switch",
      "overrides": {
        "x": 340,
        "y": 810,
        "width": 110,
        "height": 120,
        "zIndex": 5,
        "page": "42",
        "state": true,
        "buttonLabel": "Settings",
        "outputTopic": "local/Tonen",
        "icons": {
          "on": {
            "source": "custom",
            "builtin": "",
            "filled": false,
            "customUrl": "https://cdn.jsdelivr.net/npm/@mdi/svg@7.2.96/svg/cog-outline.svg",
            "tintImage": true,
            "color": "@background"
          },
          "off": {
            "source": "custom",
            "builtin": "",
            "filled": false,
            "customUrl": "https://cdn.jsdelivr.net/npm/@mdi/svg@7.2.96/svg/cog-outline.svg",
            "tintImage": true,
            "color": "@surface"
          }
        },
        "iconColorOverrides": {
          "on": {
            "enabled": false,
            "color": "#ffffff"
          },
          "off": {
            "enabled": false,
            "color": "#94a3b8"
          }
        },
        "style": "image-only",
        "labelPos": "inside_bottom",
        "showIcon": true,
        "bevel": true,
        "scale": 3,
        "btnGlow": false,
        "btnGlowColor": "#ffbf00",
        "btnGlowRadius": 20,
        "btnRadius": 0,
        "outlineWidth": 0,
        "colThumb": "#ffffff",
        "metaName": "Screen slaapkamer",
        "bgVisible": true,
        "glowEnabled": true,
        "cornerRadius": 20,
        "iconColOn": "#ffbf00",
        "colOff": "#3385ff",
        "colOn": "#ffbf00",
        "colHover": "#ffbf00",
        "iconColOff": "#80b3ff",
        "bgOpacityStart": 10,
        "glowColor": "@surface",
        "borderColor": "#80b3ff",
        "borderWidth": 1,
        "glowOpacity": 100,
        "borderOpacity": 100,
        "iconSize": 22,
        "iconColHover": "#c0b980",
        "glowRadius": 30,
        "padding": 0,
        "fontFamily": "'Roboto', sans-serif",
        "bgColStart": "#ffffff",
        "bgColEnd": "#ffffff",
        "bgOpacityEnd": 15,
        "bgAngle": 120,
        "iconOffY": 18,
        "typography": {
          "buttonLabelOutside": {
            "fontFamily": "'Exo 2', sans-serif",
            "fontSize": 20,
            "textAlign": "center",
            "textTransform": "none",
            "bold": false,
            "color": "#7eb3ff",
            "colorOff": "#7eb3ff",
            "offsetX": 0,
            "offsetY": 10
          },
          "buttonLabelInside": {
            "fontFamily": "'Exo 2', sans-serif",
            "fontSize": 20,
            "textAlign": "center",
            "textTransform": "none",
            "bold": false,
            "color": "#7eb3ff",
            "colorOff": "#7eb3ff",
            "offsetX": 0,
            "offsetY": 10
          }
        },
        "_switchTypographyV2": true,
        "_switchIconsV2": true
      },
      "bindings": {
        "state": ""
      }
    }
  },
  "selectionGroups": {
    "apparaat_instelling": {
      "allowEmptySelection": true
    }
  }
}

Second

2 Objects:

  • image
  • switch (semi-transparent blue)

When I push the switch to enlarge the image, a global variable activates an advanced flow to change x, y, height/width values. The image and switch share the same variables for x, y, width and height.
The button is one step behind, but going to the form edit mode again, the button becomes the correct size again.
(low quality video because of the file size)
DS1.8.2

Maybe I don’t understand these local and global variables or am I overlooking something or is something going wrong.

Nice solution. I overlooked these automatic functions

This in 1.8.1 with new Text object…


…became this in 1.8.2 (look at column width, the zebra color change and legend)

[BUG] v1.7.4 — Enabling X axis makes chart data disappear

Hi, I’ve found a bug in Dashboard Studio v1.7.4 on Homey Pro Mini.

Problem:
When I enable the X axis on a chart (SOC graph), the chart data completely disappears. Only the X axis labels are shown at the top of the widget instead of the bottom, and the plot area appears empty.

Steps to reproduce:

  1. Add a chart widget to the dashboard
  2. Go to Axes & Labels settings
  3. Enable “Show X Axis”
  4. The chart data vanishes — only axis labels remain visible at the top

Expected behavior:
The chart should display both the data and the X axis labels.

Actual behavior:
Enabling X axis causes the chart to render empty. The axis labels appear at the top of the widget instead of the bottom, suggesting an internal layout/dimension calculation issue.

Workarounds tried:

  • Adjusting Graph Offset Y → no effect
  • Resizing the widget → no effect
  • Disabling X axis restores the chart immediately

Environment:

  • Dashboard Studio: v1.7.4
  • Device: Homey Pro Mini
  • X Axis Format: Hours
  • 24 Hour Clock: Enabled
  • Convert UTC to Local Timezone: Enabled

I’m attaching two screenshots showing the before/after with X axis disabled vs enabled.

Thanks!

wow… when did that happened :sweat_smile:
Have to dig deeper into this, the weird thing, old dashboard do not seem to have this problem. (check the graphs on the demo dashboard). Thanks for sharing a detailed bug report, I shall fix this :+1:

Ah, I tested only using local variables. I wrongfully assumed it would be solved using global variables as wel. I need to dig a bit deeper.

Yes, 0 values should be auto, but I accedently bugged it when one of the values is not 0, all the 0 (auto) columns collapses to 0px.

In the previous version you could not change the zebra color. In the new 1.8.2 version al colors are linked to the colors in the editor, so you can change it in whatever you like.

I shall investigate. :+1:

I was wondering when the slider can display a value between 0-100 if the actual value is between 0-1.
I use it for setting the volume of a Sonos speaker. I would like to display 14 instead of 0.14 as the volume in the slider.

Is there a location where I find the list(backlog) of features people have requested and maybe some priority/sequence when you will implement them (if and when you have time off course)?

Not yet but it is on the wish list to add an option to show the value in 0 to 100%. When the slider gets an update I shall implement this. :+1:

You can also just hide the value, add a label widget, and supply that label widget with the preferred value direct from Homey. Something like [the volume has changed] => [send to [VolumeTopic] Value: [{{volume * 10}}]]

Not an official public one, but I keep track of all requests. I try to fix bugs immediately, so those are always at the top of my list. When I overhaul a widget, I try to implement the requests for that specific one. I prefer to take it widget by widget so the testers here can report their findings. Otherwise, I would probably get overwhelmed.

I do not mind multiple of the same request. The more people want something the higher the importance of the request.

There is an official changelog over here

@Satoer Thanks for the update, again !

The calendar downloading, filtering, parsing, sending to the dashboard works perfectly without crashing.
There is, however, some fine-tuning needed.

For example I set today:
All Day events are not ‘kept’ in the final view (Day View)
More-days events are not kept either.

If I set yesterday:
All day-events seem to be kept, but more-day events that ended yesterday were not kept either.

Furthermore I’d like to know your way of thinking for the ‘tomorrow’, ‘this week’, ‘next week’… :

I want to see today & tomorrow, maybe the day after tomorrow as well… What happens when i set ‘this week’. It will probably do the right thing on monday, bu what happens on a sunday? If I set a custom range for 2 to 5 may, will it auto adjust every day?

Sorry for keeping you busy :wink: I don’t want to be responsible for another flow-breaking update :rofl:

update: it also works with 3 calendars combining the already combined JSON with another calendars JSON… So that part is definitely good !


The RAW SVG editing field now seems to work !

This does not work in the text-widget. (would be overkill probably, just wanted to let you knwo that I’ve tried it out)

Well… at least we are a step further :partying_face:

Ah, yes. Totally clear, I shall fix that.

The “Load & Filter iCal” card downloads and filters events for a specific period, while the “Convert to Markdown” card turns that data into a calendar table. It takes the first available date with events and formats it into a day, week, or month view. If you’re looking for a month view, you just need to ensure you’ve filtered for a full month of events.

I have to admit, I didn’t think this through entirely. :sweat_smile: It isn’t currently possible to view “today + X days” or “tomorrow + X days” because you can’t download that exact range. Honestly, the custom date range setting is pretty useless right now since nobody wants to manually adjust flow settings every time they need a specific range. I thought I’d found a shortcut because Homey has a nice date picker in the flow cards, and I assumed it could be dynamic or linked to a Homey variable so users could calculate ranges via HomeyScript. Unfortunately, that’s not how it works.

I’ve quickly realized that date calculations are a total minefield with endless edge cases. We use day - month format while Americans use month/day or month-day. Then there’s the start of the week: we start on Monday, the USA starts on Sunday, and parts of the Middle East start on Saturday. Regardless, I was still pretty proud of how far I’d gotten! haha I plan to swap the date pickers for regular edit boxes so users can add tags to create their own dynamic ranges. Hopefully, Homey offers some kind of localized date conversion to help out. I shall also convert the yesterday, today, tomorrow to a yesterday + 7 days, today + 7 days etc. This way the correct range is available for the convert to markdown card.

If you have another range in mind, let me know.

I did not even know the existence of these. I Shall see if I can integrate this.

Yeah, I guess it’s an intriguing job to find out ‘how’ functions are used + integrating them in both the Homey-end and the Dashboard-end… I wouldn’t even know where to start in the first place.

For me I don’t really care about what’s happening next week, a calendar is just to ‘not forget’ things :slight_smile:. Today +1 or +2 is more than enought for me. With the actual possibilities if I download ‘this week’ it starts on monday in the Dashboard… ‘next week’ starts on next monday…


For the Phospor Icons:

Above an “old” suggestion, still valid for the reintroduced Label object.

# New TEST version 1.8.3

  • Calendar Fixes: All-day and multi-day events now show up exactly where they should in your selected date range.
  • Flow Tags for iCal: You can now use Flow tags when setting custom start and end dates in the Load & Filter iCal tool.
  • Dynamic Buttons: Fixed a bug where buttons wouldn’t resize properly when adjusting them using global topics. (and other related bug based on this)
  • Light Grid: The auto-hide feature is back in action and working correctly.
  • Chart Stability: Turning on the “Show X Axis” labels in the Graph widget won’t cause the whole chart to collapse anymore.

Fixed, please check.

Fixed, please check. Also let me know if you are able to set / show the desired ranges you want.

I have this fixed in the current test version Nacho. You need to install the test version to use it, or wait until it is ready for a stable release.

Dynamic buttons: ✓

Show multiple objects after a button push (without filling State (on/off) setting): ✓

State on/off setting in button: ✗

Filling this state with a boolean still causes a not working button. Going to edit screen lay-out modus (leaving the user screen), and the result of the push button is as expected (in my case: show multiple objects).

Something else: in a Markdown table one can define alignment per column. With a Text Horizontal alignment one can define “Use typography settings”, but then the individual column alignment is not taken into account.

Is this the same bug like you described over here?:

Because I fixed the other bug with the resizing of the button. Then I tried to understand what you exactly wanted to do, tried the example snippet:

  • Reset Canvas
  • set Dashboard “Resolution Presets” to unlimited (because the snippet was large)
  • Imported the snippet
  • It asked which page, I let it set to the current page
  • Pressed the button on and off => The menu and image switched from visible to non visible just fine.

So I reckon I would probably solved the problem with the button bug fix or something, because this was exactly the behaviour you expected right? Note: I do not know what the “state” setting of the button supposed to do, because even though it is made dynamic, the edit box was empty.

If this is not the bug you wanted to explain. Please create a step by step tutorial for me how to recreate the exact bug. Or make a snippet from a completely clean dashboard because I think I am missing some things. As for:

This works fine at my side also (adding the output topic inside the state). You mention a “push” button, but I think you meant “Toggle Button” right? Otherwise, then it won’t work, because a push button does not have an “on” state.

Quickly ran some tests with a small script to create the date for tomorrow (or any other day) in a flow-tag ‘DatumMorgen’ so you can use these flow-tags ‘datum’ & ‘datumMorgen’ in the custom date range. That seems to work fine !!

One minor ‘issue’ is that an all-day-event of the previous day is kept. Only all day events.
Furthermore it seems to work as one can expect…

Other things that I think about (in my specific use case):

I now use the Day View, which shows events for today and tomorrow in chronological order, which is fine.
More-days-events are shown for today & tomorrow. You think it’s possible to just show them once ‘on top’?
All Day-events now show “All Day” in the time column. The Table headers are TIME & EVENT. My Homey, my Windows, my Chrome are all set in Dutch… you know of something I’m missing to have it in Dutch? I can make the table headers invisible with the opacity ands…, but I haven’t found a way yet to filter the All Day out of the time column… Any idea?
The possibility to show a ph-hamburger icon when I go out for dinner is fun, but is it also possible to disable this functionality?

As I said, it’s just the first things that come into my mind for my personal use, certainly nothing that needs to be ‘fixed’.

I can’t say it enough how apreciate all the work you’re doing here !