Oh, but those are (svg) images you have tinted. Not the build-in phosphor icons Only the color of the build icons can be set using the editor.
Yes , I use external svg library with over 7400 unique icons. Downside: not nicely integrated as Phosphor icons. The Phosphor library contains over 1500 unique icon images.
@Bert_Onraedt could use this external library and define his own color. The color could be variable if Homey script is used.
Thanks for the explanation.
I hope someone is paying you for professional programming:-)
No need to put it to the roadmap. Just wanted to make sure I did not oversee functionality…
Thank you, it’s really a great tool!!
In the meantime I optimized the weather forecast to reduce api calls and homey load. I used homeyscript directly and set two tags to feed dashboard studio. This is better readable and editable.
// --------------------------------------------------
// Configuration
// --------------------------------------------------
const LOCATION = "Heidenheim an der Brenz";
const ICON_SIZE = 30;
const TIMEZONE = "Europe/Berlin";
try {
// --------------------------------------------------
// Geocoding
// --------------------------------------------------
const geoUrl =
`https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(LOCATION)}&count=1&format=json`;
const geoResponse = await fetch(geoUrl);
if (!geoResponse.ok)
throw new Error(`Geocoding failed: ${geoResponse.status}`);
const geoData = await geoResponse.json();
if (!geoData.results?.length)
throw new Error(`Location not found: ${LOCATION}`);
const { latitude: lat, longitude: lon } = geoData.results[0];
// --------------------------------------------------
// Weather (alles in EINEM Request)
// --------------------------------------------------
const url =
`https://api.open-meteo.com/v1/forecast?` +
`latitude=${lat}` +
`&longitude=${lon}` +
`¤t=temperature_2m,relative_humidity_2m,wind_speed_10m,surface_pressure` +
`&hourly=temperature_2m,weather_code` +
`&daily=weather_code,temperature_2m_max,temperature_2m_min,sunrise,sunset` +
`&timezone=${encodeURIComponent(TIMEZONE)}`;
const response = await fetch(url);
if (!response.ok)
throw new Error(`Weather fetch failed: ${response.status}`);
const data = await response.json();
const current = data.current;
const hourly = data.hourly;
const daily = data.daily;
// --------------------------------------------------
// Icons
// --------------------------------------------------
function getWeatherIcon(code) {
let icon = "ph-cloud";
if (code === 0) icon = "ph-sun";
else if ([1,2,3].includes(code)) icon = "ph-cloud-sun";
else if ([45,48].includes(code)) icon = "ph-cloud-fog";
else if ([51,53,55,56,57].includes(code)) icon = "ph-cloud-drizzle";
else if ([61,63,65,66,67,80,81,82].includes(code)) icon = "ph-cloud-rain";
else if ([71,73,75,77,85,86].includes(code)) icon = "ph-snowflake";
else if ([95,96,99].includes(code)) icon = "ph-cloud-lightning";
return `:${icon}|${ICON_SIZE}:`;
}
// --------------------------------------------------
// Aktuelle Stunde finden
// --------------------------------------------------
const currentHour = current.time.replace(/:\d\d$/, ":00");
let startIndex = hourly.time.indexOf(currentHour);
if (startIndex === -1)
startIndex = 0;
// --------------------------------------------------
// Header
// --------------------------------------------------
const windSpeed = current.wind_speed_10m.toFixed(1);
const humidity = current.relative_humidity_2m;
const sunrise = daily.sunrise[0].slice(11,16);
const sunset = daily.sunset[0].slice(11,16);
const header =
`\n---
| | |
| :--- | ---: |
| :ph-wind: ${windSpeed} km/h :ph-drop: ${humidity}% |:ph-sun: ${sunrise} :ph-sun-horizon: ${sunset} |`;
// ==================================================
// STUNDENVORHERSAGE
// ==================================================
let rowHours="|";
let rowHoursSep="|";
let rowHoursIcon="|";
let rowHoursTemp="|";
for (let i=0;i<7;i++) {
const idx=startIndex+i;
if(idx>=hourly.time.length)
break;
const d=new Date(hourly.time[idx]);
const time=
String(d.getHours()).padStart(2,"0")+
":"+
String(d.getMinutes()).padStart(2,"0");
rowHours += ` ${time} |`;
rowHoursSep += " :---: |";
rowHoursIcon += ` ${getWeatherIcon(hourly.weather_code[idx])} |`;
rowHoursTemp += ` ${Math.round(hourly.temperature_2m[idx])}°C |`;
}
const weatherHours =
`${rowHours}
${rowHoursSep}
${rowHoursIcon}
${rowHoursTemp}
${header}`;
// ==================================================
// TAGESVORHERSAGE
// ==================================================
const daysGerman = ["So","Mo","Di","Mi","Do","Fr","Sa"];
let rowDays="|";
let rowDaysSep="|";
let rowDaysIcon="|";
let rowMax="|";
let rowMin="|";
for(let i=0;i<7;i++){
const d=new Date(daily.time[i]);
rowDays += ` ${daysGerman[d.getDay()]} |`;
rowDaysSep += " :---: |";
rowDaysIcon += ` ${getWeatherIcon(daily.weather_code[i])} |`;
rowMax += ` ${Math.round(daily.temperature_2m_max[i])}°C |`;
rowMin += ` ${Math.round(daily.temperature_2m_min[i])}°C |`;
}
const weatherDays =
`${rowDays}
${rowDaysSep}
${rowDaysIcon}
${rowMax}
${rowMin}`;
// --------------------------------------------------
// Flow Tags
// --------------------------------------------------
await tag("WeatherHours", weatherHours);
await tag("Weatherdays", weatherDays);
// --------------------------------------------------
// Ausgabe
// --------------------------------------------------
return `Weather updated successfully.`;
}
catch(error){
return `Error: ${error.message}`;
}
Yes, I use colored png images now. I only need 6, so a 7000-icons-gallery might be a bit overkill ![]()
I just wanted to test the integrated color-method to make it more lightweight. It also works of course with the ´block´ colors, but i find that a bit overwhelming in my mostly black&white dashboard.
NEW TEST Version V1.11.2
- New: Text widget - tint inline Phosphor icons in markdown with the same pipe color syntax as images (e.g. :ph-heart|@accent: or :ph-duotone ph-android-logo|@accent|@danger:).
- New: Text widget - New setting, choose how links open (follow each link (markdown setting), always a new tab, or always the same tab).
You can now color the ph icons. See the markdown documentation for the syntax ![]()
I can’t figure out why there suddenly is a horizontal scrollbar in this markdown table (can’t remember I have seen this before):
When I increase the “Offset Y” value in “Text settings” the horizontal bar (partly) disappears.
{
"snippetHeader": "Dashboard Studio Snippet",
"snippetType": "widget-snippet",
"snippetFormatVersion": 1,
"version": "1.11.1",
"source": {
"dashboardName": "HomeDashV2",
"page": 28,
"createdAt": "2026-07-02T09:36:15.202Z"
},
"master": {
"currentPage": 28
},
"widgets": {
"text_0973": {
"type": "text",
"overrides": {
"x": 700,
"y": 350,
"width": 1990,
"height": 1280,
"page": 28,
"text": "",
"typography": {
"mdParagraph": {
"fontSize": 50,
"fontFamily": "'Exo 2', sans-serif",
"bold": false,
"color": "@onSurface",
"italic": false,
"textAlign": "left",
"offsetY": 0,
"offsetX": 20
},
"mdH1": {},
"mdH2": {},
"mdH3": {
"fontSize": 12,
"fontFamily": "'Exo 2', sans-serif",
"offsetX": 0
},
"mdTh": {
"fontSize": 40,
"fontFamily": "'Exo 2', sans-serif",
"textAlign": "table_defined",
"textTransform": "none",
"bold": false,
"color": "@background",
"colorFilled": "@background",
"offsetY": 0,
"offsetX": 0
},
"mdTableText": {
"fontSize": 35,
"textAlign": "table_defined",
"offsetY": 0,
"fontFamily": "'Exo 2', sans-serif",
"offsetX": 20
},
"mdBlockTypography": {}
},
"metaName": "Statussen",
"bgVisible": true,
"glowEnabled": true,
"bgOpacityStart": 0,
"bgOpacityEnd": 0,
"glowColor": "#000000",
"glowOpacity": 80,
"borderColor": "@background",
"borderOpacity": 30,
"cornerRadius": 20,
"textVerticalAlign": "top",
"mdTableColumnWidthMode": "custom_columns",
"mdSeparatorOpacity": 0,
"mdTableStyle": "zebra",
"mdTableRowAltBgOpacity": 7,
"mdOverflowScroll": true,
"mdTableCustomColumnWidths": {
"columns": [
40,
100,
600,
600,
550
]
},
"mdTableRowAltBg": "#E2E8F0",
"mdBlockSpacing": 4,
"mdTextWrap": false,
"mdAutoFitText": false,
"padding": 0,
"textOffX": 0,
"mdTableBorderRadius": 0,
"mdTableCellPadding": 0,
"mdIconWeight": "regular",
"textOffY": 0
},
"bindings": {
"text": "apparaatStatussen"
}
}
}
}
I did not see any problems on the demo pages with scrollbars. The provided DS snippet does not have the data Amersfoort. (text is dynamic) I can not recreate the problem without that. Can you also provide the data?
Here is the data (icons in an old-fashioned way):
Canon printer iP7250
| | | Kenmerk | Waarde | Laatste wijziging |
| :--- | :---: | :--- | :--- | :--- |
| | <img src="https://api.iconify.design/mdi:alert-circle.svg?color=%23FFBF00&width=60&height=60" width="60" height="60" /> | Netwerkstatus | Offline | 2-7-2026, 13:25 |
| | <img src="https://api.iconify.design/mdi:printer-outline.svg?color=%2380B3FF&width=60&height=60" width="60" height="60" /> | Signaalsterkte | 100 % | 29-6-2026, 9:15 |
| | <img src="https://api.iconify.design/mdi:water.svg?color=%23000000&width=60&height=60" width="60" height="60" /> | Zwart (PGBK) | 100 % | 7-6-2026, 19:36 |
| | <img src="https://api.iconify.design/mdi:water.svg?color=%23666666&width=60&height=60" width="60" height="60" /> | Zwart (BK) | 40 % | 17-3-2026, 14:14 |
| | <img src="https://api.iconify.design/mdi:water.svg?color=%23ffff00&width=60&height=60" width="60" height="60" /> | Geel | 100 % | 13-5-2026, 16:16 |
| | <img src="https://api.iconify.design/mdi:water.svg?color=%2300ffff&width=60&height=60" width="60" height="60" /> | Cyaan | 40 % | 26-4-2026, 9:18 |
| | <img src="https://api.iconify.design/mdi:water.svg?color=%23FD3DB5&width=60&height=60" width="60" height="60" /> | Magenta | 100 % | 17-6-2026, 12:06 |
Thanks. I will fix this problem next version ![]()
@satoer You’re the best !!
Seems to work fine, gonna adapt the calendar script in the weekend… ![]()
@widameista I also use open meteo in a script. I didn’t have the time to test your script yet, but it’s odd to me that you create only 2 tags? I have 24… some of them are only used in a e-mail & others are sent to Dashboard Studio.
I saw you use ph-cloud-drizzle as an icon, but that one doesn’t work in Dashboard Studio with me… Does it for you?
No the drizzle does not exist. I replaced it with ph-snowflake in the documentation. This icon is a cloud with small drops (not actual snowflakes)
adding additional tags is not a problem, but my intent was not to retrieve single weather details, but only the forecast in a Dashboard studio formatted manner ![]()
Aha, I now tested your script. Didn’t see until now that there is a whole mark-up integrated.
Makes complete sense & looks clean ![]()
Hello,
First of all thank you so much for your work, this is amazing. I’ve started recently so it might be a stupid question. I would like to show a timeline in my dashboard with notifications from Homey. I’ve tried to use the list/log widget. I made a flow that when there’s a notification in the timeline it sends it to the dashboard but I don’t understand how to show it in the dashboard after. Am I missing something obvious ?
Thanks in advance.
Hello Florence,
There’s probably a lot of different ways you can do that with Dashboard Studio.
An option can be to create a text-widget and link the text content to a unique token. In the flow you can use the Dashboard card “Send ‘unique token’ with value (tag-from-this-flow:)‘message’”
This won’t be the greatest lay-out however.
You can also put the content of the timeline messages in a variable in Homey. Create a lay-out in the text widget and then insert the variable in the text widget using the {{variable}}.
A third option, which I’m doing, is to put the (important) messages not only in the timeline, but also send them to a script. That script puts the message in JSON in a variable. It also keeps the 15 latest messages. In the Dashboard a list-widget is linked to that variable. The result looks like:
And there’s probably a lot of other options…
Thank you! It’s exactly what I wanted.
Which one of the three did you go for?
3rd one, it works fine for the messages but I seem to have made a mistake with times because all notifications have the same time (the time of the latest notification I think).
Not sure if you can help but I’ve made the script with the help of AI and I activated “show timestamp” in DashboardStudio. Am I supposed to link timestamp with something? Or is there a problem with the way the script gets the time?
const message = args[0];
const MAX_ENTRIES = 20;
const variables = await Homey.logic.getVariables();
const variable = Object.values(variables).find(v => v.name === “Timeline”);
if (!variable) {
throw new Error("La variable 'Timeline' n'existe pas.");
}
let timeline = [];
try {
timeline = JSON.parse(variable.value);
} catch (e) {
timeline = \[\];
}
const now = new Date();
const date = now.toLocaleDateString(“fr-FR”);
const time = now.toLocaleTimeString(“fr-FR”, {
hour: "2-digit",
minute: "2-digit"
});
timeline.unshift({
datetime: \`${date} ${time}\`,
text: message
});
timeline = timeline.slice(0, MAX_ENTRIES);
await Homey.logic.updateVariable({
id: variable.id,
variable: {
value: JSON.stringify(timeline)
}
});
return true;
I don’t know **** about scripts, I also use AI to make them… It’s a question of trial and error…
Here’s mine:
// Config
const VARIABLE_NAME = 'dashboard_notifications';
const MAX_ITEMS = 15;
// Input verwacht:
// ph-icon|tekst
const input = args[0] || 'ph-bell|Leeg bericht';
// Splitsen
const parts = input.split('|');
const icon = parts[0] || 'ph-bell';
const text = parts.slice(1).join('|') || 'Leeg bericht';
// Nieuw item
const entry = {
icon,
text,
timestamp: Date.now()
};
// Variabele ophalen
const variables = await Homey.logic.getVariables();
const variable = Object.values(variables).find(
v => v.name === VARIABLE_NAME
);
if (!variable) {
throw new Error(`Variabele ${VARIABLE_NAME} niet gevonden`);
}
// JSON lezen
let list = [];
try {
list = JSON.parse(variable.value || '[]');
} catch (e) {
list = [];
}
// Nieuw item toevoegen
list.unshift(entry);
// Enkel laatste X bewaren
list = list.slice(0, MAX_ITEMS);
// Opslaan
await Homey.logic.updateVariable({
id: variable.id,
variable: {
value: JSON.stringify(list)
}
});
return list;
The script is saved as ‘eventlist.js’ To add items with the flow card:



