-
-
Save jterrace/f412fdfac90687719cf3016ff968cc0b to your computer and use it in GitHub Desktop.
// Formats the given Date and returns map with format string parts. | |
const makeTimeFormatMap = function(date) { | |
const tz = Intl.DateTimeFormat().resolvedOptions().locale; | |
const dateTimeFormat = new Intl.DateTimeFormat(tz, { | |
month: 'short', | |
day: 'numeric', | |
weekday: 'short', | |
hour: 'numeric', | |
minute: 'numeric', | |
hour12: true, | |
}); | |
const partsMap = {}; | |
dateTimeFormat.formatToParts(date).map(({type, value}) => { | |
partsMap[type] = value | |
}) | |
partsMap.dayPeriod = partsMap.dayPeriod[0].toLowerCase(); | |
return partsMap; | |
}; | |
// Makes a Date from a date/time string. | |
const makeDate = function(dateString) { | |
if (dateString.includes("T")) { | |
return new Date(dateString); | |
} | |
// All day events just show up as "YYYY-MM-DD" with no | |
// time zone, so parse manually to make sure it's in "local" | |
// time zone. | |
var b = dateString.split(/\D/); | |
return new Date(b[0], b[1]-1, b[2]); | |
}; | |
// Returns an array of dates between the two time strings. | |
const getDaysArray = function (start, end) { | |
const days = []; | |
for (var dt = makeDate(start); dt <= makeDate(end); dt.setDate(dt.getDate() + 1)) { | |
days.push(new Date(dt)); | |
} | |
return days; | |
}; | |
const addEvent = function(events_by_date, date, dateStr, eventValue) { | |
events_by_date[date] = events_by_date[date] || {dateStr: dateStr, events: []}; | |
if (eventValue === null) return; | |
events_by_date[date].events.push(eventValue); | |
}; | |
const today = new Date(new Date().toDateString()); | |
const todayParts = makeTimeFormatMap(today); | |
const todayStr = `${todayParts.weekday} ${todayParts.month} ${todayParts.day}`; | |
const by_date = {}; | |
addEvent(by_date, today, todayStr, null); | |
for (const [calname, cal] of Object.entries(msg.payload)) { | |
for (const event of cal.events) { | |
const days = getDaysArray(event.start, event.end); | |
node.warn(days); | |
if (days.length == 1) { | |
const ts = days[0]; | |
const formatParts = makeTimeFormatMap(ts); | |
const dateStr = `${formatParts.weekday} ${formatParts.month} ${formatParts.day}`; | |
const timeStr = `${formatParts.hour}:${formatParts.minute}${formatParts.dayPeriod}`; | |
const dayDate = new Date(ts.toDateString()); | |
addEvent(by_date, dayDate, dateStr, timeStr + ' ' + event.summary); | |
continue; | |
} | |
// Multi day events end at midnight the day after, lop it off. | |
days.pop(); | |
for (const day of days) { | |
const dayDate = new Date(day.toDateString()); | |
if (dayDate < today) continue; | |
const formatParts = makeTimeFormatMap(day); | |
const dateStr = `${formatParts.weekday} ${formatParts.month} ${formatParts.day}`; | |
addEvent(by_date, dayDate, dateStr, event.summary); | |
} | |
} | |
} | |
node.warn(by_date); | |
const drawPayloads = []; | |
var largeSize = 30; | |
const sortedByDate = [...Object.keys(by_date)].sort(function (a, b) { | |
const x = new Date(a); | |
const y = new Date(b); | |
return x < y ? -1 : x > y ? 1 : 0; | |
}); | |
for (const dt of sortedByDate) { | |
drawPayloads.push( | |
{ | |
type: "text", | |
value: by_date[dt].dateStr, | |
font: "ppb.ttf", | |
x: 5, | |
size: largeSize, | |
color: "red" | |
}); | |
largeSize = 25; | |
if (by_date[dt].events.length > 0) { | |
drawPayloads.push( | |
{ | |
type: "multiline", | |
value: by_date[dt].events.join('|'), | |
delimiter: '|', | |
font: "rbm.ttf", | |
offset_y: 30, | |
x: 10, | |
size: 20, | |
y_padding: 5, | |
color: "black", | |
anchor: "la" | |
}); | |
} | |
} | |
node.warn(drawPayloads); | |
return {payload: drawPayloads }; |
Are you calling calendar.get_events? Try piping the output to a debug node with "complete msg object" as the output so you can see what the JSON object looks like on the right side debug message panel.
In the "Fetch Calendar Items" service node I've selected "calendar" as domain and "get_events" as service. I've also selected the correct calendar as entity. In the data field I entered the "start_date_time" line you shared on your blog.
Debug node isn't giving any error messages. I've played around with it quite a bit and as far as I can tell the issue is that the fetch isn't getting any events from the calendar.
If you go to Developer Tools -> Services and call calendar.get_events there, does it return anything? If not, maybe your calendar isn't being fetched properly?
That works, I get 2 events for the same timeline. However I still can't get the service call to fetch any events. If I use a current state node it shows 2 events, and shows what they are. I've tried multiple calendars and with start and end time as well as the snippet that's written on the blog.
This is what the call service node looks like, just to clarify.
I also had the problem that nothing came out from my action "get_events".
-> the solution was to add the "output properties": msg.payload = results
i´m not so keen in witing programms - i could only use and change some things...
So the flow works great, but i wan´t to change some things to make it more fit for me:
The day are giben in english words: is there a possibility to wirte this in german? The same for 12h and 24h...you comment something in row 25 to 27 about the "local" thinks, but i did not understand what i could change...
and an other question is, if i can impement a second calender
Just updated the script with a few changes I've made that weren't posted. You can add multiple entities to fetch with the calendar.get_events. Here you can see I have both my family calendar and holidays. And yes, you have to add the msg.payload output property now:
For 24 hour time, change hour12: true
to hour12: false
.
As for the language/locale, I'm not sure how node red pulls that information into the nodejs context, so I don't think I can help there.
Dear Jeff. Thanks a lot for the new skript...the both (or more?) calender words great. I spend now a lot of time in changing the 12h to 24h and the language...after a few hours trying (I´m not a programmer - only a user) I aked chatgpt...and this gave me a simple answer... and now it works perfekt: The only change that is nessesary is in row 3: change the value of "const tz":
here the example (see also the comments for the 12h to 24h-Change):
// Formats the given Date and returns map with format string parts.
const makeTimeFormatMap = function (date) {
const tz = "de-DE"; // Set locale to German
const dateTimeFormat = new Intl.DateTimeFormat(tz, {
month: 'short',
day: 'numeric',
weekday: 'short',
hour: 'numeric',
minute: 'numeric',
hour12: false, // For AM/PM format (can be removed if 24h format is preferred)
});
const partsMap = {};
dateTimeFormat.formatToParts(date).map(({ type, value }) => {
partsMap[type] = value;
});
partsMap.dayPeriod = partsMap.dayPeriod ? partsMap.dayPeriod[0].toLowerCase() : ''; // Adjust for AM/PM
return partsMap;
};
Later I had to adjust the writing: in Germany we do the day first, then the month...
I also had to adjust the size (thats what most people have to do to make it fit to their display) of the text an spaces between the lines because i only use a 1,54" display - i want to mount it into a electric-Socket in the bathroom...
Thanks a lot...
Wondering if I could get some help getting this going. Changing const cal = msg.payload["calendar.terrace_household"]; to the name of my calendar doesn't seem to work. It's not fetching any data.