This repository has been archived by the owner on Dec 12, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial upload of Hydrogen. Still in development.
- Loading branch information
1 parent
d9c216e
commit 79555a2
Showing
13 changed files
with
236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -99,3 +99,5 @@ ENV/ | |
|
||
# mypy | ||
.mypy_cache/ | ||
|
||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Hydrogen | ||
|
||
#### Description | ||
Hydrogen is a python app that will control your ecobee thermostats to get around the CobbEMC peak hours. Thus saving you money! |
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"apikey": "" | ||
} |
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import json | ||
import requests | ||
import logging | ||
|
||
logging.basicConfig(filename='log/hydrogen.log', level=logging.DEBUG) | ||
|
||
with open('ecobee/data/ecobee_secret.json') as data_file: | ||
data = json.load(data_file) | ||
apikey = data['apikey'] | ||
|
||
with open('ecobee/data/ecobee_authentication.json') as data_file: | ||
data = json.load(data_file) | ||
authcode = data['code'] | ||
|
||
|
||
# Only used to initialize the ecobee application on the developer console (myapp) console on ecobee.com | ||
def get_ecobee_pin(): | ||
url = "https://api.ecobee.com/authorize" | ||
params = {'response_type': 'ecobeePin', 'client_id': apikey, 'scope': 'smartWrite'} | ||
request = requests.get(url, params=params) | ||
with open('ecobee/data/ecobee_authentication.json', 'w') as outfile: | ||
json.dump(request.json(), outfile) | ||
|
||
|
||
# Get initial tokens | ||
def get_tokens(): | ||
url = 'https://api.ecobee.com/token' | ||
params = {'grant_type': 'ecobeePin', 'code': authcode, 'client_id': apikey} | ||
request = requests.post(url, params=params) | ||
if request.status_code == requests.codes.ok: | ||
with open('ecobee/data/ecobee_tokens.json', 'w') as outfile: | ||
json.dump(request.json(), outfile) | ||
else: | ||
print('Error while requesting tokens from ecobee.com.' + ' Status code: ' + str(request.status_code)) | ||
|
||
|
||
# Refresh bad tokens | ||
def refresh_tokens(refresh_token): | ||
url = 'https://api.ecobee.com/token' | ||
params = {'grant_type': 'refresh_token', | ||
'refresh_token': refresh_token, | ||
'client_id': apikey} | ||
request = requests.post(url, params=params) | ||
if request.status_code == requests.codes.ok: | ||
with open('ecobee/data/ecobee_tokens.json', 'w') as outfile: | ||
json.dump(request.json(), outfile) | ||
else: | ||
sys.exit(4) | ||
|
||
|
||
# Get all data for all thermostats | ||
def get_thermostats(): | ||
with open('ecobee/data/ecobee_tokens.json') as data_file: | ||
data = json.load(data_file) | ||
accesstoken = data['access_token'] | ||
refresh_token = data['refresh_token'] | ||
|
||
url = 'https://api.ecobee.com/1/thermostat' | ||
header = {'Content-Type': 'application/json;charset=UTF-8', 'Authorization': 'Bearer ' + accesstoken} | ||
params = {'json': ('{"selection":{"selectionType":"registered",' | ||
'"includeRuntime":"true",' | ||
'"includeSensors":"true",' | ||
'"includeProgram":"true",' | ||
'"includeEquipmentStatus":"true",' | ||
'"includeEvents":"true",' | ||
'"includeSettings":"true"}}')} | ||
request = requests.get(url, headers=header, params=params) | ||
if request.status_code == requests.codes.ok: | ||
authenticated = True | ||
thermostats = request.json()['thermostatList'] | ||
return thermostats | ||
else: | ||
authenticated = False | ||
if refresh_tokens(refresh_token): | ||
return get_thermostats() | ||
else: | ||
return None | ||
|
||
|
||
# Set your HVAC Mode | ||
def set_hvac_mode(index, hvac_mode): | ||
""" possible hvac modes are auto, auxHeatOnly, cool, heat, off | ||
indexes for thermostats are 0 (downstairs) and 1 (upstairs) """ | ||
with open('ecobee/data/ecobee_tokens.json') as data_file: | ||
data = json.load(data_file) | ||
accesstoken = data['access_token'] | ||
thermostats = get_thermostats() | ||
url = 'https://api.ecobee.com/1/thermostat' | ||
header = {'Content-Type': 'application/json;charset=UTF-8', 'Authorization': 'Bearer ' + accesstoken} | ||
params = {'format': 'json'} | ||
body = ('{"selection":{"selectionType":"thermostats","selectionMatch":' | ||
'"' + thermostats[index]['identifier'] + | ||
'"},"thermostat":{"settings":{"hvacMode":"' + hvac_mode + | ||
'"}}}') | ||
request = requests.post(url, headers=header, params=params, data=body) | ||
if request.status_code == requests.codes.ok: | ||
logging.info("Setting Ecobee to " + hvac_mode + " hvac mode on Ecobee index " + str(index)) | ||
return request | ||
else: | ||
logger.warn("Error connecting to Ecobee while attempting to set HVAC mode. Refreshing tokens...") | ||
refresh_tokens() | ||
|
||
|
||
def get_weather(index): | ||
with open('ecobee/data/ecobee_tokens.json') as data_file: | ||
data = json.load(data_file) | ||
accesstoken = data['access_token'] | ||
thermostats = get_thermostats() | ||
url = 'https://api.ecobee.com/1/thermostat' | ||
header = {'Content-Type': 'application/json;charset=UTF-8', 'Authorization': 'Bearer ' + accesstoken} | ||
params = {'format': 'json'} | ||
body = ('{"selection":{"selectionType":"thermostats","selectionMatch":' | ||
'"' + thermostats[index]['identifier'] + | ||
'"},"thermostat":{"weatherforecast":{"temperature":"}}}') | ||
request = requests.post(url, headers=header, params=params, data=body) | ||
print request.json() |
Empty file.
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
from __future__ import print_function | ||
import httplib2 | ||
import os | ||
import logging | ||
import datetime | ||
|
||
from apiclient import discovery | ||
from oauth2client import client | ||
from oauth2client import tools | ||
from oauth2client.file import Storage | ||
|
||
try: | ||
import argparse | ||
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args() | ||
except ImportError: | ||
flags = None | ||
|
||
logging.basicConfig(filename='log/hydrogen.log', level=logging.DEBUG) | ||
|
||
SCOPES = 'https://www.googleapis.com/auth/gmail.readonly' | ||
CLIENT_SECRET_FILE = 'hydrogen/data/client_secret.json' | ||
APPLICATION_NAME = 'Hydrogen' | ||
|
||
|
||
def get_credentials(): | ||
home_dir = os.path.expanduser('~') | ||
credential_dir = os.path.join(home_dir, '.credentials') | ||
if not os.path.exists(credential_dir): | ||
os.makedirs(credential_dir) | ||
credential_path = os.path.join(credential_dir, 'gmail-python-quickstart.json') | ||
|
||
store = Storage(credential_path) | ||
credentials = store.get() | ||
if not credentials or credentials.invalid: | ||
flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES) | ||
flow.user_agent = APPLICATION_NAME | ||
if flags: | ||
credentials = tools.run_flow(flow, store, flags) | ||
else: # Needed only for compatibility with Python 2.6 | ||
credentials = tools.run(flow, store) | ||
logging.info('Storing credentials to ' + credential_path) | ||
return credentials | ||
|
||
|
||
def check_gmail(): | ||
valid='' | ||
credentials = get_credentials() | ||
http = credentials.authorize(httplib2.Http()) | ||
service = discovery.build('gmail', 'v1', http=http) | ||
today = str(datetime.date.today()) | ||
yesterday = str(datetime.date.fromordinal(datetime.date.today().toordinal() - 1)) | ||
todayhours = int(datetime.datetime.now().strftime("%H")) #Hour of the day in 24 hour format | ||
|
||
if todayhours not in range(14, 19): #Check if it is peak hours for CobbEMC | ||
return 2 | ||
|
||
#Check Gmail messages with label 41(CobbEMC Peak Hours) | ||
messages = service.users().messages().list(userId='me', labelIds='Label_41').execute().get('messages', []) | ||
for message in messages: | ||
tdata = service.users().messages().get(userId='me', id=message['id']).execute() | ||
epochtime = str(tdata['internalDate']) | ||
emaildate = str(datetime.datetime.fromtimestamp(float(epochtime.replace(' ', '')[:-3].upper())).strftime("%Y-%m-%d")) | ||
if emaildate == yesterday: | ||
valid = 1 | ||
break | ||
|
||
if valid == 1: | ||
logging.info("Ecobee's need to be turned off to avoid peak hours that CobbEMC has set for " + today + " from 2 P.M. EST to 7 P.M. EST") | ||
return 1 | ||
else: | ||
return 0 | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import sys | ||
import logging | ||
import requests | ||
import datetime | ||
|
||
|
||
from ecobee import ecobee | ||
from hydrogen import hydrogen | ||
|
||
logging.basicConfig(filename='log/hydrogen.log', level=logging.DEBUG) | ||
now = datetime.datetime.now() | ||
logging.info("Hydrogen ran at " + str(now)) | ||
|
||
# Set your ecobee index list | ||
thermostatlist = [0, 1] | ||
|
||
|
||
def main(): | ||
""" Result of 1 means that the ecobees need to be turned off to avoid peak hours """ | ||
result = hydrogen.check_gmail() | ||
if result == 1: | ||
logging.info("Gmail check has come back with peak hours, setting ecobees to off hvac mode") | ||
for i in thermostatlist: | ||
requesthvacset = ecobee.set_hvac_mode(i, 'off') | ||
if requesthvacset.status_code == requests.codes.ok: | ||
logging.info("Successfully set thermostat index " + str(i) + " to off hvac mode") | ||
elif result == 2: | ||
logging.info("It's not peak hours right now, setting ecobees to cool") | ||
for i in thermostatlist: | ||
requesthvacset = ecobee.set_hvac_mode(i, 'cool') | ||
if requesthvacset.status_code == requests.codes.ok: | ||
logging.info("Successfully set thermostat index " + str(i) + " to cool hvac mode") | ||
elif result == 0: | ||
logging.info("No Peak Hours have been set by CobbEMC, we're good!") | ||
sys.exit(0) | ||
|
||
if __name__ == '__main__': | ||
main() |