#!/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\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()