#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# This program is copyright (c) 2016, P. Lutus and is released
# under the GPL (http://www.gnu.org/licenses/gpl-3.0.en.html).
import re, sys, cgi, cgitb, random, math
# enable error display and tracing
cgitb.enable()
print("Content-type:text/html\r\n\r\n")
# try to import Raspberry Pi GPIO library
try:
import RPi.GPIO as G
except:
G = False
# try to import Insteon library extension
try:
import insteon_controller_extension as ice
except:
ice = False
if(not G and not ice):
print('
Error: no GPIO extension library and no Insteon library.
')
quit()
class MyCgiHandler():
def __init__(self):
# this remote control button list
# can be any length <= 25
# and have any desired short names
self.button_list = (
'Lamp A','Lamp B','Lamp C','Lamp D',
'Lamp E','Lamp F','Lamp G','Lamp H',
'Lamp I','Lamp J','Lamp K','Lamp L',
'Lamp M','Lamp N','Lamp O','Lamp P',
)
self.page_refresh_interval = 2 # seconds
form = cgi.FieldStorage()
self.button_tag = 'BTN'
self.all_state = 0
self.button_dic = {k:False for k in self.button_list}
# map outputs to GPIO pins
# this list must be at least
# as long as the button list
self.gpio_list = tuple(range(2,28))
self.gpio_len = len(self.gpio_list)
try:
assert (len(self.button_dic) <= len(self.gpio_list))
except:
print('Error: the button list cannot be longer than the GPIO list.')
quit()
# insteon program extension mode
if(ice):
self.have_scenes = ice.provide_scenes_length() > 0
# replace default state dict with extension's dict
self.mode = ('Devices','Scenes')[self.have_scenes]
for tag in ('SEL','HID','M'): # selection button, hidden tag, meta arg
if(tag in form):
self.mode = form[tag].value
break
if(self.mode == 'Devices'):
self.button_dic = ice.get_device_status()
else:
self.mode == 'Scenes'
self.button_dic = ice.get_scene_status()
# Raspberry Pi GPIO mode
if(G):
# set GPIO mapping mode
G.setmode(G.BCM)
G.setwarnings(False)
# set all channels to output mode
G.setup(self.gpio_list,G.OUT)
# if no extension present to provide states
if(not ice):
# initialize our state list to present GPIO states
for n,k in enumerate(sorted(self.button_dic)):
if(n < self.gpio_len):
state = G.input(self.gpio_list[n]) == 1
else:
state = False
self.button_dic[k] = state
self.head_block = """
Device Controller
"""
self.process_response(form)
self.render_page()
def wrap_tag(self,tag,content='',extras = ''):
if(content): content = '\n' + content
if(extras): extras = ' ' + extras
return '<%s%s>%s%s>\n' % (tag,extras,content,tag)
def render_page(self):
table_cells = []
for n,key in enumerate(sorted(self.button_dic)):
state = self.button_dic[key]
cls = ('off','on')[state]
table_cells.append(self.wrap_tag('input',''
,'type="submit" class="%s" value="%s" name="%s"'
% (cls,key,self.button_tag)))
for tag in ('On','Off'):
table_cells.append(self.wrap_tag('input',''
,'type="submit" value="All %s" name="%s" class="%s"' % (tag,self.button_tag,tag + 'button')))
if(ice and self.have_scenes):
for tag in ('Scenes','Devices'):
table_cells.append(self.wrap_tag('input',''
,'type="submit" value="%s" name="%s" class="%s"' % (tag,'SEL','modebutton')))
table = ''
# roughly equal number of rows and columns
# but favoring wider buttons
row_length = int(math.sqrt(len(table_cells)))
# track generated rows for layout adjustment
rows = 0
while(table_cells):
row = ''
for _ in range(row_length):
if(table_cells):
cell = table_cells.pop(0)
else:
cell = ' '
row += self.wrap_tag('td',cell)
table += self.wrap_tag('tr',row)
rows += 1
page = self.wrap_tag('table',table)
if(ice):
page += self.wrap_tag('input','','type="hidden" value="%s" name="%s"' % (self.mode,'HID'))
page = self.wrap_tag('form',page,'method="post"')
meta_suff = ''
if(ice):
meta_suff = ';url=index.py?M=%s' % self.mode
head = '' % (self.page_refresh_interval,meta_suff)
head += self.head_block % (100.0/rows)
head = self.wrap_tag('head',head)
page = head + self.wrap_tag('body',page)
page = self.wrap_tag('html',page)
print(page)
def exec_state_change(self,key,state,allf = 0.0):
self.button_dic[key] = state
# Raspberry Pi GPIO mode
if(G):
n = (sorted(self.button_dic)).index(key)
if(n < self.gpio_len):
G.output(self.gpio_list[n],state)
# program extension mode
if(ice):
if(allf):
if(allf != self.all_state):
# emit this command just once
self.all_state = allf
ice.exec_com('All',state)
else:
ice.exec_com(key,state)
if(verbose):
print('%s : %s' % (key,('Off','On')[state]))
# process result of user input
def process_response(self,form):
if(self.button_tag in form):
key = form[self.button_tag].value
if(key == 'All On' or key == 'All Off'):
state = (key == 'All On')
# a scheme to emit the 'All' command only once
allf = random.random()
for key in self.button_dic:
self.exec_state_change(key,state,allf)
else:
# get the state of this button
state = self.button_dic[key]
# flip the state
state = not state
self.exec_state_change(key,state)
if __name__ == "__main__" :
verbose = False
MyCgiHandler()