Automating NetMcr Event Tracking with Node-RED, Home Assistant, and Google Calendar

Automating NetMcr Event Tracking with Node-RED, Home Assistant, and Google Calendar
Flow overview: NetMcr email → extract → format → Google Calendar.

Introduction

As part of any community, you likely have event announcements arriving via email: talks, venues, dates, agendas. Manually copying those details into Google Calendar takes time and can lead to mistakes.

In this post, we’ll show you how to build a flow using Node-RED and Home Assistant that reads NetMcr announcement emails, extracts the key details, and automatically creates Google Calendar entries — so you never miss a meetup.


Why Automate NetMcr Event Tracking?

  • Timely reminders on all your devices (phone, tablet, work computer)
  • Consistency in event details (venue, date, time, agenda)
  • Shareability with others in the community
  • Historical record of past talks and events without manual record keeping

Prerequisites

Make sure you have the following:

  • Home Assistant up & running
  • Node-RED installed and integrated with Home Assistant
  • Google Calendar integration configured in Home Assistant (with valid credentials / OAuth)
  • An email inbox/folder that receives NetMcr announcement emails (IMAP access)

Step 1: Import the Node-RED Flow

The following is the code, which can be imported directly in to Node Red.

[{"id":"ef8e7668a938ef13","type":"group","z":"17bf9644c8f14af7","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["ef105138afb19864","588093991e4d5954","f62853687e465a24","4066152898d3e52b"],"x":14,"y":139,"w":932,"h":82},{"id":"ef105138afb19864","type":"e-mail in","z":"17bf9644c8f14af7","g":"ef8e7668a938ef13","name":"NetMCR-Grabber","protocol":"IMAP","server":"","useSSL":true,"autotls":"required","port":"993","authtype":"BASIC","saslformat":true,"token":"oauth2Response.access_token","box":"netmcr","disposition":"Read","criteria":"UNSEEN","repeat":"30","fetch":"auto","inputs":0,"x":120,"y":180,"wires":[["588093991e4d5954"]],"outputLabels":["payload"]},{"id":"588093991e4d5954","type":"function","z":"17bf9644c8f14af7","g":"ef8e7668a938ef13","name":"Talk Extractor","func":"// split the payload by the agenda, keep the 2 piece\n\n// split the payload by inclusiveness, keep the first piece.\n\nvar paras =  msg.payload.split(\"**\") \nvar time = paras[4].split(\"\\n\");\n\nvar agenda1 = msg.payload.split(\"** Agenda\")\nvar agenda = agenda1[1].split(\"** Inclusiveness\")\n\nreturn { event : paras[1].split(\"\\n\")[0], \n         venue : paras[2].split(\"\\n\")[0], \n         date : paras[3].split(\"\\n\")[0] , \n         time : time[0],\n        agenda : agenda[0]\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":320,"y":180,"wires":[["f62853687e465a24"]]},{"id":"f62853687e465a24","type":"function","z":"17bf9644c8f14af7","g":"ef8e7668a938ef13","name":"Event Formatter","func":"// Conver the output from Talk Extractor and convert to \n// a format for the calendar add function.\n\n/*\nevent : paras[1].split(\"\\n\")[0], \nvenue : paras[2].split(\"\\n\")[0], \ndate : paras[3].split(\"\\n\")[0] , \ntime : time[0],\nagenda : agenda[0]\n*/\n\n// Thursday, 12th June\n\nvar fixedDate1 = msg.date.split(\" \")[2].trim(); // 12th\n    fixedDate1 = fixedDate1.substr(0, fixedDate1.length - 2);\nvar fixedDate2 = msg.date.split(\" \")[3].trim(); // June\nvar fixedDate3 = new Date().getFullYear()  // Current year or current year +1 if January\n    if (fixedDate2 == \"Jan\") {\n            fixedDate3 = new Date().setFullYear(fixedDate3.getFullYear() + 1)\n    }\n\n\nvar fixedDate = fixedDate1  + \" \" + fixedDate2 + \" \" + fixedDate3;\n\nvar startTime = new Date(fixedDate + \" \" + msg.time.split(\"-\")[0].trim() + \":00\");\nvar endTime = new Date(fixedDate + \" \" + msg.time.split(\"-\")[1].trim() + \":00\");\n\nreturn { \n//        payload : { \n           title : msg.event, \n           location : msg.venue, \n           start : startTime.toISOString(), \n           end : endTime.toISOString(),\n           description : msg.agenda,\n           date: msg.date,\n           time: msg.time\n//        }\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":180,"wires":[["4066152898d3e52b"]]},{"id":"4066152898d3e52b","type":"api-call-service","z":"17bf9644c8f14af7","g":"ef8e7668a938ef13","name":"Tech Calendar Updater","server":"","version":7,"debugenabled":true,"action":"","floorId":[],"areaId":[],"deviceId":[],"entityId":[],"labelId":[],"data":"{ \t    \"summary\": msg.title, \t    \"description\": msg.description, \t    \"start_date_time\": msg.start, \t    \"end_date_time\": msg.end, \t    \"location\": msg.location \t}","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"results"}],"queue":"none","blockInputOverrides":false,"domain":"google","service":"create_event","x":810,"y":180,"wires":[[]]}]

Step 2: How It Works

Flow overview: NetMcr email → extract → format → Google Calendar.
  1. NetMCR-Grabber: Checks your NetMcr-announcement inbox via IMAP for new, unseen mails. The email should be sorted in to it's own folder for ease of extraction.
  2. Talk Extractor: Parses out title, venue, date, time, agenda from email content (NetMcr’s format).
  3. Event Formatter: Converts date + time to ISO8601, sets correct year, handles formatting of start / end times.
  4. Tech Calendar Updater: Calls Home Assistant’s Google Calendar service (e.g. google.create_event) to create the event. You will need to have created the google calendar and shared it with Home Assistant.

Step 3: Customising for NetMcr

  • Double-check you have the NetMcr email format correct (how announcements are structured, where "Agenda" etc appears).
  • Adjust the split/parsing logic in the “Talk Extractor” if the announcement format changes (“** Agenda”, “** Inclusiveness”, etc).
  • Time zone settings: ensure Home Assistant / Google Calendar are using the correct zone (UK / BST / GMT).

Troubleshooting

IssueLikely CauseSolution
Event created at wrong timeTime zone mismatch or parsing of time segment failedCheck msg.time, ensure the /-delimited times map properly; adjust for BST/GMT
Missing event detailsEmail content changed format or tags movedUpdate the function node (“Talk Extractor”) string splitting accordingly
Google Calendar auth errorsBad credentials or token expiredRefresh OAuth credentials, test google.create_event manually

Failsafe

To avoid disappointment, when you have created your calendar, populate it with a recurring placeholder for the second Thursday of the month. This is to give you advanced notification, since the emails don't always come out with plenty of notice. In addition, some events may change their dates.

Conclusion

With this setup, every time NetMcr sends out an announcement, your Google Calendar will automatically get the full event: title, venue, date/time, agenda. No more manual copy-pasting.

👉 Try pulling the flow above into Node-RED, connect it to your NetMcr announcement inbox, test with an actual email, and see the event pop up in Google Calendar.

Alternatively, if you would like access to ready made calendar (which may contain over events in the future), try this:

Google Calendar - Sign in to Access & Edit Your Schedule
Access Google Calendar with a Google account (for personal use) or Google Workspace account (for business use).

About the author

Tim Wilkes is a UK-based security architect with over 15 years of experience in electronics, Linux, and Unix systems administration. Since 2021, he's been designing secure systems for a telecom company while indulging his passions for programming, automation, and 3D printing. Tim shares his projects, tinkering adventures, and tech insights here - partly as a personal log, and partly in the hopes that others will find them useful.

Want to connect or follow along?

LinkedIn: [phpsytems]
Twitter / X: [@timmehwimmy]
Mastodon: [@timmehwimmy@infosec.exchange]


If you've found a post helpful, consider supporting the blog - it's a part-time passion that your support helps keep alive.

⚠️ Disclaimer

This post may contain affiliate links. If you choose to purchase through them, I may earn a small commission at no extra cost to you. I only recommend items and services I’ve personally read or used and found valuable.

As an Amazon Associate I earn from qualifying purchases.