Skip to content

Commit

Permalink
v2.0 deployed code
Browse files Browse the repository at this point in the history
New approach to debouncing, pylint changes, and a little more documentation.
  • Loading branch information
Concours99 committed Mar 25, 2019
1 parent 0d255d5 commit 0388305
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 237 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,26 @@
# Alarm-Thermostat controller
#
# Copyright @ 2019, Wayne Geiser. All Rights Reserved.
#
# Contact the author via email: [email protected]

This python application sets a Radio Thermostat WiFi thermostat back when pin
#10 on the Raspberry PI goes high and returns it to the currently running
program when the same pin goes low.

I have this connected to a relay in my alarm system box. When the alarm system
is armed, the relay closes and the Raspberry pi pin goes high. When the alarm
system is disarmed, the relay opens and the Raspberry pi pin goes low.

One issue that I needed to code around:

When the alarm is tripped, the relay flashes off and on (the same as the
red light on the alarm keypad).

Obviously, I didn't want the Raspberry pi to be continually altering the
thermostat settings while this was happening, so you can see in the code where
I simply count trips to the callback routine and then make sure that things
aren't changing before I call the routines to actually do the work.

You will need to insert your own thermostat name in the TSTAT_IP variable to
customize this to your environment.
200 changes: 118 additions & 82 deletions alarm_tstat.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
""" alarm_tstat: Python app to alter RadioThermostat settings on alarm arm/disarm. """
################################################################################
#
# alarm-tstat.py - Control Radio Thermostat when a switch (i.e., alarm relay) is
# alarm_tstat.py - Control Radio Thermostat when a switch (i.e., alarm relay) is
# armed/disarmed
#
# Copyright (C) 2019, Wayne Geiser. All Rights Reserved.
Expand All @@ -9,90 +10,125 @@
# You have no rights to any of this code without expressed permission.
#
################################################################################
from time import sleep
from time import time
import RPi.GPIO as GPIO # pylint: disable=E0401
from wg_helper import wg_trace_print
from wg_radio_thermostat import HOLD_DISABLED
from wg_radio_thermostat import HOLD_ENABLED
from wg_radio_thermostat import NIGHTLIGHT_OFF
from wg_radio_thermostat import NIGHTLIGHT_ON
from wg_radio_thermostat import RADTHERM_FLOAT_ERROR
from wg_radio_thermostat import RADTHERM_INT_ERROR
from wg_radio_thermostat import radtherm_get_int
from wg_radio_thermostat import radtherm_get_todays_lowest_setting
from wg_radio_thermostat import radtherm_set_float
from wg_radio_thermostat import radtherm_set_int
from wg_radio_thermostat import SAVE_ENERGY_MODE_DISABLE
from wg_radio_thermostat import SAVE_ENERGY_MODE_ENABLE
from wg_radio_thermostat import TMODE_HEAT

__version__ = "v1.01"
__version__ = "v2.0"
TRACE = False

RELAY_PIN = 10
DEBOUNCE_SECONDS = 7.0
NUM_CALLBACKS_SINCE_HANDLED = 0

from WGHelper import *
from WGRadioThermostat import *
import time
import RPi.GPIO as GPIO # Import Raspberry Pi GPIO library

def alarm_callback(channel) :
global previous
global armed
# see if we're bouncing
current = time.time()
if (current - previous) < 7.0 :
WGTracePrint("Bouncing? Seconds since last call = " + str(current - previous))
previous = current

def alarm_callback(channel): # pylint: disable=W0613
""" Callback for GPIO pin changing from high to low or vice bersa. """
global NUM_CALLBACKS_SINCE_HANDLED # pylint: disable=W0603
NUM_CALLBACKS_SINCE_HANDLED = NUM_CALLBACKS_SINCE_HANDLED + 1


def setback_tstat():
""" Set the thermostat to the lowest temp setting on today's prog. """
setback_temp = radtherm_get_todays_lowest_setting(TRACE)
if setback_temp != RADTHERM_FLOAT_ERROR:
wg_trace_print("Setting target temp to " + str(setback_temp), TRACE)
# set the temporary temperature to the value we found, above
floatret = radtherm_set_float("t_heat", setback_temp, TRACE)
if floatret == RADTHERM_FLOAT_ERROR:
wg_error_print("setback_tstat", "Error setting t_heat")
return
# set the t-stat to hold
intret = radtherm_set_int("hold", HOLD_ENABLED, TRACE)
if intret == RADTHERM_INT_ERROR:
wg_error_print("setback_tstat", "Error setting hold")
return
# turn the night light off
intret = radtherm_set_int("intensity", NIGHTLIGHT_OFF, TRACE)
if intret == RADTHERM_INT_ERROR:
wg_error_print("setback_tstat", "Error setting intensity")
return


def run_tstat():
""" Run the current thermostat prog. """
# turn the night light on
intret = radtherm_set_int("intensity", NIGHTLIGHT_ON, TRACE)
if intret == RADTHERM_INT_ERROR:
wg_error_print("run_tstat", "Error setting intensity")
return
# disable hold
intret = radtherm_set_int("hold", HOLD_DISABLED, TRACE)
if intret == RADTHERM_INT_ERROR:
wg_trace_print("Error setting hold", TRACE)
return
# set the tstat to SAVE_ENERGY_MODE
intret = radtherm_set_int("mode", SAVE_ENERGY_MODE_ENABLE, TRACE)
if intret == RADTHERM_INT_ERROR:
wg_error_print("run_tstat", "Error enabling save energy mode")
return
# turn off SAVE_ENERGY_MODE
intret = radtherm_set_int("mode", SAVE_ENERGY_MODE_DISABLE, TRACE)
if intret == RADTHERM_INT_ERROR:
wg_error_print("run_tstat", "Error disabling save energy mode")
return

time.sleep(0.25) # needed to let things settle
if RadThermGetInt("tmode", TRACE) == TMODE_HEAT : # heat mode
# if the pin goes high
if GPIO.input(RELAY_PIN) :
WGTracePrint("System armed! Seconds since last call = " + str(current - previous))
# set the thermostat back
setback_temp = RadThermGetTodaysLowestSetting(TRACE)
if (setback_temp != RadTherm_float_ERROR) :
WGTracePrint("Setting target temp to " + str(setback_temp))
# set the temporary temperature to the value we found, above
floatret = RadThermSetFloat("t_heat", setback_temp, TRACE)
if floatret == RadTherm_float_ERROR :
return
# set the t-stat to hold
intret = RadThermSetInt("hold", HOLD_ENABLED, TRACE)
if intret == RadTherm_int_ERROR :
return
# turn the night light off
intret = RadThermSetInt("intensity", NIGHTLIGHT_OFF, TRACE)
if intret == RadTherm_int_ERROR :
return
armed = True
else : # the pin is low
if armed :
WGTracePrint("System disarmed! Seconds since last call = " + str(current - previous))
# turn the night light on
intret = RadThermSetInt("intensity", NIGHTLIGHT_ON, TRACE)
if intret == RadTherm_int_ERROR :
return
# disable hold
intret = RadThermSetInt("hold", HOLD_DISABLED, TRACE)
if intret == RadTherm_int_ERROR :
return
# set the tstat to SAVE_ENERGY_MODE
intret = RadThermSetInt("mode", SAVE_ENERGY_MODE_ENABLE, TRACE)
if intret == RadTherm_int_ERROR :
return
# turn off SAVE_ENERGY_MODE
intret = RadThermSetInt("mode", SAVE_ENERGY_MODE_DISABLE, TRACE)
if intret == RadTherm_int_ERROR :
return
# !!! Now should be running current program !!!
armed = False
else : # already disarmed
WGTracePrint("System already disarmed, ignoring! Seconds since last call = " + str(current - previous))
previous = current
else : # Don't worry about doing anything in the non-heating season
WGTracePrint("Non-heating season, ignoring! Seconds since last call = " + str(current - previous))
previous = current

GPIO.setwarnings(False) # Ignore warning for now
GPIO.setmode(GPIO.BOARD) # Use physical pin numbering
# Set relay pin to be an input pin
GPIO.setup(RELAY_PIN, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)

GPIO.add_event_detect(RELAY_PIN, GPIO.BOTH, callback = alarm_callback,
bouncetime = 500)

running = True
previous = time.time()
armed = False

WGTracePrint("Alarm/Tstat controller started. Version: " + __version__)
while (running) :
time.sleep(1)
# GPIO.cleanup() # clean up after yourself
# !!! Now should be running current program !!!


def main():
""" alarm_tstat main code. """
global NUM_CALLBACKS_SINCE_HANDLED # pylint: disable=W0603

GPIO.setwarnings(False) # Ignore warning for now
GPIO.setmode(GPIO.BOARD) # Use physical pin numbering
# Set relay pin to be an input pin
GPIO.setup(RELAY_PIN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

GPIO.add_event_detect(RELAY_PIN, GPIO.BOTH, callback=alarm_callback,
bouncetime=500)

armed = False
running = True
previous = time()

wg_trace_print("Alarm/Tstat controller started. Version: " + __version__, True)
while running:
sleep(1)
current = time()
if (current - previous) >= DEBOUNCE_SECONDS:
if NUM_CALLBACKS_SINCE_HANDLED > 1: # we've had several events wait
NUM_CALLBACKS_SINCE_HANDLED = 0 # wait until things have calmed
# down
else:
# if the pin goes high
if GPIO.input(RELAY_PIN) and not armed:
if radtherm_get_int("tmode", TRACE) == TMODE_HEAT: # heat mode
wg_trace_print("System armed!", True)
# set the thermostat back
setback_tstat()
armed = True
elif not GPIO.input(RELAY_PIN) and armed:
if radtherm_get_int("tmode", TRACE) == TMODE_HEAT: # heat mode
wg_trace_print("System disarmed!", True)
# run current program
run_tstat()
armed = False
previous = current

GPIO.cleanup() # clean up after yourself

main()
35 changes: 23 additions & 12 deletions wg_helper.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
""" Helper routines I find useful for most of my python code"""
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (C) 2018, Wayne Geiser ([email protected]). All Rights Reserved
Expand All @@ -6,27 +7,37 @@
#
# All functions, variables, etc. should start with "WG" so as not to interfere with other
# packages.

WGHelper_version = "1.0"

import datetime
import pprint

WG_HELPER_VERSION = "2.01"

###############################################################################
#
# Print out a trace message and flush the buffers.
#
def WGTracePrint(message) :
today = datetime.datetime.now()
outstring = (today.strftime("%x %X")
+ " - "
+ message)
print(outstring, flush=True)
def wg_trace_print(message, trace):
""" Print out a tracing message."""
if trace:
today = datetime.datetime.now()
outstring = (today.strftime("%x %X") + " - " + message)
print(outstring, flush=True)

###############################################################################
#
# Print out an error message and flush the buffers.
#
def WGErrorPrint(where, message) :
def wg_error_print(where, message):
"""Print out an error message."""
outstring = "Error in " + where + "! " + message
WGTracePrint(outstring)
wg_trace_print(outstring, True)

###############################################################################
#
# Print out a structure if we're tracing
#
def wg_trace_pprint(struct, trace):
"""Nicely print out a structure if we're tracing"""
if trace:
pprt = pprint.PrettyPrinter(indent=4)
pprt.pprint(struct)
Loading

0 comments on commit 0388305

Please sign in to comment.