Skip to content

Commit 4ecaa23

Browse files
author
Paul Jennings
committed
Added Google Calendar interface script.
1 parent edda4ae commit 4ecaa23

File tree

1 file changed

+166
-0
lines changed

1 file changed

+166
-0
lines changed

TStatGcal.py

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
#!/usr/bin/env python
2+
3+
#Copyright (c) 2011, Paul Jennings <pjennings-tstat@pjennings.net>
4+
#All rights reserved.
5+
6+
#Redistribution and use in source and binary forms, with or without
7+
#modification, are permitted provided that the following conditions are met:
8+
#
9+
# * Redistributions of source code must retain the above copyright notice,
10+
# this list of conditions and the following disclaimer.
11+
# * Redistributions in binary form must reproduce the above copyright
12+
# notice, this list of conditions and the following disclaimer in the
13+
# documentation and/or other materials provided with the distribution.
14+
# * The names of its contributors may not be used to endorse or promote
15+
# products derived from this software without specific prior written
16+
# permission.
17+
18+
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
#AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
#IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
#ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22+
#LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
#CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
#SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
#INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
#CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
#ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28+
#THE POSSIBILITY OF SUCH DAMAGE.
29+
30+
VERSION = 1.0
31+
32+
# Minimum and maximum values for heat and cool
33+
# The script will never set values outside of this range
34+
HEAT_MIN = 55
35+
HEAT_MAX = 80
36+
COOL_MIN = 70
37+
COOL_MAX = 100
38+
39+
# Valid commands
40+
# Remove commands that you don't want the script to execute here
41+
# mode in particular can be dangerous, because someone could create
42+
# a 'mode off' command and turn your heat off in the winter.
43+
COMMANDS = ['Heat', 'Cool', 'Mode', 'Fan']
44+
45+
try:
46+
from xml.etree import ElementTree # for Python 2.5 users
47+
except ImportError:
48+
from elementtree import ElementTree
49+
import gdata.calendar.service
50+
import gdata.service
51+
import atom.service
52+
import gdata.calendar
53+
54+
import atom
55+
import datetime
56+
import getopt
57+
import os
58+
import sys
59+
import string
60+
import time
61+
62+
import TStat
63+
64+
def main(tstatAddr, username=None, password=None, calName="Thermostat"):
65+
# Connect to thermostat
66+
tstat = TStat.TStat(tstatAddr)
67+
68+
# Log in to Google
69+
calendar_service = gdata.calendar.service.CalendarService()
70+
calendar_service.email = username
71+
calendar_service.password = password
72+
calendar_service.source = "TStatGCal-%s" % VERSION
73+
calendar_service.ProgrammaticLogin()
74+
75+
# Create date range for event search
76+
today = datetime.datetime.today()
77+
gmt = time.gmtime()
78+
gmtDiff = datetime.datetime(gmt[0], gmt[1], gmt[2], gmt[3], gmt[4]) - today
79+
tomorrow = datetime.datetime.today()+datetime.timedelta(days=2)
80+
81+
query = gdata.calendar.service.CalendarEventQuery()
82+
query.start_min = "%04i-%02i-%02i" % (today.year, today.month, today.day)
83+
query.start_max = "%04i-%02i-%02i" % (tomorrow.year, tomorrow.month, tomorrow.day)
84+
85+
# Look for a calendar called calName
86+
feed = calendar_service.GetOwnCalendarsFeed()
87+
for i, a_calendar in enumerate(feed.entry):
88+
if a_calendar.title.text == calName:
89+
query.feed = a_calendar.content.src
90+
91+
if query.feed is None:
92+
print "No calendar with name '%s' found" % calName
93+
return
94+
95+
# Search for the event that has passed but is closest to the current time
96+
closest = None
97+
closestDT = None
98+
closestWhen = None
99+
closestEvent = None
100+
feed = calendar_service.CalendarQuery(query)
101+
for i, an_event in enumerate(feed.entry):
102+
#print '\t%s. %s' % (i, an_event.title.text,)
103+
# Skip events that are not valid commands
104+
(command, value) = an_event.title.text.splitlines()[0].split()
105+
if command not in COMMANDS:
106+
print "Warning: '%s' is not a valid command" % an_event.title.text
107+
continue
108+
try:
109+
float(value)
110+
except:
111+
if value not in ['Off', 'On', 'Auto']:
112+
print "Warning: '%s' is not a valid command" % an_event.title.text
113+
continue
114+
for a_when in an_event.when:
115+
d = a_when.start_time.split("T")[0]
116+
t = a_when.start_time.split("T")[1].split(".")[0]
117+
(year, month, day) = [int(p) for p in d.split("-")]
118+
(hour, min, sec) = [int(p) for p in t.split(":")]
119+
dt = datetime.datetime(year, month, day, hour, min, sec)-gmtDiff
120+
#print "DT:", dt
121+
d = dt-datetime.datetime.today()
122+
#print "d.days:", d.days
123+
124+
# Skip events that are in the future
125+
if d.days >= 0:
126+
continue
127+
128+
if closest is None:
129+
closest = d
130+
closestDT = dt
131+
closestWhen = a_when
132+
closestEvent = an_event
133+
else:
134+
if d.days < closest.days:
135+
continue
136+
if d.seconds > closest.seconds:
137+
closest = d
138+
closestDT = dt
139+
closestWhen = a_when
140+
closestEvent = an_event
141+
142+
if closestEvent is None:
143+
print "No events found"
144+
return
145+
146+
text = closestEvent.title.text
147+
print "Closest event: %s at %s" % (text, closestDT)
148+
(command, value) = text.splitlines()[0].split()
149+
if command == 'Heat':
150+
print "Setting heat to %s" % int(value)
151+
tstat.setHeatPoint(int(value))
152+
elif command == 'Cool':
153+
print "Setting cool to %s" % value
154+
tstat.setCoolPoint(int(value))
155+
elif command == 'Fan':
156+
print "Setting fan to %s" % value
157+
tstat.setFanMode(value)
158+
elif command == 'Mode':
159+
print "Setting mode to %s" % value
160+
tstat.setTstatMode(value)
161+
162+
if __name__ == '__main__':
163+
f = open(os.path.expanduser("~/.google"))
164+
username = f.readline().splitlines()[0]
165+
password = f.readline().splitlines()[0]
166+
main(sys.argv[1], username=username, password=password, calName=sys.argv[2])

0 commit comments

Comments
 (0)