#!/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). # this program must have access to a subdirectory # named "http_cache", world writable import os, sys, re, fcntl import urllib.request, base64 import xml.etree.ElementTree as ET try: import http_cache.scenes_devices as ext except: ext = False verbose = False # set this to the network name or address of your Insteon controller topurl = 'http://pl-isy99' # create this value with: '$ echo "user:password" | base64' user_pass_base64 = 'YWRtaW46YWRtaW4=' # admin:admin dict_failed = {} dict_dir = 'http_cache' dict_path = os.path.join(dict_dir,'scenes_devices.py') def fetch_content(suff,need_response = False): global topurl url = topurl + suff url = re.sub(' ','%20',url) request = urllib.request.Request(url) request.add_header("Authorization", "Basic %s" % user_pass_base64) response = '' try: response = urllib.request.urlopen(request).read() if(need_response): response = ET.XML(response) except: None return response def fetch_conditional(node,tag): try: v = node.find(tag).text except: v = None return v def parse_nodes(nodes,memberf = False): result = {} for node in nodes: link = None members = False name = fetch_conditional(node,'name') address =fetch_conditional(node,'address') typ = fetch_conditional(node,'type') if(memberf): members = node.find('members') if(members): link = members.find('link').text if(name != None): if(link != None): if(name == 'Devices'): result[name] = '0.0.0.0' else: result[name] = link else: if(typ == None or (typ != '0.5.0.0' and typ != '16.1.65.0')): result[name] = address return result def provide_devices(): nodes = fetch_content('/rest/nodes/devices',True) return parse_nodes(nodes) def provide_scenes(): nodes = fetch_content('/rest/nodes/scenes',True) result = parse_nodes(nodes) return result def provide_scenes_length(): return len(ext.dict_scenes) def read_file_with_locking(path): data = False if(os.path.isfile(path)): with open(path) as f: try: fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) data = f.read() finally: fcntl.flock(f, fcntl.LOCK_UN | fcntl.LOCK_NB) return data def write_file_with_locking(path,data): outcome = False with open(path,'w') as f: try: fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) f.write(data) outcome = True finally: fcntl.flock(f, fcntl.LOCK_UN | fcntl.LOCK_NB) return outcome def control_scene(key,state): com = ('DFOF','DFON')[state] ids = ext.dict_scenes[key] suff = '/rest/nodes/%s/cmd/%s' % (ids,com) fetch_content(suff) def toggle_element(key,dic,state): global all_state com = ('DFOF','DFON')[state] if(key == 'All'): control_scene('Devices',state) else: ids = dic[key] suff = '/rest/nodes/%s/cmd/%s' % (ids,com) response = fetch_content(suff) if(verbose): print(response) def parse_status(nodes,dic): global dict_failed result = {} for node in nodes: id = node.attrib['id'] if(id in dic): key = dic[id] nc = node.getchildren() value = nc[0].attrib['value'] result[key] = (value != '0' and value != ' ') return result def get_device_status(): status = fetch_content('/rest/status',True) return parse_status(status,ext.dict_dev_ids) def get_scene_status(): status = fetch_content('/rest/status',True) return parse_status(status,ext.dict_scene_ids_b) def format_dic(name,dic, reverse = False): result = '%s = {\n' % name for key in sorted(dic): if(reverse): result += ' \'%s\' : \'%s\',\n' % (dic[key],key) else: result += ' \'%s\' : \'%s\',\n' % (key,dic[key]) result += '}\n\n' return result def generate_dict_file(): if(not os.access(dict_dir, os.W_OK)): print('

Error: directory %s either not present or not writable.

' % dict_dir) return data = fetch_content('/rest/nodes/devices',True) devices = parse_nodes(data) data = fetch_content('/rest/nodes/scenes',True) scenes = parse_nodes(data) # this contains device addresses instead of scene addresses scenes_b = parse_nodes(data,True) s_devices = format_dic('dict_devices',devices) s_dev_ids = format_dic('dict_dev_ids',devices,True) s_scenes = format_dic('dict_scenes',scenes) s_scene_ids = format_dic('dict_scene_ids',scenes,True) s_scene_ids_b = format_dic('dict_scene_ids_b',scenes_b,True) data = s_devices+s_dev_ids+s_scenes+s_scene_ids+s_scene_ids_b header = '#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n' comment = '# generated by %s\n\n' % os.path.abspath(__file__) content = header+comment+data write_file_with_locking(dict_path,content) def read_dicts(): global ext if(not ext): generate_dict_file() import html_cache.scenes_devices as ext def exec_com(key,state): if(verbose): print('command: "%s" %s' % (key,state)) if(key in ext.dict_scenes): toggle_element(key,ext.dict_scenes,state) else: toggle_element(key,ext.dict_devices,state) funct_dic = { '--scenes' : provide_scenes, '--devices' : provide_devices, '--sstatus' : get_scene_status, '--dstatus' : get_device_status, } read_dicts() if __name__ == "__main__" : args = sys.argv[1:] key = False for arg in args: if(key): exec_com(key,arg.lower() == 'true' or arg == '1') key = False elif(arg in funct_dic): print(funct_dic[arg]()) else: key = arg