Skip to content

Instantly share code, notes, and snippets.

@jterrace
Last active January 22, 2025 08:01
Show Gist options
  • Save jterrace/f412fdfac90687719cf3016ff968cc0b to your computer and use it in GitHub Desktop.
Save jterrace/f412fdfac90687719cf3016ff968cc0b to your computer and use it in GitHub Desktop.
node-red code to pull calendar and push to eink
// 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 };
@jterrace
Copy link
Author

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:

image

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.

@Alex-Pi4
Copy link

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...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment