Simple Python script that reads Google Calendar and updates OH items with event information

Hi James,
I looked through all my openhab.log files since the end of August and did not find anything like that.
I am running openhab 2.2.0. Maybe older openhab versions throw this warning?

What do you get when looking into your items using REST API?

My items are looking like this:

{
 {
  "link": "http://openhabianpi:8080/rest/items/gCal_Event1_StartTime",
  "state": "2018-01-09T15:30:00.000+0100",
  "stateDescription": {
    "pattern": "%1$td.%1$tm.%1$tY %1$tH:%1$tM",
    "readOnly": false,
    "options": []
  },
  "type": "DateTime",
  "name": "gCal_Event1_StartTime",
  "label": "Event1 Start",
  "category": "calendar",
  "tags": [],
  "groupNames": [
    "gCalEvent"
  ]
}

Iā€™m not sure if your timezome information ā€œZT00:00:00+00:00ā€ ist a valid one ā€¦

Your google calendar settings should look like this:

grafik

Of course you may select another zone, but I guess there has to be selected at least the primary time zone

The problem is that this is not formatted correctly, there are two ā€˜Tā€™ segments. The script will do this if there is an exception thrown by ā€˜datetime.datetime.strptimeā€™ because it will fallback to just jamming the timezone on the end of the events start time. I guess the strptime function can throw but still format the date.

changing the code to this solves the problem:

        if 'start' in SingleEvent:
            EventStartTime = SingleEvent['start'].get('dateTime', SingleEvent['start'].get('date'))

        try:
            datetime.datetime.strptime(EventStartTime, '%Y-%m-%dT%H:%M:%S' + S.CalendarTimeZone)
        except ValueError:
            if "T" not in EventStartTime:
                EventStartTime = EventStartTime + 'T00:00:00' + S.CalendarTimeZone
            else:
                EventStartTime = EventStartTime

        if 'end' in SingleEvent:
            EventEndTime = SingleEvent['end'].get('dateTime', SingleEvent['end'].get('date'))

        try:
            datetime.datetime.strptime(EventEndTime, '%Y-%m-%dT%H:%M:%S' + S.CalendarTimeZone)
        except ValueError:
            if "T" not in EventEndTime:
                EventEndTime = EventEndTime + 'T00:00:00' + S.CalendarTimeZone
            else:
                EventEndTime = EventEndTime

2 Likes

Thanks so much for the help, modifying the script has solved the issue.

Hi guys,

any ideas ? IĀ“ve read the whole documentation of this Google API but i didnĀ“t find anything for the error: Connection aborted.
System is running on a RPI 3 (with Internet Access :slight_smile: )

pi@raspberrypi:/etc/openhab2/scripts/CalSyncHAB-master $ python CalSyncHAB.py 
Traceback (most recent call last):
  File "CalSyncHAB.py", line 126, in <module>
    Main()
  File "CalSyncHAB.py", line 58, in Main
    OpenHABResponse = requests.post(CalendarEventSummaryItemURL, data = '', allow_redirects = True)
  File "/usr/lib/python2.7/dist-packages/requests/api.py", line 94, in post
    return request('post', url, data=data, json=json, **kwargs)
  File "/usr/lib/python2.7/dist-packages/requests/api.py", line 49, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/lib/python2.7/dist-packages/requests/sessions.py", line 457, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/lib/python2.7/dist-packages/requests/sessions.py", line 569, in send
    r = adapter.send(request, **kwargs)
  File "/usr/lib/python2.7/dist-packages/requests/adapters.py", line 407, in send
    raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', error(111, 'Connection refused'))

Best regards

This one looks like your credentials donā€™t match. Are you sure:

  • Username/password are correct
  • Your secret JSON is readable and the correct one
  • Your calendar URL is correct and
  • You configured the calender to be readable

I took @FrankRā€™s example and tried to put my own items in the ruleā€¦ Iā€™m stuck right at the endā€¦ :wink:

Iā€™ve got basically the same Setup with items and stuff (a bit of a renaming)ā€¦

My rule:

rule "Binder Abfallkalender"
when
	Time cron "0 0 1 * * ?" or
	System started
then
	gCalTrash.members.forEach[ gc | 
		val eventStart = gCalendar.members.filter[ act | act.name == gc.name+"_StartTime"].head 
		val eventEnd =   gCalendar.members.filter[ act | act.name == gc.name+"_EndTime"].head 
		val eventDesc =  gCalendar.members.filter[ act | act.name == gc.name+"_Description"].head 
		val eventSum =   gCalendar.members.filter[ act | act.name == gc.name+"_Summary"].head 
		var dtStart = new DateTime((eventStart as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
		var dtEnd = new DateTime((eventEnd as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
...
end

I canā€™t get the right casts for dtStart and dtEnd

2018-02-19 20:21:35.713 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Error during the execution of startup rule 'Binder Abfallkalender': Could not cast Calendar_GoogleTrash_Event1_StartTime (Type=DateTimeItem, State=2018-02-19T00:00:00.000+0100, Label=TrashKal Ev1 Start, Category=calendar, Groups=[gCalendar]) to org.eclipse.smarthome.core.library.types.DateTimeType; line 41, column 31, length 26

this is the exact line with var dtStart...

Whatā€™s wrong here?

I tried:

var eventStart
var DateTime eventStart
var eventStart ...           as DateTimeItem
var DateTime eventStart ...  as DateTimeItem

// also the deprecated
		var dtStart = new DateTime((eventStart as DateTimeType).calendar.timeInMillis)

always the same error-messageā€¦

Hi Thomas,
I still run

var DateTime dtStart = new DateTime((eventStart.state as DateTimeType).calendar.timeInMillis)

There has to be a ā€ž.stateā€œ following ā€ževentStartā€œ

1 Like

Thanks. Trees and forest and so. It works now!

Here the fixed Version (without Deprecated Function) and != null changed to !== null

rule "ExecCal"
when
	Time cron "0 0/1 * * * ?"
then
	logInfo("ExecCal", "Start")
	gCal.members.forEach[ gc | 
		val eventStart = gCalEvent.members.filter[ act |
			act.name == gc.name+"_StartTime"].head
		val eventEnd = gCalEvent.members.filter[ act |
			act.name == gc.name+"_EndTime"].head
		val eventDesc = gCalEvent.members.filter[ act |
			act.name == gc.name+"_Description"].head
		val eventSum = gCalEvent.members.filter[ act |
			act.name == gc.name+"_Summary"].head
		var DateTime dtStart = new DateTime((eventStart.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
		var DateTime dtEnd = new DateTime((eventEnd.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
		var Long dStart = now.millis - dtStart.millis
		var Long dEnd = now.millis - dtEnd.millis
		if(dStart >=0 && dStart < 60000) {
			val buffer = eventDesc.state.toString.split(";")
			val calItem = gCalItem.members.filter[ ci | ci.name == eventSum.state.toString].head
			if (calItem !== null) {
				logInfo("ExecCal", gc.name + " starting now with '" + calItem.name + "' -> " + buffer.get(0))
				sendCommand(calItem, buffer.get(0))
			}
			else {
				logInfo("ExecCal", "WARNING: cannot find item named '" + eventSum.state + "'")
			}
		}
		if(dEnd >=0 && dEnd < 60000) {
			val buffer = eventDesc.state.toString.split(";")
			val calItem = gCalItem.members.filter[ ci | ci.name == eventSum.state.toString].head
			if (calItem !== null) {
				sendCommand(calItem, buffer.get(1))
				logInfo("ExecCal", gc.name + " ended now with'" + calItem.name + "' -> " + buffer.get(1))
			}
			else {
				logInfo("ExecCal", "WARNING: cannot find item named '" + eventSum.state + "'")
			}
		}
	]
	logInfo("ExecCal", "End")
end

Try this :slight_smile:

var dtStart = new DateTime((eventStart as DateTimeType).zonedDateTime.toInstant.toEpochMilli

Nice work,
I use IFFTT with the calender, higher WAF factor, but I will definately look into this one aswell!

One error seems to be the Python script.
My Max Value is on 5 events!
if I now create an event at 17:00 for 17:15 and read in Openhab.
If I delete the event before 17:15 and read in Openhab, but only 4 events in the future, then the event will not be removed at 17:15 in Openhab!

I think we need a little Function like

if RetrievedEvents < MaxEvents than Clear all Openhab Itemā€™s

I you didnā€™t Change the Script, in line 54 to 70 all events are purged and then theyā€™re created anewā€¦ There shouldnā€™t be the fitfth element still in openHAB?

Yes ā€¦ but if RetrievedEvents only 4 Itemā€™s then the Fifth Item are not cleared

Ah - Thatā€™s right! I changed the script a bit, Iā€™m running a few tests this week.
remember me to publish my code on the weekend. I took care of purging Event-IDs, which are no more used ā€¦

1 Like

5 Events in Future
04
RestAPI 5 Event


Now I deleted the 5 Eventā€¦
53
RestAPI for 5 Event

Here is my Fixed Version :wink:

PS: My python Skills are not very good ā€¦ but it works ā€¦ :wink:

2 Likes

I Kind of hijacked the script by @davorf and did mainly the following works:

  1. add MQTT-Support
  2. made it controllable, to either use MQTT or REST to update OH2 items
  3. added reading for additional Event-Attributes: EventId, EventNotificationTime, EventEmailTime
  4. moved from pruging all items and updating them afterwards to purging those items, which are not used anymore

ad 1) + 2)
you can now chose your weapon of choice for updating: MQTT or REST

ad 3)
I like the idea to have an Event to trigger something directly at the StartTime, but I also have Events, which I use for throwing my Kids out of the house! :wink: e.g. they have an appointment for ther sports, I let openHAB say via the room loudspeakers: ā€œGet out to < EVENTNAME>ā€. For that Iā€™d like to have distinct times and I use Google Calender notification for it.

  • Email-Notification time for notifying way in advance
  • normal notification time for notifying a few minutes before

So you can have actions and rules on that tiems.

ad 4)
at the end, the script compares the number of items updated with the MaxEntry in the settings and purges left overs in OH2, if there are ones.
That way you wonā€™t have needless ā€œitem changesā€ within openHAB on every script run.

Iā€™m unsure, if I would make an PR for this on davorfā€™s githubā€¦ or if I should just fork it - @davorf, whatā€™s your opinion? :wink:

Hello!

Feel free to make a PR, but please, get the latest version first, since Iā€™ve just merged a pull request before noticing your post. These changes are really good, but thereā€™s a possibility of conflict, so, it would be much easier if you would apply those changes on the latest source. Thank you for your contribution.

Best regards,
Davor

1 Like