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

Whatever you did to get here, before you deleted everything. Then you can invoke the script from OpenHAB, before you do that you will get those errors.

was a mistake. Nothing worked fine. My items were only updated from starting the script outside openhab, directly from the shell.

The thing is that before and after I reinstalled the script and credentials the script only worked when started from the shell. Though it looked as if it never worked when started by openhab.

The problem is not that the script isn’t started by openhab. In fact the script is started by openhab, but after a few steps it prompts for verification code from within “tools.run_flow()” function. From within openhab it looks like the credentials aren’t transferred into that function. Started from the shell (by user “openhabian”) everything works fine. The executeCommandLine is started by user “openhab” which seems to be the only difference. That’s why I granted 777 rights to the complete path where CalSyncHAB.py is started from.

Hmm
 Strange. I looked at my command and I’m actually running the command from a .sh script, I think that was the only way to get it to work. Also you may just want to chown openhab:openhab everything

mike@housebitch:~$ more /etc/openhab2/scripts/calsynchab.sh
#!/bin/sh
/usr/bin/python /etc/openhab2/scripts/CalSyncHAB/CalSyncHAB.py --noauth_local_webserver

Hello!
I put the call to the script into a shell scripts as above shown by @Mike_Bagdanoff . I also chown’d /etc/openhab2/scripts/CalSnycHAB folder and all it’s containing files with openhab:openhabian. Nothing helped.

But the following command did the trick while being looged into the shell with user “openhabian”:

sudo -u openhab /etc/openhab2/scripts/CalSyncHAB.sh

The script prompted me for verification code. I copied the shown url into the browser on my windows machine and pasted the given verification code back into the script prompt.

From there on the script called from openhab runs perfectly without prompting and updates my items. There must be slight difference in the verification codes depending on the unix user. As “openhab” doesn’t have a shell, I was always logged in via ssh with “openhabian”. But the scripts ist executed by the OH2 rule with user “openhab”.

Uff, such a simple solution.

To summarize the entire story I can recommend the following on a headless unix machine:

  1. Do like described by @davorf on top of this thread
  2. create a folder “CalSyncHAB” within /etc/openhab2/scripts
  3. Put the python scripts, .ini file and the .json file into the new folder
  4. “chown openhab:openhabian /etc/openhab2/scripts/CalSyncHAB”
  5. cd into CalSyncHAB folder and “chown openhab:openhabian *”
  6. open .ini file and for setting “ClientSecretFile” enter the complete path, e.g. “ClientSecretFile: /etc/openhab2/scripts/CalSyncHAB/CalSyncHABSecret.json”
  7. create a script “/etc/openhab2/scripts/CalSyncHAB.sh” with: “#!/bin/sh
    /usr/bin/python /etc/openhab2/scripts/CalSyncHAB/CalSyncHAB.py --noauth_local_webserver”
  8. “chown openhab:openhabian /etc/openhab2/scripts/CalSyncHAB.sh”
  9. “sudo -u openhab /etc/openhab2/scripts/CalSyncHAB.sh”, copy the url shown by the script into your browser, click “Allow” and paste the given code back into the script prompt.
  10. From your openhab rules do: “executeCommandLine(”/etc/openhab2/scripts/CalSyncHAB.sh",5*1000)"
  11. Be happy :smiley:
5 Likes

Now that I’ve got the script working, how can I raise events from my calendar items?

Hello!

Sorry, but I don’t understand what you mean with “raise events from calendar items”.

Best regards,
Davor

I would like to have rules that react upon the start time and end time items. But I don’t know how to write them without creating a cron rule that checks every minute if one of the items equals current time of day

Hello!

The only way I know is using a cron expression, even though I made this script so I could display events in UI. Haven’t had any plans on triggering something using it. Since you’re already using a cron expression for script execution, you could use the same rule and check start/end time. If current time is within start/end time range, you could turn on some virtual switch (one switch for every set of event items), and then react upon switch state change.

Best regards,
Davor

Hello davorf,
Thanks, I thought about this kind of solution, but would like to avoid it. I thought there might be something like

rule "heatingSetPoint"
when
    Channel '...GoogleCal_Event1_StartTime#start' triggered START
then
   ... send command to heating with temperature given by GoogleCal_Event1_Description ...
end

Something that looks like the events raised by astro-binding.

Hello!

This approach would require a binding that has channels and things defined. Since this is just an external script, I’m pretty sure it can’t be done this way.

Best regards,
Davor

Ok, thanks @davorf. I will try

Well, here is my solution for my own question. I am using two cron expressions. One retrieves the calendar items from my Google Cal while the other one executes the events.

All items need to be in special groups.
There are some dummy items which are only used for naming conventions. They all belong to group (gCal):

String  gCal_Event1 (gCal)
// 5 more
String  gCal_Event6 (gCal)

Then we have our event items, which belong to group (gCalEvent):

String     gCal_Event1_Summary "Event1 Sum.: [%s]" <calendar> (gCalEvent)
String     gCal_Event1_Location "Event1 Loc.: [%s]" <calendar> (gCalEvent) 
String     gCal_Event1_Description "Event1 Desc. [%s]" <calendar> (gCalEvent) 
DateTime   gCal_Event1_StartTime  "Event1 Start [%1$td.%1$tm.%1$tY %1$tH:%1$tM]" <calendar> (gCalEvent)
DateTime   gCal_Event1_EndTime "Event1 End [%1$td.%1$tm.%1$tY %1$tH:%1$tM]" <calendar> (gCalEvent)
// 5 more
DateTime   gCal_Event6_EndTime "Event6 End [%1$td.%1$tm.%1$tY %1$tH:%1$tM]" <calendar> (gCalEvent)

Last but not least there are alle the items I want to control. They belong to group (gCalItem)

Switch lightKitchen "kitchen light" (gCalItem)
Number heatLiving "heat linving room [%s °C]" <heating> (gCalItem)
Switch wallplugOffice "wallplug office" <wallplug> (gCalItem)
//... and many more

My first rule retrieves the next 6 events from my calendar via CalSynHAB every half hour:

rule "GetCalEvents"
when
	Time cron "0 0/30 * * * ?"
then
	var String results = executeCommandLine("/etc/openhab2/scripts/CalSyncHAB.sh",5*1000)
	logInfo("GetCalEvents", results)
end

The second rule is the heart of it all. It is executes very minute. The name of the item to be controlled has to be put into the event summary. The commands that shall be sent are placed in the event description seprated by a semicolon. The first value is sent at starting time, the second when the event ends.

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).calendar.timeInMillis)
		var DateTime dtEnd = new DateTime((eventEnd.state as DateTimeType).calendar.timeInMillis)
		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

Thanks Davor,

great work! I’d like to have not only the Start and EndTime but also the notification times based on the reminders.
I don’t get to understand the returned JSON, so can someone help here?

I added some lines and now i also get back the notification times (email and popup) for each Event (YAY! I was looking for that since the beginning!). I’ll also would like to change the updates from the REST API to MQTT as I use that for all my items already.

What I don’t understand at first: Why does the script update the items twice? once at intitializing and then with the real Input? Is it because in case, the calendar won’t fetch the “maxEvents”? so, just in case you have now only 4 items from the standard 5, you would get the last one double (one as the “old” Event5 and then as the “new” Event4)?

If I’m stable enough, I’ll try to get the code available for those interested.

Thanks for this script. After spending a couple of days trying to get the CalDAV binding working reliably I had this script up and running in a few minutes.
I have now implemented a second instance of the script and now have two calendars updating without issue.

2 Likes

Is anybody having issues after upgrading to latest Openhab Release 2.2.0 ?

I have issues with my start and endtimes:

   2018-01-05 16:51:05.950 [WARN ] [rest.core.internal.item.ItemResource] - Received HTTP POST request at 'items/Calendar_Google_Home_Event1_StartTime' with an invalid status value '2018-01-21T08:30:00ZT00:00:00+00:00'.

I had those warnings already on the 2.1 stable, but one moment later everything gets updated fine.

What I am struggling to understand is what is wrong with the Time / Date Syntax which the script is pushing to the REST API, maybe something with the timezone


Received HTTP POST request at ‘items/Calendar_Google_Home_Event5_EndTime’ with an invalid status value ‘2018-11-04T10:00:00ZT00:00:00+00:00’.

Did you define your items as DateTime?

This should work:

DateTime   gCal_Event1_StartTime  "Event1 Start [%1$td.%1$tm.%1$tY %1$tH:%1$tM]" <calendar> (gCalEvent)
DateTime   gCal_Event1_EndTime "Event1 End [%1$td.%1$tm.%1$tY %1$tH:%1$tM]" <calendar> (gCalEvent)

Thanks for the reply, yes it is defined:

DateTime Calendar_Google_Home_Event1_StartTime “Event1 Start [%1$td.%1$tm.%1$tY %1$tH:%1$tM]” (gCalEvent)
DateTime Calendar_Google_Home_Event1_EndTime “Event1 End [%1$td.%1$tm.%1$tY %1$tH:%1$tM]” (gCalEvent)

And I get this error:

2018-01-09 13:14:18.130 [WARN ] [rest.core.internal.item.ItemResource] - Received HTTP POST request at ‘items/Calendar_Google_Home_E vent1_StartTime’ with an invalid status value ‘2018-01-21T10:00:00ZT00:00:00+00:00’.
2018-01-09 13:14:18.134 [WARN ] [rest.core.internal.item.ItemResource] - Received HTTP POST request at ‘items/Calendar_Google_Home_E vent1_EndTime’ with an invalid status value ‘2018-02-01T10:00:00ZT00:00:00+00:00’.