Interacting with Google Calendar

From Domoticz
Jump to: navigation, search

This guide describes how to set up Domoticz so you can use Google Calendar to schedule events. The components used are also available on other operating systems. Google has changed the way it allows the apps to interact with theirs sites, the OAuth 1.0 is now deprecated and it was replaced with OAuth 2.0. See https://code.google.com/p/googlecl/ This guide is deprecated.

Goal

Use Google Calendar to schedule events in your Domoticz.

Though it's intended use is to schedule regular events such as control your heating, lighting, and irrigation system, you can also use it to control your Domoticz from anywhere you have Internet access even when you can't access your Domoticz directly.

It works by making a list every 10-minutes of Calendar events for today. A script then scans the list each minute and switches devices Off and On based on entries in the Calendar listing.

The small print

  • Calendar events can be upto a year in duration and no more.
  • No checks are made that the calendar entries you specify are valid. Though errors won't break Domoticz they may prevent other calendar entries that are due from being actioned.
  • Don't use it for safety-critical applications. For example, don't use it to turn on heaters or machinery if no one is present to deal with the consequences.
  • If you don't keep your calendar private others can control your Domoticz and mess with your stuff.

No claim is made that this code fit for your purpose, I know only that it works for me. If the software breaks, your only entitlement is to keep all the pieces.

Calendar entry format

Two forms of calendar entry are allowed:

  • simple commands that meet most needs, and
  • complex commands for when things get tricky

Simple Commands

Simple Commands have two variants

devicename = setting -- optional comment. Eg. Lamp3=On
and
devicename == setting -- optional comment. Eg Lamp3==On

In the first variant, the one with a single "="-sign, the device state is changed only if it's different from the required state. That is, while Domoticz thinks Lamp3 is already On the command "Lamp3=On" will have no effect.

In the second variant, the command will be sent once a minute whatever the current state of the switch. This might be of value if either:

  • you need to be sure the command got through - useful if signal strength is low or it's a noisy environment, or
  • the switch might have been altered manually such that Domoticz is unaware of the current state.

Complex Commands

Complex Commands are written in LUA but with restrictions (who would have guessed?):

  • first, there is a limit to how long Complex Commands can be (as yet undetermined), and second
  • the Google Calendar facility strips the ","-character from anything you type in the title field

To mitigate to some extent the first restriction, the script contains aliases for some of Domoticzs' array names, namely:

  • cA for commandArray
  • od for otherdevices, and
  • odsv for otherdevices_svalues

Though cryptic, it means that the following command doesn't take so many characters:

for i,v in ipairs({"Light3","Light4"}) do if od[v]~="On" then cA[v]="On" end end

However, remember the second restriction? No ","-characters? To overcome this restriction, you need to replace every "," with a ";". So the Title line you type in to Google Calendar needs to look like this:

for i;v in ipairs({"Light3";"Light4"}) do if od[v]~="On" then cA[v]="On" end end

Yes, I know it would be simpler to have two Calendar entries containing:

Light3 = On, and
Light4 = On

... but where is the fun in that?

Because LUA commands are so powerful, from Version 1.05 they are disabled "out of the box". If you want to use them and understand the issues, you'll need to edit the script and change LUAevents to "true".

Components used

  • Domoticz
  • Google Command Line tool

Prerequisites

  • Raspberry Pi running Debian Wheezy
  • Running Domoticz installation
  • Internet access

Install additional software on your Pi

Google CL

Google Cl is a command line interface to many of their tools.

sudo apt-get install googlecl

A RAM disk

Consider installing a 1Mb RAM disk to prevent wearing your SD card by following the commands in Wiki page Setting up a RAM drive on Raspberry Pi. Because you'll need to make small changes to both the script and the cron entry, it's best to leave it until you've got everything working.

Create a dedicated Google Calendar

From a web browser go to Google Calendar Online and create a Calendar within your main Calendar. In this guide, we use one called Domoticz.

Make sure the Calendar is private. If not, anyone who has access to your Calendar can schedule any event including changing your security panel settings.

Initialize the Google Calendar

Under the user you want to run the script, launch the following command line replacing the string "Domoticz" with your calendar name:

~ $ google calendar today --cal Domoticz
Please specify user: [email protected]
Could not get default browser: could not locate runnable browser
Please log in and/or grant access via your browser at https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token=4%2F-Lmq8AkWmxCakEGdsXwq6Z&hd=default then hit enter.

Now copy the URL to a browser and link it to you account, then come back and hit enter.

Set-up the crontab

With the same user, edit the crontab

~ $ crontab -e 

and add this line to update the listing every 10 minutes:

*/10 * * * * /usr/bin/google calendar today --cal Domoticz > /tmp/gcalt.txt

Create your Google Calendar Lua script

Now create the lua script script_time_calendar.lua in the ~/domoticz/scripts/lua folder.

  --[[ script_time_calendar.lua based on an original idea by epierre
 
 Edits:
   1.01 21/03/14 Allow LUA commands in Calendar entries. Event needs a ( or [ to trigger
   1.02 22/03/14 Use == to specify that a basic action must be forced each minute
                 and = if it should be actioned only if necessary
   1.03 23/03/14 Print the commandArray if LUA events are actioned
   1.04 24/03/14 Error reporting for LUA events. Allow "--" comments in event text
   1.05 Make LUA commands optional.  Change LUAevents to "true" below to enable them
 --]]
 
 id ="(CAL) "
 debug = false
 LUAevents = false
 
 tmpdir = "/tmp/"
 months={Jan=1,Feb=2,Mar=3,Apr=4,May=5,Jun=6,Jul=7,Aug=8,Sep=9,Oct=10,Nov=11,Dec=12}
 
 printf = function(s,...) return print(id..s:format(...)) end
 
 function recode(str) -- a calendar date string to mddhhmm
   m=months[str:sub(1,3)]
   v=tonumber(m..str:sub(5,6)..str:sub(8,9)..str:sub(11,12))
   if debug then printf("recode: %q -> %s",str,v) end
   return v
 end
 
 function trim(s) return (s:gsub("^%s*(.-)%s*$", "%1")) end
 
 function loadstring(str,name) -- omit this function if your domoticz supports it natively
   local file = tmpdir.."loadstring.tmp"
   local f,err=io.open(file,"w")
   if not f then
     printf("Can't open %s (%s)",file,err)
   else
     f:write(str)
     f:close()
     f,err=loadfile(file)
     if f then f,err=f() 
       if debug then printf('f() returns %s and err %s',f,err) end
     end
   end
   return f,err
 end
 
 commandArray = {}
 LUAevent = false -- an LUA event has been processed
 
 now=os.date("*t")
 t=now.month*1000000+now.day*10000+now.hour*100+now.min -- now in mddhhmm format
 
 listing=tmpdir.."gcalt.txt" fhnd,err=io.open(listing)
 if fhnd then
   fhnd:lines() fhnd:lines()
   for line in fhnd:lines() do if debug then printf("%s",line) end
     enil = string.reverse(line) cr = enil:find(",") -- find trailing comma
     if cr then
       cl = #line-cr
       action=line:sub(1,cl) range=line:sub(cl+2) -- split the record
       noitca = string.reverse(action) i,j=noitca:find('--',1,true) -- commented ?
       if i then action=action:sub(1,#action-j) end                 -- if so, remove it
       s=recode(range:sub(1,12)) e=recode(range:sub(16)) -- start & end time
       if debug then printf("Start: %s  End: %s  Now: %s",s,e,t) end
       if (t>=s and (t<=e or s>e)) then -- if this event is current
         if LUAevents and (action:find("%(") or action:find("%[")) then -- non-trivial LUA code
           printf('%s',line)
           s = action:gsub(";",",") -- subst the "," character
           cA=commandArray od=otherdevices odsv=otherdevices_svalues -- abbreviations
           sts,err = loadstring(s)
           if err ~= nil then
             printf('Status: %s   Error: %s',sts,err)
           else
             LUAevent = true
           end
         else
           i,j = action:find("=")  -- is it device = setting
           k,l = action:find("==") -- or device == setting ?
           if i then
             device  = trim(action:sub(1,i-1))
             setting = trim(action:sub(k and l+1 or j+1))
             if k or (otherdevices[device] ~= setting) then
               commandArray[device]=setting
               printf("%s",line)
             end
           end
         end
       end
     end
   end
   fhnd:close()
 else
   printf("Can't open Calendar listing file: %s (%s)",listing,err)
 end
 
 if debug or LUAevent then for i,v in pairs(commandArray) do printf("commandArray[%q] =  %q",i,v) end end
 
 return commandArray