Interacting with Google Calendar
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