pax_global_header00006660000000000000000000000064127753435400014524gustar00rootroot0000000000000052 comment=a390f356bc74e132e6c1ca410bf5db983ef03b4e openzwave-controlpanel-0.2a+git20161006.a390f35/000077500000000000000000000000001277534354000206325ustar00rootroot00000000000000openzwave-controlpanel-0.2a+git20161006.a390f35/.gitignore000066400000000000000000000000201277534354000226120ustar00rootroot00000000000000*.o *.xml ozwcp openzwave-controlpanel-0.2a+git20161006.a390f35/LICENSE000066400000000000000000000032061277534354000216400ustar00rootroot00000000000000// OpenZWave Control Panel // // Copyright (c) 2010 Greg Satz // All rights reserved. // // SOFTWARE NOTICE AND LICENSE // This work (including software, documents, or other related items) is being // provided by the copyright holders under the following license. By obtaining, // using and/or copying this work, you (the licensee) agree that you have read, // understood, and will comply with the following terms and conditions: // // Permission to use, copy, and distribute this software and its documentation, // without modification, for any purpose and without fee or royalty is hereby // granted, provided that you include the full text of this NOTICE on ALL // copies of the software and documentation or portions thereof. // // THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS // MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT // LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR // PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE // ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. // // COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR // CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR // DOCUMENTATION. // // The name and trademarks of copyright holders may NOT be used in advertising // or publicity pertaining to the software without specific, written prior // permission. Title to copyright in this software and any associated // documentation will at all times remain with copyright holders. //----------------------------------------------------------------------------- openzwave-controlpanel-0.2a+git20161006.a390f35/Makefile000066400000000000000000000045111277534354000222730ustar00rootroot00000000000000# # Makefile for OpenzWave Control Panel application # Greg Satz # GNU make only .SUFFIXES: .cpp .o .a .s CC := $(CROSS_COMPILE)gcc CXX := $(CROSS_COMPILE)g++ LD := $(CROSS_COMPILE)g++ AR := $(CROSS_COMPILE)ar rc RANLIB := $(CROSS_COMPILE)ranlib DEBUG_CFLAGS := -Wall -Wno-unknown-pragmas -Wno-inline -Wno-format -g -DDEBUG -ggdb -O0 RELEASE_CFLAGS := -Wall -Wno-unknown-pragmas -Werror -Wno-format -O3 -DNDEBUG DEBUG_LDFLAGS := -g # Change for DEBUG or RELEASE CFLAGS := -c $(DEBUG_CFLAGS) LDFLAGS := $(DEBUG_LDFLAGS) OPENZWAVE := ../open-zwave/ LIBMICROHTTPD := -L/usr/local/lib/ -lmicrohttpd INCLUDES := -I $(OPENZWAVE)/cpp/src -I $(OPENZWAVE)/cpp/src/command_classes/ \ -I $(OPENZWAVE)/cpp/src/value_classes/ -I $(OPENZWAVE)/cpp/src/platform/ \ -I $(OPENZWAVE)/cpp/src/platform/unix -I $(OPENZWAVE)/cpp/tinyxml/ \ -I /usr/local/include/ # Remove comment below for gnutls support #GNUTLS := -lgnutls # for Linux uncomment out next three lines LIBZWAVE := $(wildcard $(OPENZWAVE)/*.a) #LIBUSB := -ludev #LIBS := $(LIBZWAVE) $(GNUTLS) $(LIBMICROHTTPD) -pthread $(LIBUSB) -lresolv # for Mac OS X comment out above 2 lines and uncomment next 5 lines #ARCH := -arch i386 -arch x86_64 #CFLAGS += $(ARCH) #LIBZWAVE := $(wildcard $(OPENZWAVE)/cpp/lib/mac/*.a) LIBUSB := -framework IOKit -framework CoreFoundation LIBS := $(LIBZWAVE) $(GNUTLS) $(LIBMICROHTTPD) -pthread $(LIBUSB) $(ARCH) -lresolv %.o : %.cpp $(CXX) $(CFLAGS) $(INCLUDES) -o $@ $< %.o : %.c $(CC) $(CFLAGS) $(INCLUDES) -o $@ $< all: defs ozwcp defs: ifeq ($(LIBZWAVE),) @echo Please edit the Makefile to avoid this error message. @exit 1 endif ozwcp.o: ozwcp.h webserver.h $(OPENZWAVE)/cpp/src/Options.h $(OPENZWAVE)/cpp/src/Manager.h \ $(OPENZWAVE)/cpp/src/Node.h $(OPENZWAVE)/cpp/src/Group.h \ $(OPENZWAVE)/cpp/src/Notification.h $(OPENZWAVE)/cpp/src/platform/Log.h webserver.o: webserver.h ozwcp.h $(OPENZWAVE)/cpp/src/Options.h $(OPENZWAVE)/cpp/src/Manager.h \ $(OPENZWAVE)/cpp/src/Node.h $(OPENZWAVE)/cpp/src/Group.h \ $(OPENZWAVE)/cpp/src/Notification.h $(OPENZWAVE)/cpp/src/platform/Log.h ozwcp: ozwcp.o webserver.o zwavelib.o $(LIBZWAVE) $(LD) -o $@ $(LDFLAGS) ozwcp.o webserver.o zwavelib.o $(LIBS) dist: ozwcp rm -f ozwcp.tar.gz tar -c --exclude=".svn" -hvzf ozwcp.tar.gz ozwcp config/ cp.html cp.js openzwavetinyicon.png README clean: rm -f ozwcp *.o openzwave-controlpanel-0.2a+git20161006.a390f35/README000066400000000000000000000053371277534354000215220ustar00rootroot00000000000000OpenZWave Control Panel v0.2a 8/8/2011 Welcome to a very alpha release of the OpenZWave Control Panel. OVERVIEW This README for the OpenZWave Control Panel will attempt to provide the necessary information to compile, use and develop. The OpenZWave Control Panel (ozwcp for short) is an application built on the OpenZWave library http://code.google.com/p/open-zwave/ that permits users to query, manage and monitor Z-Wave nodes and networks. It provides a web based user interface using AJAX principles. The code is very new, as is the library, so bugs will be present. This tool will allow the basic use of Z-Wave devices as well as help push forward the development of the OpenZWave library. INSTALLATION To install ozwcp, visit http://code.google.com/p/openzwave-control-panel/ and click on the "source" tab. You will need an svn client to check out a copy of the sources. You will need a copy of the latest version of the OpenZWave library at http://code.google.com/p/open-zwave/. Use the "source" tab on that project to get a copy. ozwcp uses GNU's libmicrohttpd library available at http://www.gnu.org/software/libmicrohttpd/. I have set up this library using the --enable-messages configure option for debugging but this is optional. All three of these installations should share live in the same directory (share a common parent). The ozwcp Makefile assumes this. See Makefile for comments about Mac OS X support. Note: Makefile is configured to build on Mac OS X; to build on Linux, you will need to comment out lines under # for Mac OS X comment and uncomment lines under # for Linux uncomment Currently Windows is not supported. It should be possible to port this to the Window's cygwin environment if anyone is interested in pursing that option. OPERATION ozwcp currently runs from the command line and takes two flags: -d for debugging -p port number the web server will listen on Once started, connect to the hostname:postnum that ozwcp is running on using a web broswer and you will get to the user interface. Firefox has been extensively tested. Safari works. Intnernet Explorer doesn't work yet. This is also on the to do list. ozwcp currently must be run from within its distribution directory. It serves files that must be present for it to work properly. You may need to run it as root depending on the port number you use. CAVEATS This is an alpha release at best. It has only ever worked in a single envionment. Please report bugs to the OpenZWave google groups mailing list: http://groups.google.com/group/openzwave. See the file TODO for a list of things on the to do list. NOTE The html/javascript tooltip support came from: http://sixrevisions.com/tutorials/javascript_tutorial/create_lightweight_javascript_tooltip/ openzwave-controlpanel-0.2a+git20161006.a390f35/TODO000066400000000000000000000003251277534354000213220ustar00rootroot00000000000000opwcp to do list: (3/12/2011) Update Makefile to automatically compile on Mac OS X Support various flavors of Internet Explorer Figure out packaging/distribution mechanism Finish testing all controller functions openzwave-controlpanel-0.2a+git20161006.a390f35/cp.html000066400000000000000000000776251277534354000221430ustar00rootroot00000000000000 OpenZWave Control Panel

OpenZWave Control Panel

Controller Interface


Controller Status

Backup Controller

Network
Controller
Functions
Devices
Node Id Basic Type Generic Type Product Name Location Value Last Heard Status






































Log output
                    
openzwave-controlpanel-0.2a+git20161006.a390f35/cp.js000066400000000000000000002164421277534354000216030ustar00rootroot00000000000000var pollhttp; var scenehttp; var topohttp; var stathttp; var atsthttp; var racphttp; var polltmr = null; var pollwait = null; var divcur = new Array(); var divcon = new Array(); var divinfo = new Array(); var nodes = new Array(); var nodename = new Array(); var nodeloc = new Array(); var nodegrp = new Array(); var nodegrpgrp = new Array(); var nodepoll = new Array(); var nodepollpoll = new Array(); var astate = false; var needsave = 0; var nodecount; var nodeid; var sucnodeid; var tt_top = 3; var tt_left = 3; var tt_maxw = 300; var tt_speed = 10; var tt_timer = 20; var tt_endalpha = 95; var tt_alpha = 0; var tt_h = 0; var tt = document.createElement('div'); var t = document.createElement('div'); var c = document.createElement('div'); var b = document.createElement('div'); var ie = document.all ? true : false; var curnode = null; var curscene = null; var scenes = new Array(); var routes = new Array(); var curclassstat = null; var classstats = new Array(); if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari pollhttp = new XMLHttpRequest(); } else { pollhttp = new ActiveXObject("Microsoft.XMLHTTP"); } if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari scenehttp = new XMLHttpRequest(); } else { scenehttp = new ActiveXObject("Microsoft.XMLHTTP"); } if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari topohttp = new XMLHttpRequest(); } else { topohttp = new ActiveXObject("Microsoft.XMLHTTP"); } if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari stathttp = new XMLHttpRequest(); } else { stathttp = new ActiveXObject("Microsoft.XMLHTTP"); } if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari atsthttp = new XMLHttpRequest(); } else { atsthttp = new ActiveXObject("Microsoft.XMLHTTP"); } if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari racphttp = new XMLHttpRequest(); } else { racphttp = new ActiveXObject("Microsoft.XMLHTTP"); } function GetDefaultDevice() { var devhttp; if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari devhttp = new XMLHttpRequest(); } else { devhttp = new ActiveXObject("Microsoft.XMLHTTP"); } devhttp.onreadystatechange = function() { if (devhttp.readyState==4 && devhttp.status==200){ if (devhttp.responseText == 'NULL') document.DevPost.devname.value = ''; else document.DevPost.devname.value = devhttp.responseText; } BED(); } devhttp.open("GET", "currdev", true); if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari devhttp.send(null); } else { // code for IE6, IE5 devhttp.send(); } } function OptionGroup(label, disabled) { var element = document.createElement('optgroup'); if (disabled !== undefined) element.disabled = disabled; if (label !== undefined) element.label = label; return element; } function SaveNode(newid) { var i = newid.substr(4); var c = -1; if (curnode != null) c = curnode.substr(4); document.getElementById('divconfigcur').innerHTML = divcur[i]; document.getElementById('divconfigcon').innerHTML = divcon[i]; document.getElementById('divconfiginfo').innerHTML = divinfo[i]; if (c != -1) { if (i != c) document.getElementById(curnode).className = 'normal'; } else { document.getElementById('configcur').disabled = false; document.getElementById('configcon').disabled = false; document.getElementById('configinfo').disabled = false; } curnode = newid; DoNodeHelp(); UpdateSceneValues(i); $('#devices tr.success').removeClass('success'); $('#' + curnode).addClass('success'); return true; } function ClearNode() { if (curnode != null) { document.getElementById(curnode).className = 'normal'; document.NodePost.nodeops.selectedIndex = 0; document.getElementById('divconfigcur').innerHTML = ''; document.getElementById('divconfigcon').innerHTML = ''; document.getElementById('divconfiginfo').innerHTML = ''; document.getElementById('nodeinfo').style.display = 'none'; document.getElementById('nodecntl').style.display = 'none'; UpdateSceneValues(-1); curnode = null; } return true; } function DisplayNode(n) { return true; } function PollTimeout() { pollhttp.abort(); Poll(); } function Poll() { try { pollhttp.open("GET", 'poll.xml', true); pollhttp.onreadystatechange = PollReply; if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari pollhttp.send(null); } else { // code for IE6, IE5 pollhttp.send(); } pollwait = setTimeout(PollTimeout, 3000); //3 seconds } catch (e) { pollwait = setTimeout(PollTimeout, 3000); //3 seconds } } function PollReply() { var xml; if (pollhttp.readyState == 4 && pollhttp.status == 200) { clearTimeout(pollwait); xml = pollhttp.responseXML; var poll_elems = xml.getElementsByTagName('poll'); if (poll_elems.length > 0) { var changed = false; var poll_elem = poll_elems[0]; if (poll_elem.getAttribute('homeid') != document.getElementById('homeid').value) document.getElementById('homeid').value = poll_elem.getAttribute('homeid'); if (poll_elem.getAttribute('nodecount') != nodecount) { nodecount = poll_elem.getAttribute('nodecount'); document.getElementById('nodecount').value = nodecount; } if (poll_elem.getAttribute('nodeid') != nodeid) { nodeid = poll_elem.getAttribute('nodeid'); } if (poll_elem.getAttribute('sucnodeid') != sucnodeid) { sucnodeid = poll_elem.getAttribute('sucnodeid'); document.getElementById('sucnodeid').value = sucnodeid; } if (poll_elem.getAttribute('cmode') != document.getElementById('cmode').value) document.getElementById('cmode').value = poll_elem.getAttribute('cmode'); if (poll_elem.getAttribute('save') != needsave) { needsave = poll_elem.getAttribute('save'); if (needsave == '1') { document.getElementById('saveinfo').style.display = 'block'; } else { document.getElementById('saveinfo').style.display = 'none'; } } if (poll_elem.getAttribute('noop') == '1') { var testhealreport = document.getElementById('testhealreport'); testhealreport.innerHTML = testhealreport.innerHTML + 'No Operation message completed.
'; } var admin_elem = xml.getElementsByTagName('admin'); var admin_elem = admin_elem[0]; if (admin_elem.getAttribute('active') == 'true') { if (!astate) { document.AdmPost.admgo.style.display = 'none'; document.AdmPost.admcan.style.display = 'inline'; document.AdmPost.adminops.disabled = true; astate = true; } } else if (admin_elem.getAttribute('active') == 'false') { if (astate) { document.AdmPost.admgo.style.display = 'inline'; document.AdmPost.admcan.style.display = 'none'; document.AdmPost.adminops.disabled = false; astate = false; } } if (admin_elem.firstChild != null) { ainfo = document.getElementById('adminfo'); ainfo.innerHTML = admin_elem.firstChild.nodeValue; ainfo.style.display = 'block'; } var update_elem = xml.getElementsByTagName('update'); if (update_elem.length > 0) { var remove = update_elem[0].getAttribute('remove'); if (remove != undefined) { var remnodes = remove.split(','); changed = true; for (var i = 0; i < remnodes.length; i++) { nodes[remnodes[i]] = null; if (curnode == ('node' + remnodes[i])) ClearNode(); } } } var node_elems = xml.getElementsByTagName('node'); changed |= node_elems.length > 0; for (var i = 0; i < node_elems.length; i++) { var node_elem = node_elems[i]; var id = node_elem.getAttribute('id'); nodes[id] = { time: node_elem.getAttribute('time'), btype: node_elem.getAttribute('btype'), id: node_elem.getAttribute('id'), gtype: node_elem.getAttribute('gtype'), manufacturer: node_elem.getAttribute('manufacturer'), product: node_elem.getAttribute('product'), name: node_elem.getAttribute('name'), location: node_elem.getAttribute('location'), listening: node_elem.getAttribute('listening') == 'true', frequent: node_elem.getAttribute('frequent') == 'true', zwaveplus: node_elem.getAttribute('zwaveplus') == 'true', beam: node_elem.getAttribute('beam') == 'true', routing: node_elem.getAttribute('routing') == 'true', security: node_elem.getAttribute('security') == 'true', status: node_elem.getAttribute('status'), values: null, groups: null }; var k = 0; var values_node = node_elem.getElementsByTagName('value'); var id_node = nodes[id]; id_node.values = new Array(); for (var j = 0; j < values_node.length; j++) { var values = values_node[j]; id_node.values[k] = { readonly: values.getAttribute('readonly') == 'true', genre: values.getAttribute('genre'), cclass: values.getAttribute('class'), type: values.getAttribute('type'), instance: values.getAttribute('instance'), index: values.getAttribute('index'), label: values.getAttribute('label'), units: values.getAttribute('units'), polled: values.getAttribute('polled') == true, help: null, value: null }; var help = values.getElementsByTagName('help'); var node_values = id_node.values[k]; if (help.length > 0) node_values.help = help[0].firstChild.nodeValue; else node_values.help = ''; if (node_values.type == 'list') { var items = values.getElementsByTagName('item'); var current = values.getAttribute('current'); node_values.value = new Array(); for (var l = 0; l < items.length; l++) { node_values.value[l] = { item: items[l].firstChild.nodeValue, selected: (current == items[l].firstChild.nodeValue) }; } } else if ( values.firstChild != null) node_values.value = values.firstChild.nodeValue; else node_values.value = '0'; k++; } var groups = node_elem.getElementsByTagName('groups'); id_node.groups = new Array(); groups = groups[0].getElementsByTagName('group'); k = 0; for (var j = 0; j < groups.length; j++) { var group = groups[j]; id_node.groups[k] = { id: group.getAttribute('ind'), max: group.getAttribute('max'), label: group.getAttribute('label'), nodes: null }; if (group.firstChild != null) id_node.groups[k].nodes = group.firstChild.nodeValue.split(','); else id_node.groups[k].nodes = new Array(); k++; } } var log_elems = xml.getElementsByTagName('log'); var log_elem = log_elems[0]; if (log_elems != null && log_elem.getAttribute('size') > 0) { var ta = document.getElementById('logdata'); ta.innerHTML = ta.innerHTML + return2br(log_elem.firstChild.nodeValue); ta.scrollTop = ta.scrollHeight; } if (changed) { var stuff = ''; for (var i = 1; i < nodes.length; i++) { var node = nodes[i]; if (node == null) continue; var dt = new Date(node.time * 1000); var yd = new Date(dt.getDate() - 1); var ts; var ext; var exthelp; if (dt < yd) ts = dt.toLocaleDateString() + ' ' + dt.toLocaleTimeString(); else ts = dt.toLocaleTimeString(); var val = ''; if (node.values.length > 0) for (var j = 0; j < node.values.length; j++) { if (node.values[j].genre != 'user') continue; if (node.values[j].type == 'list') { for (var l = 0; l < node.values[j].value.length; l++) if (!node.values[j].value[l].selected) continue; else val = node.values[j].value[l].item; } else if (node.values[j] != null) { val = node.values[j].value; if (val == 'False') val = 'off'; else if (val == 'True') val = 'on'; } break; } ext = ' '; exthelp = ''; if (node.id == nodeid) { ext += '*'; exthelp += 'controller, '; } if (node.listening) { ext += 'L'; exthelp += 'listening, '; } if (node.frequent) { ext += 'F'; exthelp += 'FLiRS, '; } if (node.beam) { ext += 'B'; exthelp += 'beaming, '; } if (node.routing) { ext += 'R'; exthelp += 'routing, '; } if (node.security) { ext += 'S'; exthelp += 'security, '; } if (node.zwaveplus) { ext += "+"; exthelp += 'ZwavePlus, '; } if (exthelp.length > 0) exthelp = exthelp.substr(0, exthelp.length - 2); stuff += '' + node.id + ext + '' + node.btype + '' + node.gtype + '' + node.manufacturer + ' ' + node.product + '' + node.name + '' + node.location + '' + val + '' + ts + '' + node.status + ''; CreateDivs('user', divcur, i); CreateDivs('config', divcon, i); CreateDivs('system', divinfo, i); CreateName(node.name, i); CreateLocation(node.location, i); CreateGroup(i); CreatePoll(i); } document.getElementById('tbody').innerHTML = stuff; if (curnode != null) SaveNode(curnode); } } polltmr = setTimeout(Poll, 750); } } function BED() { var forms = document.forms; var off = (document.DevPost.devname.value.length == 0) && !document.DevPost.usbb.checked; var info; tt.setAttribute('id', 'tt'); t.setAttribute('id', 'tttop'); c.setAttribute('id', 'ttcont'); b.setAttribute('id', 'ttbot'); tt.appendChild(t); tt.appendChild(c); tt.appendChild(b); document.body.appendChild(tt); tt.style.opacity = 0; tt.style.filter = 'alpha(opacity=0)'; tt.style.display = 'none'; for (var i = 0; i < forms.length; i++) { var form = forms[i]; if (form.name == '') continue; for (var j = 0; j < form.elements.length; j++) { var element = form.elements[j]; if ((element.name == 'initialize') || (element.name == 'devname') || (element.name == 'usbb')) continue; if ((element.tagName == 'BUTTON') || (element.tagName == 'SELECT') || (element.tagName == 'INPUT')) element.disabled = off; else element.disabled = !off; } } document.getElementById('configcur').disabled = off; document.getElementById('configcur').checked = true; document.getElementById('configcon').disabled = off; document.getElementById('configcon').checked = false; document.getElementById('configinfo').disabled = off; document.getElementById('configinfo').checked = false; // document.NetPost.netops.selectedIndex = 0; // document.NetPost.netops.disabled = off; info = document.getElementById('netinfo'); info.style.display = 'none'; document.AdmPost.adminops.selectedIndex = 0; document.AdmPost.adminops.disabled = off; info = document.getElementById('adminfo'); info.style.display = 'none'; info = document.getElementById('admcntl'); info.style.display = 'none'; document.NodePost.nodeops.selectedIndex = 0; document.NodePost.nodeops.disabled = off; info = document.getElementById('nodeinfo'); info.style.display = 'none'; info = document.getElementById('nodecntl'); info.style.display = 'none'; if (off) { document.getElementById('homeid').value = ''; document.getElementById('cmode').value = ''; document.getElementById('nodecount').value = ''; document.getElementById('sucnodeid').value = ''; document.getElementById('saveinfo').style.display = 'none'; document.getElementById('tbody').innerHTML = ''; document.getElementById('divconfigcur').innerHTML = ''; document.getElementById('divconfigcon').innerHTML = ''; document.getElementById('divconfiginfo').innerHTML = ''; document.getElementById('logdata').innerHTML = ''; } if (!off) { Poll(); } else { pollhttp.abort(); clearTimeout(polltmr); clearTimeout(pollwait); } curnode = null; } function ShowToolTip(help, width) { tt.style.display = 'block'; c.innerHTML = help; tt.style.width = width ? width + 'px' : 'auto'; if (!width && ie) { t.style.display = 'none'; b.style.display = 'none'; tt.style.width = tt.offsetWidth; t.style.display = 'block'; b.style.display = 'block'; } if (tt.offsetWidth > tt_maxw) { tt.style.width = tt_maxw + 'px'; } tt_h = parseInt(tt.offsetHeight) + tt_top; clearInterval(tt.timer); tt.timer = setInterval(function() { FadeToolTip(1); }, tt_timer) } function PosToolTip(e) { tt.style.top = ((ie ? event.clientY + document.documentElement.scrollTop : e.pageY) - tt_h) + 'px'; tt.style.left = ((ie ? event.clientX + document.documentElement.scrollLeft : e.pageX) + tt_left) + 'px'; } function FadeToolTip(d) { var a = tt_alpha; if ((a != tt_endalpha && d == 1) || (a != 0 && d == -1)) { var i = tt_speed; if (tt_endalpha - a < tt_speed && d == 1) { i = tt_endalpha - a; } else if (tt_alpha < tt_speed && d == -1) { i = a; } tt_alpha = a + (i * d); tt.style.opacity = tt_alpha * .01; tt.style.filter = 'alpha(opacity=' + tt_alpha + ')'; } else { clearInterval(tt.timer); if (d == -1) { tt.style.display = 'none'; } } } function HideToolTip() { clearInterval(tt.timer); tt.timer = setInterval(function() { FadeToolTip(-1); }, tt_timer); } function DoConfig(id) { if (curnode != null) { var dcur = document.getElementById('divconfigcur'); var dcon = document.getElementById('divconfigcon'); var dinfo = document.getElementById('divconfiginfo'); var node = curnode.substr(4); dcur.innerHTML = divcur[node]; dcon.innerHTML = divcon[node]; dinfo.innerHTML = divinfo[node]; if (id == 'configcur') { dcur.className = ''; dcon.className = 'hide'; dinfo.className = 'hide'; } else if (id == 'configcon') { dcon.className = ''; dcur.className = 'hide'; dinfo.className = 'hide'; } else { dinfo.className = ''; dcur.className = 'hide'; dcon.className = 'hide'; } return true; } else { return false; } } function DoValue(id, convert) { if (curnode != null) { var posthttp; var params; var arg = document.getElementById(id).value; if (typeof convert == 'undefined') { convert = true; } if (convert) { if (arg.toLowerCase() == 'off') arg = 'false'; else if (arg.toLowerCase() == 'on') arg = 'true'; } params = id + '=' + arg; if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari posthttp = new XMLHttpRequest(); } else { posthttp = new ActiveXObject("Microsoft.XMLHTTP"); } posthttp.open('POST', 'valuepost.html', true); posthttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); posthttp.send(params); } return false; } function DoButton(id, pushed) { if (curnode != null) { var posthttp; var params; var arg = document.getElementById(id).value; if (pushed) arg = 'true'; else arg = 'false'; params = id + '=' + arg; if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari posthttp = new XMLHttpRequest(); } else { posthttp = new ActiveXObject("Microsoft.XMLHTTP"); } posthttp.open('POST', 'buttonpost.html', true); posthttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); posthttp.send(params); } return false; } function DoDevUSB() { document.DevPost.devname.disabled = document.DevPost.usbb.checked; return true; } function DoDevPost(fun) { if (document.DevPost.devname.value.length > 0 || document.DevPost.usbb.checked) { var posthttp; var params; params = 'dev=' + document.DevPost.devname.value + '&fn=' + fun + '&usb=' + document.DevPost.usbb.checked; if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari posthttp = new XMLHttpRequest(); } else { posthttp = new ActiveXObject("Microsoft.XMLHTTP"); } posthttp.open('POST', 'devpost.html', true); posthttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); posthttp.send(params); if (fun == 'close') { document.DevPost.devname = ''; document.DevPost.usbb.checked = false; } BED(); } return false; } function DoNetHelp() { var ninfo = document.getElementById('netinfo'); var scencntl = document.getElementById('scencntl'); var topocntl = document.getElementById('topocntl'); var topo = document.getElementById('topo'); var statcntl = document.getElementById('statcntl'); var statnet = document.getElementById('statnet'); var statnode = document.getElementById('statnode'); var statclass = document.getElementById('statclass'); var thcntl = document.getElementById('thcntl'); var testhealreport = document.getElementById('testhealreport'); if (document.NetPost.netops.value == 'scen') { ninfo.innerHTML = 'Scene management and execution.'; ninfo.style.display = 'block'; scencntl.style.display = 'block'; topocntl.style.display = 'none'; topo.style.display = 'none'; statcntl.style.display = 'none'; statnet.style.display = 'none'; statnode.style.display = 'none'; statclass.style.display = 'none'; thcntl.style.display = 'none'; testhealreport.style.display = 'none'; SceneLoad('load'); } else if (document.NetPost.netops.value == 'topo') { ninfo.innerHTML = 'Topology views'; ninfo.style.display = 'block'; scencntl.style.display = 'none'; topocntl.style.display = 'block'; topo.style.display = 'block'; statcntl.style.display = 'none'; statnet.style.display = 'none'; statnode.style.display = 'none'; statclass.style.display = 'none'; thcntl.style.display = 'none'; testhealreport.style.display = 'none'; curscene = null; TopoLoad('load'); } else if (document.NetPost.netops.value == 'stat') { ninfo.innerHTML = 'Statistic views'; ninfo.style.display = 'block'; scencntl.style.display = 'none'; topocntl.style.display = 'none'; topo.style.display = 'none'; statcntl.style.display = 'block'; statnet.style.display = 'block'; statnode.style.display = 'block'; thcntl.style.display = 'none'; testhealreport.style.display = 'none'; curscene = null; StatLoad('load'); } else if (document.NetPost.netops.value == 'test') { ninfo.innerHTML = 'Test & Heal Network'; ninfo.style.display = 'block'; scencntl.style.display = 'none'; topocntl.style.display = 'none'; topo.style.display = 'none'; statcntl.style.display = 'none'; statnet.style.display = 'none'; statnode.style.display = 'none'; statclass.style.display = 'none'; thcntl.style.display = 'block'; testhealreport.style.display = 'block'; curscene = null; } else { ninfo.style.display = 'none'; scencntl.style.display = 'none'; topocntl.style.display = 'none'; topo.style.display = 'none'; statcntl.style.display = 'none'; statnet.style.display = 'none'; statnode.style.display = 'none'; statclass.style.display = 'none'; thcntl.style.display = 'none'; testhealreport.style.display = 'none'; curscene = null; } return true; } function DoAdmPost(can) { var posthttp; var fun; var params; if (can) { fun = 'cancel'; ainfo = document.getElementById('adminfo'); ainfo.innerHTML = 'Cancelling controller function.'; ainfo.style.display = 'block'; } else { fun = document.AdmPost.adminops.value; if (fun == 'choice') return false; } params = 'fun=' + fun; if (fun == 'hnf' || fun == 'remfn' || fun == 'repfn' || fun == 'reqnu' || fun == 'reqnnu' || fun == 'assrr' || fun == 'delarr' || fun == 'reps' || fun == 'addbtn' || fun == 'delbtn' || fun == 'refreshnode') { if (curnode == null) { ainfo = document.getElementById('adminfo'); ainfo.innerHTML = 'Must select a node below for this function.'; ainfo.style.display = 'block'; return false; } params += '&node=' + curnode; } else if (fun == 'snif') { if (curnode == null) params += '&node=node255'; else params += '&node=' + curnode; } if (fun == 'addbtn' || fun == 'delbtn') { if (document.AdmPost.button.value.length == 0) { ainfo = document.getElementById('adminfo'); ainfo.innerHTML = 'Button number is required.'; ainfo.style.display = 'block'; document.AdmPost.button.select(); return false; } params += '&button=' + document.AdmPost.button.value; } if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari posthttp = new XMLHttpRequest(); } else { posthttp = new ActiveXObject("Microsoft.XMLHTTP"); } posthttp.open('POST', 'admpost.html', true); posthttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); posthttp.send(params); if (fun == 'remc' || fun == 'remd') { document.getElementById('divconfigcur').innerHTML = ''; document.getElementById('divconfigcon').innerHTML = ''; document.getElementById('divconfiginfo').innerHTML = ''; curnode = null; } return false; } function DoAdmHelp() { ainfo = document.getElementById('adminfo'); var acntl = document.getElementById('admcntl'); acntl.innerHTML = ''; document.AdmPost.admgo.style.display = 'inline'; if (document.AdmPost.adminops.value == 'addd') { ainfo.innerHTML = 'Add a new device or controller to the Z-Wave network.'; ainfo.style.display = 'block'; } else if (document.AdmPost.adminops.value == 'addds') { ainfo.innerHTML = 'Add a new device or controller to the Z-Wave Network (Secure Option).'; ainfo.style.display = 'block'; } else if (document.AdmPost.adminops.value == 'cprim') { ainfo.innerHTML = 'Create new primary controller in place of dead old controller.'; ainfo.style.display = 'block'; } else if (document.AdmPost.adminops.value == 'rconf') { ainfo.innerHTML = 'Receive configuration from another controller.'; ainfo.style.display = 'block'; } else if (document.AdmPost.adminops.value == 'remd') { ainfo.innerHTML = 'Remove a device or controller from the Z-Wave network.'; ainfo.style.display = 'block'; } else if (document.AdmPost.adminops.value == 'remfn') { ainfo.innerHTML = 'Remove a failed node that is on the controller\'s list of failed nodes.'; ainfo.style.display = 'block'; } else if (document.AdmPost.adminops.value == 'hnf') { ainfo.innerHTML = 'Check whether a node is in the controller\'s failed nodes list.'; ainfo.style.display = 'block'; } else if (document.AdmPost.adminops.value == 'repfn') { ainfo.innerHTML = 'Replace a failed device with a working device.'; ainfo.style.display = 'block'; } else if (document.AdmPost.adminops.value == 'tranpr') { ainfo.innerHTML = 'Transfer primary to a new controller and make current secondary.'; ainfo.style.display = 'block'; } else if (document.AdmPost.adminops.value == 'reqnu') { ainfo.style.display = 'block'; ainfo.innerHTML = 'Update the controller with network information from the SUC/SIS.'; } else if (document.AdmPost.adminops.value == 'reqnnu') { ainfo.innerHTML = 'Get a node to rebuild its neighbor list.'; ainfo.style.display = 'block'; } else if (document.AdmPost.adminops.value == 'assrr') { ainfo.innerHTML = 'Assign a network return route to a device.'; ainfo.style.display = 'block'; } else if (document.AdmPost.adminops.value == 'delarr') { ainfo.innerHTML = 'Delete all network return routes from a device.'; ainfo.style.display = 'block'; } else if (document.AdmPost.adminops.value == 'snif') { ainfo.innerHTML = 'Send a node information frame.'; ainfo.style.display = 'block'; } else if (document.AdmPost.adminops.value == 'reps') { ainfo.innerHTML = 'Send information from primary to secondary.'; ainfo.style.display = 'block'; } else if (document.AdmPost.adminops.value == 'addbtn' || document.AdmPost.adminops.value == 'delbtn') { if (curnode == null) { ainfo.innerHTML = 'Must select a node below for this function.'; ainfo.style.display = 'block'; document.AdmPost.adminops.selectedIndex = 0; document.AdmPost.admgo.style.display = 'none'; return false; } acntl.innerHTML = ''; acntl.style.display = 'block'; document.AdmPost.button.select(); if (document.AdmPost.adminops.value == 'addbtn') ainfo.innerHTML = 'Add a button from a handheld.'; else ainfo.innerHTML = 'Remove a button from a handheld.'; ainfo.style.display = 'block'; } else if (document.AdmPost.adminops.value == 'refreshnode') { ainfo.innerHTML = 'Refresh Node Info'; ainfo.style.display = 'block'; } else { ainfo.style.display = 'none'; document.AdmPost.admgo.style.display = 'none'; } return true; } function DoNodePost(val) { var posthttp; var fun; var params; fun = document.NodePost.nodeops.value; if (fun == 'choice') return false; params = 'fun=' + fun + '&node=' + curnode + '&value=' + val; if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari posthttp = new XMLHttpRequest(); } else { posthttp = new ActiveXObject("Microsoft.XMLHTTP"); } posthttp.open('POST', 'nodepost.html', true); posthttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); posthttp.send(params); return false; } function DoNodeHelp() { var ninfo = document.getElementById('nodeinfo'); if (curnode == null) { ninfo.innerHTML = 'Must select a node below for this function.'; ninfo.style.display = 'block'; document.NodePost.nodeops.selectedIndex = 0; return false; } var node = curnode.substr(4); var ncntl = document.getElementById('nodecntl'); if (document.NodePost.nodeops.value == 'nam') { ninfo.innerHTML = 'Naming functions.'; ninfo.style.display = 'block'; ncntl.innerHTML = nodename[node]; ncntl.style.display = 'block'; document.NodePost.name.select(); } else if (document.NodePost.nodeops.value == 'loc') { ninfo.innerHTML = 'Location functions.'; ninfo.style.display = 'block'; ncntl.innerHTML = nodeloc[node]; ncntl.style.display = 'block'; document.NodePost.location.select(); } else if (document.NodePost.nodeops.value == 'grp') { ninfo.innerHTML = 'Group/Association functions'; ninfo.style.display = 'block'; ncntl.innerHTML = nodegrp[node] + nodegrpgrp[node][1]; ncntl.style.display = 'block'; } else if (document.NodePost.nodeops.value == 'pol') { ninfo.innerHTML = 'Polling settings'; ninfo.style.display = 'block'; ncntl.innerHTML = nodepoll[node]; ncntl.style.display = 'block'; DoPoll(); } else { ninfo.style.display = 'none'; ncntl.style.display = 'none'; } return true; } function DoGroup() { var node = curnode.substr(4); var ngrp = document.getElementById('nodegrp'); ngrp.innerHTML = nodegrpgrp[node][document.NodePost.group.value]; return true; } function DoGrpPost() { var posthttp; var params = 'fun=group&node=' + curnode + '&num=' + document.NodePost.group.value + '&groups='; var opts = document.NodePost.groups.options; var i; for (i = 0; i < opts.length; i++) if (opts[i].selected) { params += opts[i].text + ','; } if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari posthttp = new XMLHttpRequest(); } else { posthttp = new ActiveXObject("Microsoft.XMLHTTP"); } posthttp.open('POST', 'grouppost.html', true); posthttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); posthttp.send(params); return false; } function DoPoll() { var node = curnode.substr(4); var npoll = document.getElementById('nodepoll'); var polled = document.getElementById('polled'); if (polled != null) npoll.innerHTML = nodepollpoll[node][polled.value]; return true; } function DoPollPost() { var posthttp; var params = 'fun=poll&node=' + curnode + '&ids='; var opts = document.NodePost.polls.options; var i; for (i = 0; i < opts.length; i++) params += opts[i].id + ','; params += '&poll='; for (i = 0; i < opts.length; i++) if (opts[i].selected) params += '1,'; else params += '0,'; if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari posthttp = new XMLHttpRequest(); } else { posthttp = new ActiveXObject("Microsoft.XMLHTTP"); } posthttp.open('POST', 'pollpost.html', true); posthttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); posthttp.send(params); return false; } function DoSavePost() { var posthttp; var params = 'fun=save'; if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari posthttp = new XMLHttpRequest(); } else { posthttp = new ActiveXObject("Microsoft.XMLHTTP"); } posthttp.open('POST', 'savepost.html', true); posthttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); posthttp.send(params); return false; } function SceneLoad(fun) { var params = 'fun=' + fun; if (fun == 'load') { DisplaySceneSceneValue(null); var scenescenevalues = document.getElementById('scenescenevalues'); while (scenescenevalues.options.length > 0) scenescenevalues.remove(0); } if (fun == 'delete') { if (curscene == null) { alert("Scene not selected"); return false; } DisplaySceneSceneValue(null); params += '&id=' + curscene; var slt = document.getElementById('scenelabeltext'); slt.value = ''; } else if (fun == 'execute') { if (curscene == null) { alert("Scene not selected"); return false; } params += '&id=' + curscene; } else if (fun == 'values') { if (curscene == null) { alert("Scene not selected"); return false; } params += '&id=' + curscene; var slt = document.getElementById('scenelabeltext'); slt.value = scenes[curscene].label; DisplaySceneSceneValue(null); } else if (fun == 'label') { if (curscene == null) { alert("Scene not selected"); return false; } var slt = document.getElementById('scenelabeltext'); if (slt.value.length == 0) { alert('Missing label text'); return false; } params += '&id=' + curscene + '&label=' + slt.value; slt.value = ''; } else if (fun == 'addvalue') { if (curscene == null) { alert("Scene not selected"); return false; } if (curnode == null) { alert('Node not selected'); return false; } var values = document.getElementById('scenevalues'); if (values.options.selectedIndex == -1) { alert('Value not selected'); return false; } var vals = values.options[values.options.selectedIndex].value.split('-'); if (vals[3] != 'list' && vals[3] != 'bool') { var value = document.getElementById('valuetext'); if (value.value.length == 0) { alert('Data not entered'); return false; } params += '&id=' + curscene + '&vid=' + vals[0] + '-' + vals[1] + '-' + vals[2] + '-' + vals[3] + '-' + vals[4] + '-' + vals[5] + '&value=' + value.value; } else { var value = document.getElementById('valueselect'); params += '&id=' + curscene + '&vid=' + vals[0] + '-' + vals[1] + '-' + vals[2] + '-' + vals[3] + '-' + vals[4] + '-' + vals[5] + '&value=' + value.options[value.selectedIndex].value; } DisplaySceneSceneValue(null); } else if (fun == 'update') { if (curscene == null) { alert("Scene not selected"); return false; } if (curnode == null) { alert('Node not selected'); return false; } var values = document.getElementById('scenescenevalues'); if (values.options.selectedIndex == -1) { alert('Value not selected'); return false; } var vals = values.options[values.options.selectedIndex].value.split('-'); if (vals[3] != 'list' && vals[3] != 'bool') { var value = document.getElementById('scenevaluetext'); if (value.value.length == 0) { alert('Data not entered'); return false; } params += '&id=' + curscene + '&vid=' + vals[0] + '-' + vals[1] + '-' + vals[2] + '-' + vals[3] + '-' + vals[4] + '-' + vals[5] + '&value=' + value.value; } else { var value = document.getElementById('valueselect'); params += '&id=' + curscene + '&vid=' + vals[0] + '-' + vals[1] + '-' + vals[2] + '-' + vals[3] + '-' + vals[4] + '-' + vals[5] + '&value=' + value.options[value.selectedIndex].value; } DisplaySceneSceneValue(null); } else if (fun == 'remove') { if (curscene == null) { alert("Scene not selected"); return false; } var values = document.getElementById('scenescenevalues'); if (values.options.selectedIndex == -1) { alert('Scene value not selected'); return false; } var vals = values.options[values.options.selectedIndex].value.split('-'); params += '&id=' + curscene + '&vid=' + vals[0] + '-' + vals[1] + '-' + vals[2] + '-' + vals[3] + '-' + vals[4] + '-' + vals[5]; DisplaySceneSceneValue(null); } scenehttp.open('POST', 'scenepost.html', true); scenehttp.onreadystatechange = SceneReply; scenehttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); scenehttp.send(params); return false; } function SceneReply() { var xml; var elem; if (scenehttp.readyState == 4 && scenehttp.status == 200) { xml = scenehttp.responseXML; scenes_elem = xml.getElementsByTagName('scenes'); if (scenes_elem.length > 0) { var i; var id; var sceneids = document.getElementById('sceneids'); var scenevalues = document.getElementById('scenevalues'); var scenescenevalues = document.getElementById('scenescenevalues'); var elem = scenes_elem[0]; i = elem.getAttribute('sceneid'); if (i != null) { scenes = new Array(); while (sceneids.options.length > 0) sceneids.remove(0); } i = elem.getAttribute('scenevalue'); if (i != null) { scenes[curscene].values = new Array(); while (scenescenevalues.options.length > 0) scenescenevalues.remove(0); } for (i = 0; i < elem.childNodes.length; i++) { var children = elem.childNodes[i]; if (children.nodeType != 1) continue; if (children.tagName == 'sceneid') { id = children.getAttribute('id'); label = children.getAttribute('label'); scenes[id] = { label: label, values: new Array() }; sceneids.add(new Option('[' + id + '] ' + label, id)); } else if (children.tagName == 'scenevalue') { var value = { home: children.getAttribute('home'), node: children.getAttribute('node'), label: children.getAttribute('label'), units: children.getAttribute('units'), type: children.getAttribute('type'), cclass: children.getAttribute('class'), genre: children.getAttribute('genre'), instance: children.getAttribute('instance'), index: children.getAttribute('index'), value: children.firstChild.nodeValue }; id = children.getAttribute('id'); var node = nodes[value.node]; var val = ['[' + value.node + ']']; if(node) val = val.concat([ node.product + ': ' + value.value ]); var vid = [ value.node, value.cclass, value.genre, value.type, value.instance, value.index ].join('-'); scenescenevalues.add(new Option(val.join(' '), vid)); scenes[id].values.push(value); } } } } } function UpdateSceneValues(c) { var sv = document.getElementById('scenevalues'); var node = nodes[c]; var optgroups = sv.getElementsByTagName('optgroup'); while (sv.options.length > 0) sv.remove(0); for(var i=optgroups.length - 1; i>=0; i--) sv.removeChild(optgroups[i]); if (c == -1) return; var optgroup = OptionGroup('[' + node.id + '] ' + ' ' + node.product); sv.add(optgroup); for (var i = 0; i < node.values.length; i++) { var value = node.values[i]; if (value.genre != 'user') continue; if (value.readonly) continue; if (value.type == 'button') continue; var vid = node.id + '-' + value.cclass + '-user-' + value.type + '-' + value.instance + '-' + value.index; var label = '[' + value.instance + '] ' + value.label + ': ' + value.value; optgroup.appendChild(new Option(label, vid)); } DisplaySceneValue(null); } function DisplaySceneValue(opt) { var vt = document.getElementById('valuetext'); var vs = document.getElementById('valueselect'); var vu = document.getElementById('valueunits'); if (opt == null) { vt.style.display = 'inline'; vs.style.display = 'none'; vt.value = ''; while (vs.options.length > 0) vs.remove(0); vu.innerHTML = ''; return false; } var vals = opt.value.split('-'); for (var j = 0; j < nodes[vals[0]].values.length; j++) { var value = nodes[vals[0]].values[j]; if (value.cclass == vals[1] && value.genre == 'user' && value.type == vals[3] && value.instance == vals[4] && value.index == vals[5]) break; } if (vals[3] == 'list') { vt.style.display = 'none'; vs.style.display = 'inline'; } else if (vals[3] == 'bool') { if (value.value == 'True') { vs.add(new Option('On', 'true', true)); vs.add(new Option('Off', 'false')); } else { vs.add(new Option('Off', 'false', true)); vs.add(new Option('On', 'true')); } vt.style.display = 'none'; vs.style.display = 'inline'; } else { vt.value = value.value; vt.style.display = 'inline'; vs.style.display = 'none'; } vu.innerHTML = value.units; return false; } function DisplaySceneSceneValue(opt) { var vt = document.getElementById('scenevaluetext'); var vs = document.getElementById('scenevalueselect'); var vu = document.getElementById('scenevalueunits'); if (opt == null) { vt.style.display = 'inline'; vs.style.display = 'none'; vt.value = ''; while (vs.options.length > 0) vs.remove(0); vu.innerHTML = ''; return false; } var vals = opt.value.split('-'); for (var j = 0; j < scenes[curscene].values.length; j++) { var value = scenes[curscene].values[j]; if (value.cclass == vals[1] && value.genre == 'user' && value.type == vals[3] && value.instance == vals[4] && value.index == vals[5]) break; } if (vals[3] == 'list') { vt.style.display = 'none'; vs.style.display = 'inline'; } else if (vals[3] == 'bool') { if (value.value == 'True') { vs.add(new Option('On', 'on', true)); vs.add(new Option('Off', 'off')); } else { vs.add(new Option('Off', 'off', true)); vs.add(new Option('On', 'on')); } vt.style.display = 'none'; vs.style.display = 'inline'; } else { vt.value = value.value; vt.style.display = 'inline'; vs.style.display = 'none'; } vu.innerHTML = value.units; return false; } function TopoLoad(fun) { var params = 'fun=' + fun; topohttp.open('POST', 'topopost.html', true); topohttp.onreadystatechange = TopoReply; topohttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); topohttp.send(params); return false; } function TopoReply() { var xml; if (topohttp.readyState == 4 && topohttp.status == 200) { xml = topohttp.responseXML; var elems = xml.getElementsByTagName('topo'); if (elems.length > 0) { var i; var id; var list; var elem = elems[0]; for (i = 0; i < elem.childNodes.length; i++) { var child = elem.childNodes[i]; if (child.nodeType != 1) continue; if (child.tagName == 'node') { id = child.getAttribute('id'); list = child.firstChild.nodeValue; routes[id] = list.split(','); } } var stuff = 'Nodes'; var topohead = document.getElementById('topohead'); var node = nodes[i]; for (i = 1; i < nodes.length; i++) { if (node == null) continue; stuff += '' + i + ''; } stuff += '' topohead.innerHTML = stuff; stuff = ''; for (i = 1; i < nodes.length; i++) { if (node == null) continue; stuff += '' + i + ''; var j, k = 0; for (j = 1; j < nodes.length; j++) { if (nodes[j] == null) continue; var route = routes[i]; if (route != undefined && k < route.length && j == route[k]) { stuff += '*'; k++; } else { stuff += ' '; } } stuff += ''; } var topobody = document.getElementById('topobody'); topobody.innerHTML = stuff; } } } function DisplayStatClass(t, n) { var scb = document.getElementById('statclassbody'); var sn = document.getElementById('statnode'); if (curclassstat != null) { var lastn = curclassstat.id.substr(8); if (n != lastn) { curclassstat.className = 'normal'; } } if (curclassstat == null || t != curclassstat) { curclassstat = t; t.className = 'highlight'; sn.style.width = '72%'; scb.innerHTML = classstats[n]; document.getElementById('statclass').style.display = 'block'; } else { curclassstat = null; t.className = 'normal'; sn.style.width = '100%'; scb.innerHTML = ''; document.getElementById('statclass').style.display = 'none'; } return true; } function StatLoad(fun) { var params = 'fun=' + fun; stathttp.open('POST', 'statpost.html', true); stathttp.onreadystatechange = StatReply; stathttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); stathttp.send(params); return false; } function StatReply() { var xml; var elem; if (stathttp.readyState == 4 && stathttp.status == 200) { xml = stathttp.responseXML; elem = xml.getElementsByTagName('stats'); if (elem.length > 0) { var errors = xml.getElementsByTagName('errors'); var counts = xml.getElementsByTagName('counts'); var infos = xml.getElementsByTagName('info'); var error = errors[0]; var cnt = error.childNodes.length; var count = counts[0]; if (count.childNodes.length > cnt) cnt = count.childNodes.length; var info = infos[0]; if (info.childNodes.length > cnt) cnt = info.childNodes.length; var stuff = ''; var i; for (i = 0; i < cnt; i++) { if (i < error.childNodes.length) if (error.childNodes[i].nodeType != 1) continue; if (i < count.childNodes.length) if (count.childNodes[i].nodeType != 1) continue; if (i < info.childNodes.length) if (info.childNodes[i].nodeType != 1) continue; stuff += ''; if (i < error.childNodes.length) stuff += '' + error.childNodes[i].getAttribute('label') + ': ' + error.childNodes[i].firstChild.nodeValue + ''; else stuff += '  '; if (i < count.childNodes.length) stuff += '' + count.childNodes[i].getAttribute('label') + ': ' + count.childNodes[i].firstChild.nodeValue + ''; else stuff += '  '; if (i < info.childNodes.length) stuff += '' + info.childNodes[i].getAttribute('label') + ': ' + info.childNodes[i].firstChild.nodeValue + ''; else stuff += '  '; stuff += ''; } var statnetbody = document.getElementById('statnetbody'); statnetbody.innerHTML = stuff; var nodes = xml.getElementsByTagName('node'); var stuff = ''; var oldnode = null; if (curclassstat != null) oldnode = curclassstat.id; for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; stuff += '' + node.getAttribute('id') + ''; var nstat = node.getElementsByTagName('nstat'); for (var j = 0; j < nstat.length; j++) { stuff += '' + nstat[j].firstChild.nodeValue + ''; } stuff += ''; var cstuff = ''; var cclass = node.getElementsByTagName('commandclass'); for (var j = 0; j < cclass.length; j++) { cstuff += '' + cclass[j].getAttribute('name') + ''; var cstat = cclass[j].getElementsByTagName('cstat'); for (var k = 0; k < cstat.length; k++) { cstuff += '' + cstat[k].firstChild.nodeValue + ''; } cstuff += ''; } classstats[i] = cstuff; } var statnodebody = document.getElementById('statnodebody'); statnodebody.innerHTML = stuff; } if (oldnode != null) { var scb = document.getElementById('statclassbody'); scb.innerHTML = classstats[oldnode.substr(8)]; curclassstat = document.getElementById(oldnode); curclassstat.className = 'highlight'; } } } function TestHealLoad(fun) { var params = 'fun=' + fun; if (fun == 'test') { var cnt = document.getElementById('testnode'); if (cnt.value.length == 0) { params += '&num=0'; } else { params += '&num=' + cnt.value; } var cnt = document.getElementById('testmcnt'); if (cnt.value.length == 0) { alert('Missing count value'); return false; } params += '&cnt=' + cnt.value; } else if (fun == 'heal') { var cnt = document.getElementById('healnode'); if (cnt.value.length == 0) { params += '&num=0'; } else { params += '&num=' + cnt.value; } var check = document.getElementById('healrrs'); if (check.checked) params += '&healrrs=1'; } atsthttp.open('POST', 'thpost.html', true); atsthttp.onreadystatechange = TestHealReply; atsthttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); atsthttp.send(params); return false; } function TestHealReply() { var xml; var elem; if (atsthttp.readyState == 4 && atsthttp.status == 200) { xml = atsthttp.responseXML; var threport = document.getElementById('testhealreport'); elem = xml.getElementsByTagName('test'); if (elem.length > 0) { threport.innerHTML = ''; } elem = xml.getElementsByTagName('heal'); if (elem.length > 0) { threport.innerHTML = ''; } } } function RequestAllConfig(n) { var params = 'fun=racp&node=' + n; racphttp.open('POST', 'confparmpost.html', true); racphttp.onreadystatechange = PollReply; racphttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); racphttp.send(params); return false; } function RequestAll(n) { var params = 'fun=racp&node=' + n; racphttp.open('POST', 'refreshpost.html', true); racphttp.onreadystatechange = PollReply; racphttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); racphttp.send(params); return false; } function quotestring(s) { return s.replace(/\'/g, ""); } function return2br(dataStr) { return dataStr.replace(/(\r\n|[\r\n])/g, "
"); } function boxsize(field) { if (field.length < 8) return 8; return field.length + 2; } function CreateOnOff(i, j, vid) { var data = ' 0) data += ' onmouseover="ShowToolTip(\'' + quotestring(value.help) + '\',0);" onmouseout="HideToolTip();"'; data += '>' + value.units + ''; return data; } function CreateTextBox(i, j, vid) { var data = ' 0) data += ' onmouseover="ShowToolTip(\'' + quotestring(value.help) + '\',0);" onmouseout="HideToolTip();"'; var trimmed_value = value.value.replace(/(\n\s*$)/, ""); data += '> 0) data += ' onmouseover="ShowToolTip(\'' + quotestring(value.help) + '\',0);" onmouseout="HideToolTip();"'; if (value.readonly) data += ' disabled="true">'; else data += '>'; data += '' + value.units + ''; if (!value.readonly) data += ''; data += ''; return data; } function CreateList(i, j, vid) { var data = ' 0) data += ' onmouseover="ShowToolTip(\'' + quotestring(values.help) + '\',0);" onmouseout="HideToolTip();"'; data += '>' + values.units + ''; return data; } function CreateLabel(i, j, vid) { return '' + nodes[i].values[j].units + ''; } function CreateButton(i, j, vid) { var data = ' 0) data += ' onmouseover="ShowToolTip(\'' + quotestring(nodes[i].values[j].help) + '\',0);" onmouseout="HideToolTip();"'; data += '>'; } function CreateLocation(val, ind) { nodeloc[ind] = ''; } function CreateGroup(ind) { var grp; var i, j, k; if (nodes[ind].groups.length == 0) { nodegrp[ind] = ''; nodegrpgrp[ind] = new Array(); nodegrpgrp[ind][1] = ''; return; } nodegrp[ind] = ''; k = 0; for (j = 1; j < nodes.length; j++) { var node = nodes[j]; if (node == null) continue; // build a list of instances var instances = [String(j)]; for (var l = 0; l < node.values.length; l++) { instances[l + 1] = j + '.' + node.values[l].instance; instances.push(j + '.' + node.values[l].instance); } // make unique instances = instances.filter(function(item, i, ar) { return ar.indexOf(item) === i; }); // only show when we have found multiple instances if (instances.length <= 2) { instances = [String(j)]; } if (nodes[ind].groups[i].nodes != null) while (k < nodes[ind].groups[i].nodes.length && nodes[ind].groups[i].nodes[k] < j) k++; for (var l = 0; l < instances.length; l++) { if (nodes[ind].groups[i].nodes.indexOf(instances[l]) != -1) nodegrpgrp[ind][grp] += ''; else nodegrpgrp[ind][grp] += ''; } } nodegrpgrp[ind][grp] += ''; grp++; } nodegrp[ind] += ''; } function CreatePoll(ind) { var uc = 0; var sc = 0; var node = nodes[ind]; if (node.values != null) for (var i = 0; i < node.values.length; i++) { if (node.values[i].genre == 'user') uc++; if (node.values[i].genre == 'system') sc++; } if (uc > 0 || sc > 0) nodepoll[ind] = '
'; else nodepoll[ind] = ''; nodepollpoll[ind] = new Array(2); CreatePollPoll('user', ind, uc); CreatePollPoll('system', ind, sc); } function CreatePollPoll(genre, ind, cnt) { var ind1; if (genre == 'user') ind1 = 0; else ind1 = 1; var nodepoll = nodepollpoll[ind]; nodepoll[ind1] = ''; } else nodepoll[ind1] = ''; } openzwave-controlpanel-0.2a+git20161006.a390f35/openzwavetinyicon.png000066400000000000000000000017621277534354000251410ustar00rootroot00000000000000‰PNG  IHDR üí£ pHYsÄÄ•+!tEXtSoftwareGraphicConverter (Intel)w‡úwIDATxœ´–çKQÅýW{+j4`WĆ(b‰Š ,`бaˆ]±÷>ùMöEž’÷ÜMt¸vïÛwÏÌ™r®‹|°¹8ûñéI¤µU>–ÈHñö//]ÁÁ’š* 28(ÇÇÿ 0>.))âá!¾¾RX( ¶¶&?~ÈÌŒ¢&$è¯ÒÔ$WWêëÅÓS]îè‹ [4''²·§ëúÚ¶³±¡Ø®® i`}]22Ô/Ž&üƒ}ÈË“ˆ%HcÅÄHi© Éù¹üüé†×„ÏŸýüdeE¥·Wù!”¬,‘ÃC¹¹Q÷÷÷¥§G’’”"~rjv„Ì×|ÿ®§à5G“ÌÝ]ýõöVƒ[ZÒµ½-÷÷úý⢌™Àßš ÕUÍXn®Æ9 MNJY™’E!!ZB<„…I]sr^Àþ¶µéó—/zúæ¦>——+¡¡2<¬IÆk#Û¼†‡ËÑ‘9€‡‰ŽÖƒHéè¨"57ë>õÇfU•ß¼Z°dÂ~ÌÍéArz*AA¥µÁé µ´è¼ÒhÒó"Ä·ZÌ€ì‘+è¦Nx€RJ(Jˆa9ÆêïW<ÒfÀ’¥¿¿|ýjòs‹ww’œ,qqÚÀD"[Fgü?ÀãÓK–Ïδ^i +f%º¯ ÀÒéV–—ÅÍMæç?€ÒLO—êj«§› QÈ-úóé“m¥¥ÉôôûÐÉ %ûÅøCÝÞ€â1$ì•Ñ ß¾iQ3ƒÉ:5¥mxy©›H‹)æÚKu2nù³1*xà•M†UQ‘î0 y¥éDdŠý?¾ÀP2”ÇÝ]µˆ9ȤãWÄ !ðP7æÅÇKf¦9Š˜ÉhFëöõéqÅź¸° Æ†<0¥÷É ¡ ©¦ÐЇfg_OGsà„Ð0Þdgk¬ìÛ™S€­-Õdƒæs~¾TVJE…ää¨vr4?á²q)ª­µyÒÛ8ˆ¨íì¨ã°ÔÞ®—;ü%á,š ¤DUõ‡.EÜtÈAW—¾¾4„‰wxD÷’€gÙ±7òÏä éø Dÿf³³Si†•˜¨!v8Kís%…+®PGza 1w`Ns@¼Ôäâ¦Q*ƵŽ+Ll¬–Sw·æÉ©Y—L‹ö ÿÿeÃý\†±ãIEND®B`‚openzwave-controlpanel-0.2a+git20161006.a390f35/ozwcp.cpp000066400000000000000000000647621277534354000225170ustar00rootroot00000000000000//----------------------------------------------------------------------------- // // ozwcp.cpp // // OpenZWave Control Panel // // Copyright (c) 2010 Greg Satz // All rights reserved. // // SOFTWARE NOTICE AND LICENSE // This work (including software, documents, or other related items) is being // provided by the copyright holders under the following license. By obtaining, // using and/or copying this work, you (the licensee) agree that you have read, // understood, and will comply with the following terms and conditions: // // Permission to use, copy, and distribute this software and its documentation, // without modification, for any purpose and without fee or royalty is hereby // granted, provided that you include the full text of this NOTICE on ALL // copies of the software and documentation or portions thereof. // // THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS // MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT // LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR // PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE // ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. // // COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR // CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR // DOCUMENTATION. // // The name and trademarks of copyright holders may NOT be used in advertising // or publicity pertaining to the software without specific, written prior // permission. Title to copyright in this software and any associated // documentation will at all times remain with copyright holders. //----------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include "Options.h" #include "Manager.h" #include "Node.h" #include "Group.h" #include "Notification.h" #include "Log.h" #include "microhttpd.h" #include "ozwcp.h" #include "webserver.h" using namespace OpenZWave; static Webserver *wserver; pthread_mutex_t nlock = PTHREAD_MUTEX_INITIALIZER; MyNode *nodes[MAX_NODES]; int32 MyNode::nodecount = 0; pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER; bool done = false; bool needsave = false; bool noop = false; uint32 homeId = 0; uint8 nodeId = 0; uint8 SUCnodeId = 0; const char *cmode = ""; int32 debug = false; bool MyNode::nodechanged = false; list MyNode::removed; /* * MyNode::MyNode constructor * Just save the nodes into an array and other initialization. */ MyNode::MyNode (int32 const ind) : type(0) { if (ind < 1 || ind >= MAX_NODES) { Log::Write(LogLevel_Info, "new: bad node value %d, ignoring...", ind); delete this; return; } newGroup(ind); setTime(time(NULL)); setChanged(true); nodes[ind] = this; nodecount++; } /* * MyNode::~MyNode destructor * Remove stored data. */ MyNode::~MyNode () { while (!values.empty()) { MyValue *v = values.back(); values.pop_back(); delete v; } while (!groups.empty()) { MyGroup *g = groups.back(); groups.pop_back(); delete g; } } /* * MyNode::remove * Remove node from array. */ void MyNode::remove (int32 const ind) { if (ind < 1 || ind >= MAX_NODES) { Log::Write(LogLevel_Info, "remove: bad node value %d, ignoring...", ind); return; } if (nodes[ind] != NULL) { addRemoved(ind); delete nodes[ind]; nodes[ind] = NULL; nodecount--; } } /* * compareValue * Function to compare values in the vector for sorting. */ bool compareValue (MyValue *a, MyValue *b) { return (a->getId() < b->getId()); } /* * MyNode::sortValues * Sort the ValueIDs */ void MyNode::sortValues () { sort(values.begin(), values.end(), compareValue); setChanged(true); } /* * MyNode::addValue * Per notifications, add a value to a node. */ void MyNode::addValue (ValueID id) { MyValue *v = new MyValue(id); values.push_back(v); setTime(time(NULL)); setChanged(true); } /* * MyNode::removeValue * Per notification, remove value from node. */ void MyNode::removeValue (ValueID id) { vector::iterator it; bool found = false; for (it = values.begin(); it != values.end(); it++) { if ((*it)->id == id) { delete *it; values.erase(it); found = true; break; } } if (!found) fprintf(stderr, "removeValue not found Home 0x%08x Node %d Genre %s Class %s Instance %d Index %d Type %s\n", id.GetHomeId(), id.GetNodeId(), valueGenreStr(id.GetGenre()), cclassStr(id.GetCommandClassId()), id.GetInstance(), id.GetIndex(), valueTypeStr(id.GetType())); setTime(time(NULL)); setChanged(true); } /* * MyNode::saveValue * Per notification, update value info. Nothing really but update * tracking state. */ void MyNode::saveValue (ValueID id) { setTime(time(NULL)); setChanged(true); } /* * MyNode::newGroup * Get initial group information about a node. */ void MyNode::newGroup (uint8 node) { int n = Manager::Get()->GetNumGroups(homeId, node); for (int i = 1; i <= n; i++) { MyGroup *p = new MyGroup(); p->groupid = i; p->max = Manager::Get()->GetMaxAssociations(homeId, node, i); p->label = Manager::Get()->GetGroupLabel(homeId, node, i); groups.push_back(p); } } /* * MyNode::addGroup * Add group membership based on notification updates. */ void MyNode::addGroup (uint8 node, uint8 g, uint8 n, InstanceAssociation *v) { fprintf(stderr, "addGroup: node %d group %d n %d\n", node, g, n); if (groups.size() == 0) newGroup(node); for (vector::iterator it = groups.begin(); it != groups.end(); ++it) if ((*it)->groupid == g) { (*it)->grouplist.clear(); for (int i = 0; i < n; i++) { char str[32]; if (v[i].m_instance == 0) snprintf( str, 32, "%d", v[i].m_nodeId ); else snprintf( str, 32, "%d.%d", v[i].m_nodeId, v[i].m_instance ); (*it)->grouplist.push_back(str); } setTime(time(NULL)); setChanged(true); return; } fprintf(stderr, "addgroup: node %d group %d not found in list\n", node, g); } /* * MyNode::getGroup * Return group ptr for XML output */ MyGroup *MyNode::getGroup (uint8 i) { for (vector::iterator it = groups.begin(); it != groups.end(); ++it) if ((*it)->groupid == i) return *it; return NULL; } /* * MyNode::updateGroup * Synchronize changes from user and update to network */ void MyNode::updateGroup (uint8 node, uint8 grp, char *glist) { char *p = glist; vector::iterator it; char *np; vector v; uint8 n; uint8 j; fprintf(stderr, "updateGroup: node %d group %d\n", node, grp); for (it = groups.begin(); it != groups.end(); ++it) if ((*it)->groupid == grp) break; if (it == groups.end()) { fprintf(stderr, "updateGroup: node %d group %d not found\n", node, grp); return; } n = 0; while (p != NULL && *p && n < (*it)->max) { np = strsep(&p, ","); v.push_back( np ); n++; } /* Look for nodes in the passed-in argument list, if not present add them */ vector::iterator nit; for (j = 0; j < n; j++) { for (nit = (*it)->grouplist.begin(); nit != (*it)->grouplist.end(); ++nit) if (v[j].compare( *nit ) == 0 ) break; if (nit == (*it)->grouplist.end()) { // not found int nodeId = 0, instance = 0; sscanf(v[j].c_str(),"%d.%d", &nodeId, &instance); Manager::Get()->AddAssociation(homeId, node, grp, nodeId, instance); } } /* Look for nodes in the vector (current list) and those not found in the passed-in list need to be removed */ for (nit = (*it)->grouplist.begin(); nit != (*it)->grouplist.end(); ++nit) { for (j = 0; j < n; j++) if (v[j].compare( *nit ) == 0 ) break; if (j >= n) { int nodeId = 0, instance = 0; sscanf(nit->c_str(),"%d.%d", &nodeId, &instance); Manager::Get()->RemoveAssociation(homeId, node, grp, nodeId, instance); } } } /* * Scan list of values to be added to/removed from poll list */ void MyNode::updatePoll(char *ilist, char *plist) { vector ids; vector polls; MyValue *v; char *p; char *np; p = ilist; while (p != NULL && *p) { np = strsep(&p, ","); ids.push_back(np); } p = plist; while (p != NULL && *p) { np = strsep(&p, ","); polls.push_back(*np == '1' ? true : false); } if (ids.size() != polls.size()) { fprintf(stderr, "updatePoll: size of ids %d not same as size of polls %d\n", ids.size(), polls.size()); return; } vector::iterator it = ids.begin(); vector::iterator pit = polls.begin(); while (it != ids.end() && pit != polls.end()) { v = lookup(*it); if (v == NULL) { fprintf(stderr, "updatePoll: value %s not found\n", *it); continue; } /* if poll requested, see if not on list */ if (*pit) { if (!Manager::Get()->isPolled(v->getId())) if (!Manager::Get()->EnablePoll(v->getId())) fprintf(stderr, "updatePoll: enable polling for %s failed\n", *it); } else { // polling not requested and it is on, turn it off if (Manager::Get()->isPolled(v->getId())) if (!Manager::Get()->DisablePoll(v->getId())) fprintf(stderr, "updatePoll: disable polling for %s failed\n", *it); } ++it; ++pit; } } /* * Parse textualized value representation in the form of: * 2-SWITCH MULTILEVEL-user-byte-1-0 * node-class-genre-type-instance-index */ MyValue *MyNode::lookup (string data) { uint8 node = 0; uint8 cls; uint8 inst; uint8 ind; ValueID::ValueGenre vg; ValueID::ValueType typ; size_t pos1, pos2; string str; node = strtol(data.c_str(), NULL, 10); if (node == 0) return NULL; pos1 = data.find("-", 0); if (pos1 == string::npos) return NULL; pos2 = data.find("-", ++pos1); if (pos2 == string::npos) return NULL; str = data.substr(pos1, pos2 - pos1); cls = cclassNum(str.c_str()); if (cls == 0xFF) return NULL; pos1 = pos2; pos2 = data.find("-", ++pos1); if (pos2 == string::npos) return NULL; str = data.substr(pos1, pos2 - pos1); vg = valueGenreNum(str.c_str()); pos1 = pos2; pos2 = data.find("-", ++pos1); if (pos2 == string::npos) return NULL; str = data.substr(pos1, pos2 - pos1); typ = valueTypeNum(str.c_str()); pos1 = pos2; pos2 = data.find("-", ++pos1); if (pos2 == string::npos) return NULL; str = data.substr(pos1, pos2 - pos1); inst = strtol(str.c_str(), NULL, 10); pos1 = pos2 + 1; str = data.substr(pos1); ind = strtol(str.c_str(), NULL, 10); ValueID id(homeId, node, vg, cls, inst, ind, typ); MyNode *n = nodes[node]; if (n == NULL) return NULL; for (vector::iterator it = n->values.begin(); it != n->values.end(); it++) if ((*it)->id == id) return *it; return NULL; } /* * Returns a count of values */ uint16 MyNode::getValueCount () { return values.size(); } /* * Returns an n'th value */ MyValue *MyNode::getValue (uint16 n) { if (n < values.size()) return values[n]; return NULL; } /* * Mark all nodes as changed */ void MyNode::setAllChanged (bool ch) { nodechanged = ch; int i = 0; int j = 1; while (j <= nodecount && i < MAX_NODES) { if (nodes[i] != NULL) { nodes[i]->setChanged(true); j++; } i++; } } /* * Returns next item on the removed list. */ uint8 MyNode::getRemoved() { if (removed.size() > 0) { uint8 node = removed.front(); removed.pop_front(); return node; } return 0; } //----------------------------------------------------------------------------- // // Callback that is triggered when a value, group or node changes //----------------------------------------------------------------------------- void OnNotification (Notification const* _notification, void* _context) { ValueID id = _notification->GetValueID(); switch (_notification->GetType()) { case Notification::Type_ValueAdded: Log::Write(LogLevel_Info, "Notification: Value Added Home 0x%08x Node %d Genre %s Class %s Instance %d Index %d Type %s", _notification->GetHomeId(), _notification->GetNodeId(), valueGenreStr(id.GetGenre()), cclassStr(id.GetCommandClassId()), id.GetInstance(), id.GetIndex(), valueTypeStr(id.GetType())); pthread_mutex_lock(&nlock); nodes[_notification->GetNodeId()]->addValue(id); nodes[_notification->GetNodeId()]->setTime(time(NULL)); nodes[_notification->GetNodeId()]->setChanged(true); pthread_mutex_unlock(&nlock); break; case Notification::Type_ValueRemoved: Log::Write(LogLevel_Info, "Notification: Value Removed Home 0x%08x Node %d Genre %s Class %s Instance %d Index %d Type %s", _notification->GetHomeId(), _notification->GetNodeId(), valueGenreStr(id.GetGenre()), cclassStr(id.GetCommandClassId()), id.GetInstance(), id.GetIndex(), valueTypeStr(id.GetType())); pthread_mutex_lock(&nlock); nodes[_notification->GetNodeId()]->removeValue(id); nodes[_notification->GetNodeId()]->setTime(time(NULL)); nodes[_notification->GetNodeId()]->setChanged(true); pthread_mutex_unlock(&nlock); break; case Notification::Type_ValueChanged: Log::Write(LogLevel_Info, "Notification: Value Changed Home 0x%08x Node %d Genre %s Class %s Instance %d Index %d Type %s", _notification->GetHomeId(), _notification->GetNodeId(), valueGenreStr(id.GetGenre()), cclassStr(id.GetCommandClassId()), id.GetInstance(), id.GetIndex(), valueTypeStr(id.GetType())); pthread_mutex_lock(&nlock); nodes[_notification->GetNodeId()]->saveValue(id); pthread_mutex_unlock(&nlock); break; case Notification::Type_ValueRefreshed: Log::Write(LogLevel_Info, "Notification: Value Refreshed Home 0x%08x Node %d Genre %s Class %s Instance %d Index %d Type %s", _notification->GetHomeId(), _notification->GetNodeId(), valueGenreStr(id.GetGenre()), cclassStr(id.GetCommandClassId()), id.GetInstance(), id.GetIndex(), valueTypeStr(id.GetType())); pthread_mutex_lock(&nlock); nodes[_notification->GetNodeId()]->setTime(time(NULL)); nodes[_notification->GetNodeId()]->setChanged(true); pthread_mutex_unlock(&nlock); break; case Notification::Type_Group: { Log::Write(LogLevel_Info, "Notification: Group Home 0x%08x Node %d Group %d", _notification->GetHomeId(), _notification->GetNodeId(), _notification->GetGroupIdx()); InstanceAssociation *v = NULL; int8 n = Manager::Get()->GetAssociations(homeId, _notification->GetNodeId(), _notification->GetGroupIdx(), &v); pthread_mutex_lock(&nlock); nodes[_notification->GetNodeId()]->addGroup(_notification->GetNodeId(), _notification->GetGroupIdx(), n, v); pthread_mutex_unlock(&nlock); if (v != NULL) delete [] v; } break; case Notification::Type_NodeNew: Log::Write(LogLevel_Info, "Notification: Node New Home %08x Node %d Genre %s Class %s Instance %d Index %d Type %s", _notification->GetHomeId(), _notification->GetNodeId(), valueGenreStr(id.GetGenre()), cclassStr(id.GetCommandClassId()), id.GetInstance(), id.GetIndex(), valueTypeStr(id.GetType())); pthread_mutex_lock(&glock); needsave = true; pthread_mutex_unlock(&glock); break; case Notification::Type_NodeAdded: Log::Write(LogLevel_Info, "Notification: Node Added Home %08x Node %d Genre %s Class %s Instance %d Index %d Type %s", _notification->GetHomeId(), _notification->GetNodeId(), valueGenreStr(id.GetGenre()), cclassStr(id.GetCommandClassId()), id.GetInstance(), id.GetIndex(), valueTypeStr(id.GetType())); pthread_mutex_lock(&nlock); new MyNode(_notification->GetNodeId()); pthread_mutex_unlock(&nlock); pthread_mutex_lock(&glock); needsave = true; pthread_mutex_unlock(&glock); break; case Notification::Type_NodeRemoved: Log::Write(LogLevel_Info, "Notification: Node Removed Home %08x Node %d Genre %s Class %s Instance %d Index %d Type %s", _notification->GetHomeId(), _notification->GetNodeId(), valueGenreStr(id.GetGenre()), cclassStr(id.GetCommandClassId()), id.GetInstance(), id.GetIndex(), valueTypeStr(id.GetType())); pthread_mutex_lock(&nlock); MyNode::remove(_notification->GetNodeId()); pthread_mutex_unlock(&nlock); pthread_mutex_lock(&glock); needsave = true; pthread_mutex_unlock(&glock); break; case Notification::Type_NodeProtocolInfo: Log::Write(LogLevel_Info, "Notification: Node Protocol Info Home %08x Node %d Genre %s Class %s Instance %d Index %d Type %s", _notification->GetHomeId(), _notification->GetNodeId(), valueGenreStr(id.GetGenre()), cclassStr(id.GetCommandClassId()), id.GetInstance(), id.GetIndex(), valueTypeStr(id.GetType())); pthread_mutex_lock(&nlock); nodes[_notification->GetNodeId()]->saveValue(id); pthread_mutex_unlock(&nlock); pthread_mutex_lock(&glock); needsave = true; pthread_mutex_unlock(&glock); break; case Notification::Type_NodeNaming: Log::Write(LogLevel_Info, "Notification: Node Naming Home %08x Node %d Genre %s Class %s Instance %d Index %d Type %s", _notification->GetHomeId(), _notification->GetNodeId(), valueGenreStr(id.GetGenre()), cclassStr(id.GetCommandClassId()), id.GetInstance(), id.GetIndex(), valueTypeStr(id.GetType())); pthread_mutex_lock(&nlock); nodes[_notification->GetNodeId()]->saveValue(id); pthread_mutex_unlock(&nlock); break; case Notification::Type_NodeEvent: Log::Write(LogLevel_Info, "Notification: Node Event Home %08x Node %d Status %d Genre %s Class %s Instance %d Index %d Type %s", _notification->GetHomeId(), _notification->GetNodeId(), _notification->GetEvent(), valueGenreStr(id.GetGenre()), cclassStr(id.GetCommandClassId()), id.GetInstance(), id.GetIndex(), valueTypeStr(id.GetType())); pthread_mutex_lock(&nlock); nodes[_notification->GetNodeId()]->saveValue(id); pthread_mutex_unlock(&nlock); break; case Notification::Type_PollingDisabled: Log::Write(LogLevel_Info, "Notification: Polling Disabled Home %08x Node %d Genre %s Class %s Instance %d Index %d Type %s", _notification->GetHomeId(), _notification->GetNodeId(), valueGenreStr(id.GetGenre()), cclassStr(id.GetCommandClassId()), id.GetInstance(), id.GetIndex(), valueTypeStr(id.GetType())); //pthread_mutex_lock(&nlock); //nodes[_notification->GetNodeId()]->setPolled(false); //pthread_mutex_unlock(&nlock); break; case Notification::Type_PollingEnabled: Log::Write(LogLevel_Info, "Notification: Polling Enabled Home %08x Node %d Genre %s Class %s Instance %d Index %d Type %s", _notification->GetHomeId(), _notification->GetNodeId(), valueGenreStr(id.GetGenre()), cclassStr(id.GetCommandClassId()), id.GetInstance(), id.GetIndex(), valueTypeStr(id.GetType())); //pthread_mutex_lock(&nlock); //nodes[_notification->GetNodeId()]->setPolled(true); //pthread_mutex_unlock(&nlock); break; case Notification::Type_SceneEvent: Log::Write(LogLevel_Info, "Notification: Scene Event Home %08x Node %d Genre %s Class %s Instance %d Index %d Type %s Scene Id %d", _notification->GetHomeId(), _notification->GetNodeId(), valueGenreStr(id.GetGenre()), cclassStr(id.GetCommandClassId()), id.GetInstance(), id.GetIndex(), valueTypeStr(id.GetType()), _notification->GetSceneId()); break; case Notification::Type_CreateButton: Log::Write(LogLevel_Info, "Notification: Create button Home %08x Node %d Button %d", _notification->GetHomeId(), _notification->GetNodeId(), _notification->GetButtonId()); break; case Notification::Type_DeleteButton: Log::Write(LogLevel_Info, "Notification: Delete button Home %08x Node %d Button %d", _notification->GetHomeId(), _notification->GetNodeId(), _notification->GetButtonId()); break; case Notification::Type_ButtonOn: Log::Write(LogLevel_Info, "Notification: Button On Home %08x Node %d Button %d", _notification->GetHomeId(), _notification->GetNodeId(), _notification->GetButtonId()); break; case Notification::Type_ButtonOff: Log::Write(LogLevel_Info, "Notification: Button Off Home %08x Node %d Button %d", _notification->GetHomeId(), _notification->GetNodeId(), _notification->GetButtonId()); break; case Notification::Type_DriverReady: Log::Write(LogLevel_Info, "Notification: Driver Ready, homeId %08x, nodeId %d", _notification->GetHomeId(), _notification->GetNodeId()); pthread_mutex_lock(&glock); homeId = _notification->GetHomeId(); nodeId = _notification->GetNodeId(); if (Manager::Get()->IsStaticUpdateController(homeId)) { cmode = "SUC"; SUCnodeId = Manager::Get()->GetSUCNodeId(homeId); } else if (Manager::Get()->IsPrimaryController(homeId)) cmode = "Primary"; else cmode = "Slave"; pthread_mutex_unlock(&glock); break; case Notification::Type_DriverFailed: Log::Write(LogLevel_Info, "Notification: Driver Failed, homeId %08x", _notification->GetHomeId()); pthread_mutex_lock(&glock); done = false; needsave = false; homeId = 0; cmode = ""; pthread_mutex_unlock(&glock); pthread_mutex_lock(&nlock); for (int i = 1; i < MAX_NODES; i++) MyNode::remove(i); pthread_mutex_unlock(&nlock); break; case Notification::Type_DriverReset: Log::Write(LogLevel_Info, "Notification: Driver Reset, homeId %08x", _notification->GetHomeId()); pthread_mutex_lock(&glock); done = false; needsave = true; homeId = _notification->GetHomeId(); if (Manager::Get()->IsStaticUpdateController(homeId)) { cmode = "SUC"; SUCnodeId = Manager::Get()->GetSUCNodeId(homeId); } else if (Manager::Get()->IsPrimaryController(homeId)) cmode = "Primary"; else cmode = "Slave"; pthread_mutex_unlock(&glock); pthread_mutex_lock(&nlock); for (int i = 1; i < MAX_NODES; i++) MyNode::remove(i); pthread_mutex_unlock(&nlock); break; case Notification::Type_EssentialNodeQueriesComplete: Log::Write(LogLevel_Info, "Notification: Essential Node %d Queries Complete", _notification->GetNodeId()); pthread_mutex_lock(&nlock); nodes[_notification->GetNodeId()]->setTime(time(NULL)); nodes[_notification->GetNodeId()]->setChanged(true); pthread_mutex_unlock(&nlock); break; case Notification::Type_NodeQueriesComplete: Log::Write(LogLevel_Info, "Notification: Node %d Queries Complete", _notification->GetNodeId()); pthread_mutex_lock(&nlock); nodes[_notification->GetNodeId()]->sortValues(); nodes[_notification->GetNodeId()]->setTime(time(NULL)); nodes[_notification->GetNodeId()]->setChanged(true); pthread_mutex_unlock(&nlock); pthread_mutex_lock(&glock); needsave = true; pthread_mutex_unlock(&glock); break; case Notification::Type_AwakeNodesQueried: Log::Write(LogLevel_Info, "Notification: Awake Nodes Queried"); break; case Notification::Type_AllNodesQueriedSomeDead: Log::Write(LogLevel_Info, "Notification: Awake Nodes Queried Some Dead"); break; case Notification::Type_AllNodesQueried: Log::Write(LogLevel_Info, "Notification: All Nodes Queried"); break; case Notification::Type_Notification: switch (_notification->GetNotification()) { case Notification::Code_MsgComplete: Log::Write(LogLevel_Info, "Notification: Notification home %08x node %d Message Complete", _notification->GetHomeId(), _notification->GetNodeId()); break; case Notification::Code_Timeout: Log::Write(LogLevel_Info, "Notification: Notification home %08x node %d Timeout", _notification->GetHomeId(), _notification->GetNodeId()); break; case Notification::Code_NoOperation: Log::Write(LogLevel_Info, "Notification: Notification home %08x node %d No Operation Message Complete", _notification->GetHomeId(), _notification->GetNodeId()); pthread_mutex_lock(&glock); noop = true; pthread_mutex_unlock(&glock); break; case Notification::Code_Awake: Log::Write(LogLevel_Info, "Notification: Notification home %08x node %d Awake", _notification->GetHomeId(), _notification->GetNodeId()); pthread_mutex_lock(&nlock); nodes[_notification->GetNodeId()]->setTime(time(NULL)); nodes[_notification->GetNodeId()]->setChanged(true); pthread_mutex_unlock(&nlock); break; case Notification::Code_Sleep: Log::Write(LogLevel_Info, "Notification: Notification home %08x node %d Sleep", _notification->GetHomeId(), _notification->GetNodeId()); pthread_mutex_lock(&nlock); nodes[_notification->GetNodeId()]->setTime(time(NULL)); nodes[_notification->GetNodeId()]->setChanged(true); pthread_mutex_unlock(&nlock); break; case Notification::Code_Dead: Log::Write(LogLevel_Info, "Notification: Notification home %08x node %d Dead", _notification->GetHomeId(), _notification->GetNodeId()); pthread_mutex_lock(&nlock); nodes[_notification->GetNodeId()]->setTime(time(NULL)); nodes[_notification->GetNodeId()]->setChanged(true); pthread_mutex_unlock(&nlock); break; default: Log::Write(LogLevel_Info, "Notification: Notification home %08x node %d Unknown %d", _notification->GetHomeId(), _notification->GetNodeId(), _notification->GetNotification()); break; } break; case Notification::Type_ControllerCommand: Log::Write(LogLevel_Info, "Notification: ControllerCommand home %08x Event %d Error %d", _notification->GetHomeId(), _notification->GetEvent(), _notification->GetNotification()); pthread_mutex_lock(&nlock); web_controller_update((OpenZWave::Driver::ControllerState)_notification->GetEvent(), (OpenZWave::Driver::ControllerError)_notification->GetNotification(), (void *)_context); pthread_mutex_unlock(&nlock); break; default: Log::Write(LogLevel_Info, "Notification: type %d home %08x node %d genre %d class %d instance %d index %d type %d", _notification->GetType(), _notification->GetHomeId(), _notification->GetNodeId(), id.GetGenre(), id.GetCommandClassId(), id.GetInstance(), id.GetIndex(), id.GetType()); break; } } //----------------------------------------------------------------------------- //
// Create the driver and then wait //----------------------------------------------------------------------------- int32 main(int32 argc, char* argv[]) { int32 i; extern char *optarg; long webport = DEFAULT_PORT; char *ptr; while ((i = getopt(argc, argv, "dp:")) != EOF) switch (i) { case 'd': debug = 1; break; case 'p': webport = strtol(optarg, &ptr, 10); if (ptr == optarg) goto bad; break; default: bad: fprintf(stderr, "usage: ozwcp [-d] -p \n"); exit(1); } for (i = 0; i < MAX_NODES; i++) nodes[i] = NULL; Options::Create("./config/", "", "--SaveConfiguration=true --DumpTriggerLevel=0"); Options::Get()->Lock(); Manager::Create(); wserver = new Webserver(webport); Manager::Get()->AddWatcher(OnNotification, wserver); while (!wserver->isReady()) { delete wserver; sleep(2); wserver = new Webserver(webport); } while (!done) { // now wait until we are done sleep(1); } delete wserver; Manager::Get()->RemoveWatcher(OnNotification, NULL); Manager::Destroy(); Options::Destroy(); exit(0); } openzwave-controlpanel-0.2a+git20161006.a390f35/ozwcp.h000066400000000000000000000077451277534354000221620ustar00rootroot00000000000000//----------------------------------------------------------------------------- // // ozwcp.h // // OpenZWave Control Panel // // Copyright (c) 2010 Greg Satz // All rights reserved. // // SOFTWARE NOTICE AND LICENSE // This work (including software, documents, or other related items) is being // provided by the copyright holders under the following license. By obtaining, // using and/or copying this work, you (the licensee) agree that you have read, // understood, and will comply with the following terms and conditions: // // Permission to use, copy, and distribute this software and its documentation, // without modification, for any purpose and without fee or royalty is hereby // granted, provided that you include the full text of this NOTICE on ALL // copies of the software and documentation or portions thereof. // // THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS // MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT // LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR // PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE // ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. // // COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR // CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR // DOCUMENTATION. // // The name and trademarks of copyright holders may NOT be used in advertising // or publicity pertaining to the software without specific, written prior // permission. Title to copyright in this software and any associated // documentation will at all times remain with copyright holders. //----------------------------------------------------------------------------- #include #include #include "Driver.h" #include "Notification.h" #include "ValueStore.h" #include "Value.h" #include "ValueBool.h" #include "ValueByte.h" #include "ValueDecimal.h" #include "ValueInt.h" #include "ValueList.h" #include "ValueShort.h" #include "ValueString.h" using namespace OpenZWave; #define MAX_NODES 255 #define DEFAULT_PORT 8090 extern const char *valueGenreStr(ValueID::ValueGenre); extern ValueID::ValueGenre valueGenreNum(char const *); extern const char *valueTypeStr(ValueID::ValueType); extern ValueID::ValueType valueTypeNum(char const *); extern const char *nodeBasicStr(uint8); extern const char *cclassStr(uint8); extern uint8 cclassNum(char const *str); extern const char *controllerErrorStr(Driver::ControllerError err); class MyValue { friend class MyNode; public: MyValue(ValueID id) : id(id) {}; ~MyValue() {}; ValueID getId() { return id; } private: ValueID id; }; typedef struct { uint8 groupid; uint8 max; string label; vector grouplist; } MyGroup; class MyNode { public: MyNode(int32 const); static void remove(int32 const); static int32 getNodeCount() { return nodecount; }; void sortValues(); void addValue(ValueID id); void removeValue(ValueID id); void saveValue(ValueID id); uint16 getValueCount(); static MyValue *lookup(string id); MyValue *getValue(uint16 n); uint32 getTime() { return mtime; } void setTime(uint32 t) { mtime = t; } static bool getAnyChanged() { return nodechanged; } static void setAllChanged(bool ch); bool getChanged() { return changed; } void setChanged(bool ch) { changed = ch; nodechanged = ch; } static void addRemoved(uint8 node) { removed.push_back(node); } static uint32 getRemovedCount() { return removed.size(); } static uint8 getRemoved(); void addGroup(uint8 node, uint8 g, uint8 n, InstanceAssociation *v); MyGroup *getGroup(uint8 i); void updateGroup(uint8 node, uint8 grp, char *glist); uint8 numGroups() { return groups.size(); } void updatePoll(char *ilist, char *plist); private: ~MyNode(); void newGroup(uint8 n); static int32 nodecount; int32 type; uint32 mtime; bool changed; static bool nodechanged; static list removed; vector groups; vector values; }; openzwave-controlpanel-0.2a+git20161006.a390f35/ozwcp.html000066400000000000000000000112161277534354000226630ustar00rootroot00000000000000 OpenZWave Control Panel
OpenZWave Control Panel
Interface














Node Id Type Name Location




















openzwave-controlpanel-0.2a+git20161006.a390f35/test.sh000077500000000000000000000020051277534354000221450ustar00rootroot00000000000000#!/bin/bash #valgrind --track-fds=yes --time-stamp=yes --leak-check=full --show-reachable=yes --track-origins=yes --malloc-fill=01 --free-fill=80 ./ozwcp -d -p 9900 if [ "$1" = "" ]; then echo usage: test.sh host:port exit 1 fi host=http://$1 for (( i = 1 ; i <= 10 ; i = i + 1 )) ; do echo loop $i wget -r -l 1 -O /dev/null -o /dev/null $host sleep 1 wget -O /dev/null -o /dev/null --post-data="fn=open&dev=/dev/cu.SLAB_USBtoUART" $1'/devpost.html?dev=/dev/cu.SLAV_USBtoUART&fn=open' wget -O /dev/null -o /dev/null $host j=20 while [ $j -gt 0 ]; do wget -O /tmp/poll.xml -o /dev/null $1'/poll.xml' if ( test -s /tmp/poll.xml && grep 'log size="0"' /tmp/poll.xml > /dev/null 2>&1 ) ; then let j-- else j=100 fi done wget -O /dev/null -o /dev/null --post-data="fn=close&dev=/dev/cu.SLAB_USBtoUART" $1'/devpost.html?dev=/dev/cu.SLAB_USBtoUART&fn=close' wget -O /dev/null -o /dev/null $host done wget -O /dev/null -o /dev/null --post-data="fn=exit" $1'/devpost.html?dev=&fn=exit' openzwave-controlpanel-0.2a+git20161006.a390f35/testusb.sh000077500000000000000000000017551277534354000226720ustar00rootroot00000000000000#!/bin/bash #valgrind --track-fds=yes --time-stamp=yes --leak-check=full --show-reachable=yes --track-origins=yes --malloc-fill=01 --free-fill=80 ./ozwcp -d -p 9900 if [ "$1" = "" ]; then echo usage: testusb.sh host:port exit 1 fi host=http://$1 for (( i = 1 ; i <= 10 ; i = i + 1 )) ; do echo loop $i wget -r -l 1 -O /dev/null -o /dev/null $host sleep 1 echo open wget -O /dev/null -o /dev/null --post-data="fn=open&usb=true" $1'/devpost.html?dev=&fn=open&usb=true' wget -O /dev/null -o /dev/null $host j=20 while [ $j -gt 0 ]; do wget -O /tmp/poll.xml -o /dev/null $1'/poll.xml' if ( test -s /tmp/poll.xml && grep 'log size="0"' /tmp/poll.xml > /dev/null 2>&1 ) ; then let j-- else j=100 fi done echo close wget -O /dev/null -o /dev/null --post-data="fn=close&usb=true" $1'/devpost.html?dev=&fn=close&usb=true' wget -O /dev/null -o /dev/null $host done echo exit wget -O /dev/null -o /dev/null --post-data="fn=exit" $1'/devpost.html?dev=&fn=exit' openzwave-controlpanel-0.2a+git20161006.a390f35/webserver.cpp000066400000000000000000001567001277534354000233530ustar00rootroot00000000000000// // webserver.cpp -- libmicrohttpd web interface for ozwcp // // Copyright (c) 2010 Greg Satz // All rights reserved. // // SOFTWARE NOTICE AND LICENSE // This work (including software, documents, or other related items) is being // provided by the copyright holders under the following license. By obtaining, // using and/or copying this work, you (the licensee) agree that you have read, // understood, and will comply with the following terms and conditions: // // Permission to use, copy, and distribute this software and its documentation, // without modification, for any purpose and without fee or royalty is hereby // granted, provided that you include the full text of this NOTICE on ALL // copies of the software and documentation or portions thereof. // // THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS // MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT // LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR // PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE // ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. // // COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR // CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR // DOCUMENTATION. // // The name and trademarks of copyright holders may NOT be used in advertising // or publicity pertaining to the software without specific, written prior // permission. Title to copyright in this software and any associated // documentation will at all times remain with copyright holders. //----------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include "Manager.h" #include "Driver.h" #include "Node.h" #include "ValueBool.h" #include "ValueByte.h" #include "ValueDecimal.h" #include "ValueInt.h" #include "ValueList.h" #include "ValueRaw.h" #include "ValueShort.h" #include "ValueString.h" #include "tinyxml.h" #include "microhttpd.h" #include "ozwcp.h" #include "webserver.h" using namespace OpenZWave; #define NBSP(str) (str) == NULL || (*str) == '\0' ? " " : (str) #define BLANK(str) (str) == NULL || (*str) == '\0' ? "" : (str) #define FNF "File not foundFile not found: %s" #define UNKNOWN "NothingnessThere is nothing here. Sorry.\n" #define DEFAULT "" #define EMPTY "" typedef struct _conninfo { conntype_t conn_type; char const *conn_url; void *conn_arg1; void *conn_arg2; void *conn_arg3; void *conn_arg4; void *conn_res; struct MHD_PostProcessor *conn_pp; } conninfo_t; bool Webserver::usb = false; char *Webserver::devname = NULL; unsigned short Webserver::port = 0; bool Webserver::ready = false; extern pthread_mutex_t nlock; extern MyNode *nodes[]; extern pthread_mutex_t glock; extern bool done; extern bool needsave; extern uint32 homeId; extern uint8 nodeId; // controller node id extern uint8 SUCnodeId; extern char *cmode; extern bool noop; extern int debug; /* * web_send_data * Send internal HTML string */ static int web_send_data (struct MHD_Connection *connection, const char *data, const int code, bool free, bool copy, const char *ct) { struct MHD_Response *response; int ret; if (!copy && ! free) response = MHD_create_response_from_buffer(strlen(data), (void *) data, MHD_RESPMEM_PERSISTENT); else if (copy) response = MHD_create_response_from_buffer(strlen(data), (void *) data, MHD_RESPMEM_MUST_COPY); else response = MHD_create_response_from_buffer(strlen(data), (void *) data, MHD_RESPMEM_MUST_FREE); if (response == NULL) return MHD_NO; if (ct != NULL) MHD_add_response_header(response, "Content-type", ct); ret = MHD_queue_response(connection, code, response); MHD_destroy_response(response); return ret; } /* * web_read_file * Read files and send them out */ ssize_t web_read_file (void *cls, uint64_t pos, char *buf, size_t max) { FILE *file = (FILE *)cls; fseek(file, pos, SEEK_SET); return fread(buf, 1, max, file); } /* * web_close_file */ void web_close_file (void *cls) { FILE *fp = (FILE *)cls; fclose(fp); } /* * web_send_file * Read files and send them out */ int web_send_file (struct MHD_Connection *conn, const char *filename, const int code, const bool unl) { struct stat buf; FILE *fp; struct MHD_Response *response; const char *p; const char *ct = NULL; int ret; if ((p = strchr(filename, '.')) != NULL) { p++; if (strcmp(p, "xml") == 0) ct = "text/xml"; else if (strcmp(p, "js") == 0) ct = "text/javascript"; } if (stat(filename, &buf) == -1 || ((fp = fopen(filename, "r")) == NULL)) { if (strcmp(p, "xml") == 0) response = MHD_create_response_from_buffer(0, (void *) "", MHD_RESPMEM_PERSISTENT); else { int len = strlen(FNF) + strlen(filename) - 1; // len(%s) + 1 for \0 char *s = (char *)malloc(len); if (s == NULL) { fprintf(stderr, "Out of memory FNF\n"); exit(1); } snprintf(s, len, FNF, filename); response = MHD_create_response_from_buffer(len, (void *) s, MHD_RESPMEM_MUST_FREE); // free } } else response = MHD_create_response_from_callback(buf.st_size, 32 * 1024, &web_read_file, fp, &web_close_file); if (response == NULL) return MHD_YES; if (ct != NULL) MHD_add_response_header(response, "Content-type", ct); ret = MHD_queue_response(conn, code, response); MHD_destroy_response(response); if (unl) unlink(filename); return ret; } /* * web_get_groups * Return some XML to carry node group associations */ void Webserver::web_get_groups (int n, TiXmlElement *ep) { int cnt = nodes[n]->numGroups(); int i; TiXmlElement* groupsElement = new TiXmlElement("groups"); ep->LinkEndChild(groupsElement); groupsElement->SetAttribute("cnt", cnt); for (i = 1; i <= cnt; i++) { TiXmlElement* groupElement = new TiXmlElement("group"); MyGroup *p = nodes[n]->getGroup(i); groupElement->SetAttribute("ind", i); groupElement->SetAttribute("max", p->max); groupElement->SetAttribute("label", p->label.c_str()); string str = ""; for (uint j = 0; j < p->grouplist.size(); j++) { str += p->grouplist[j]; if (j + 1 < p->grouplist.size()) str += ","; } TiXmlText *textElement = new TiXmlText(str.c_str()); groupElement->LinkEndChild(textElement); groupsElement->LinkEndChild(groupElement); } } /* * web_get_values * Retrieve class values based on genres */ void Webserver::web_get_values (int i, TiXmlElement *ep) { uint16 idcnt = nodes[i]->getValueCount(); for (uint16 j = 0; j < idcnt; j++) { TiXmlElement* valueElement = new TiXmlElement("value"); MyValue *vals = nodes[i]->getValue(j); ValueID id = vals->getId(); valueElement->SetAttribute("genre", valueGenreStr(id.GetGenre())); valueElement->SetAttribute("type", valueTypeStr(id.GetType())); valueElement->SetAttribute("class", cclassStr(id.GetCommandClassId())); valueElement->SetAttribute("instance", id.GetInstance()); valueElement->SetAttribute("index", id.GetIndex()); valueElement->SetAttribute("label", Manager::Get()->GetValueLabel(id).c_str()); valueElement->SetAttribute("units", Manager::Get()->GetValueUnits(id).c_str()); valueElement->SetAttribute("readonly", Manager::Get()->IsValueReadOnly(id) ? "true" : "false"); if (id.GetGenre() != ValueID::ValueGenre_Config) valueElement->SetAttribute("polled", Manager::Get()->isPolled(id) ? "true" : "false"); if (id.GetType() == ValueID::ValueType_List) { vector strs; Manager::Get()->GetValueListItems(id, &strs); valueElement->SetAttribute("count", strs.size()); string str; Manager::Get()->GetValueListSelection(id, &str); valueElement->SetAttribute("current", str.c_str()); for (vector::iterator it = strs.begin(); it != strs.end(); it++) { TiXmlElement* itemElement = new TiXmlElement("item"); valueElement->LinkEndChild(itemElement); TiXmlText *textElement = new TiXmlText((*it).c_str()); itemElement->LinkEndChild(textElement); } } else { string str; TiXmlText *textElement; if (Manager::Get()->GetValueAsString(id, &str)) textElement = new TiXmlText(str.c_str()); else textElement = new TiXmlText(""); if (id.GetType() == ValueID::ValueType_Decimal) { uint8 precision; if (Manager::Get()->GetValueFloatPrecision(id, &precision)) fprintf(stderr, "node = %d id = %d value = %s precision = %d\n", i, j, str.c_str(), precision); } valueElement->LinkEndChild(textElement); } string str = Manager::Get()->GetValueHelp(id); if (str.length() > 0) { TiXmlElement* helpElement = new TiXmlElement("help"); TiXmlText *textElement = new TiXmlText(str.c_str()); helpElement->LinkEndChild(textElement); valueElement->LinkEndChild(helpElement); } ep->LinkEndChild(valueElement); } } /* * SendTopoResponse * Process topology request and return appropiate data */ const char *Webserver::SendTopoResponse (struct MHD_Connection *conn, const char *fun, const char *arg1, const char *arg2, const char *arg3) { TiXmlDocument doc; char str[16]; static char fntemp[32]; char *fn; uint i, j, k; uint8 cnt; uint32 len; uint8 *neighbors; TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "utf-8", "" ); doc.LinkEndChild(decl); TiXmlElement* topoElement = new TiXmlElement("topo"); doc.LinkEndChild(topoElement); if (strcmp(fun, "load") == 0) { cnt = MyNode::getNodeCount(); i = 0; j = 1; while (j <= cnt && i < MAX_NODES) { if (nodes[i] != NULL) { len = Manager::Get()->GetNodeNeighbors(homeId, i, &neighbors); if (len > 0) { TiXmlElement* nodeElement = new TiXmlElement("node"); snprintf(str, sizeof(str), "%d", i); nodeElement->SetAttribute("id", str); string list = ""; for (k = 0; k < len; k++) { snprintf(str, sizeof(str), "%d", neighbors[k]); list += str; if (k < (len - 1)) list += ","; } fprintf(stderr, "topo: node=%d %s\n", i, list.c_str()); TiXmlText *textElement = new TiXmlText(list.c_str()); nodeElement->LinkEndChild(textElement); topoElement->LinkEndChild(nodeElement); delete [] neighbors; } j++; } i++; } } strncpy(fntemp, "/tmp/ozwcp.topo.XXXXXX", sizeof(fntemp)); fn = mktemp(fntemp); if (fn == NULL) return EMPTY; strncat(fntemp, ".xml", sizeof(fntemp)); if (debug) doc.Print(stdout, 0); doc.SaveFile(fn); return fn; } static TiXmlElement *newstat (char const *tag, char const *label, uint32 const value) { char str[32]; TiXmlElement* statElement = new TiXmlElement(tag); statElement->SetAttribute("label", label); snprintf(str, sizeof(str), "%d", value); TiXmlText *textElement = new TiXmlText(str); statElement->LinkEndChild(textElement); return statElement; } static TiXmlElement *newstat (char const *tag, char const *label, char const *value) { TiXmlElement* statElement = new TiXmlElement(tag); statElement->SetAttribute("label", label); TiXmlText *textElement = new TiXmlText(value); statElement->LinkEndChild(textElement); return statElement; } /* * SendStatResponse * Process statistics request and return appropiate data */ const char *Webserver::SendStatResponse (struct MHD_Connection *conn, const char *fun, const char *arg1, const char *arg2, const char *arg3) { TiXmlDocument doc; static char fntemp[32]; char *fn; TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "utf-8", "" ); doc.LinkEndChild(decl); TiXmlElement* statElement = new TiXmlElement("stats"); doc.LinkEndChild(statElement); if (strcmp(fun, "load") == 0) { struct Driver::DriverData data; int i, j; int cnt; char str[16]; Manager::Get()->GetDriverStatistics(homeId, &data); TiXmlElement* errorsElement = new TiXmlElement("errors"); errorsElement->LinkEndChild(newstat("stat", "ACK Waiting", data.m_ACKWaiting)); errorsElement->LinkEndChild(newstat("stat", "Read Aborts", data.m_readAborts)); errorsElement->LinkEndChild(newstat("stat", "Bad Checksums", data.m_badChecksum)); errorsElement->LinkEndChild(newstat("stat", "CANs", data.m_CANCnt)); errorsElement->LinkEndChild(newstat("stat", "NAKs", data.m_NAKCnt)); errorsElement->LinkEndChild(newstat("stat", "Out of Frame", data.m_OOFCnt)); statElement->LinkEndChild(errorsElement); TiXmlElement* countsElement = new TiXmlElement("counts"); countsElement->LinkEndChild(newstat("stat", "SOF", data.m_SOFCnt)); countsElement->LinkEndChild(newstat("stat", "Total Reads", data.m_readCnt)); countsElement->LinkEndChild(newstat("stat", "Total Writes", data.m_writeCnt)); countsElement->LinkEndChild(newstat("stat", "ACKs", data.m_ACKCnt)); countsElement->LinkEndChild(newstat("stat", "Total Broadcasts Received", data.m_broadcastReadCnt)); countsElement->LinkEndChild(newstat("stat", "Total Broadcasts Transmitted", data.m_broadcastWriteCnt)); statElement->LinkEndChild(countsElement); TiXmlElement* infoElement = new TiXmlElement("info"); infoElement->LinkEndChild(newstat("stat", "Dropped", data.m_dropped)); infoElement->LinkEndChild(newstat("stat", "Retries", data.m_retries)); infoElement->LinkEndChild(newstat("stat", "Unexpected Callbacks", data.m_callbacks)); infoElement->LinkEndChild(newstat("stat", "Bad Routes", data.m_badroutes)); infoElement->LinkEndChild(newstat("stat", "No ACK", data.m_noack)); infoElement->LinkEndChild(newstat("stat", "Network Busy", data.m_netbusy)); infoElement->LinkEndChild(newstat("stat", "Not Idle", data.m_notidle)); infoElement->LinkEndChild(newstat("stat", "Non Delivery", data.m_nondelivery)); infoElement->LinkEndChild(newstat("stat", "Routes Busy", data.m_routedbusy)); statElement->LinkEndChild(infoElement); cnt = MyNode::getNodeCount(); i = 0; j = 1; while (j <= cnt && i < MAX_NODES) { struct Node::NodeData ndata; if (nodes[i] != NULL) { Manager::Get()->GetNodeStatistics(homeId, i, &ndata); TiXmlElement* nodeElement = new TiXmlElement("node"); snprintf(str, sizeof(str), "%d", i); nodeElement->SetAttribute("id", str); nodeElement->LinkEndChild(newstat("nstat", "Sent messages", ndata.m_sentCnt)); nodeElement->LinkEndChild(newstat("nstat", "Failed sent messages", ndata.m_sentFailed)); nodeElement->LinkEndChild(newstat("nstat", "Retried sent messages", ndata.m_retries)); nodeElement->LinkEndChild(newstat("nstat", "Received messages", ndata.m_receivedCnt)); nodeElement->LinkEndChild(newstat("nstat", "Received duplicates", ndata.m_receivedDups)); nodeElement->LinkEndChild(newstat("nstat", "Received unsolicited", ndata.m_receivedUnsolicited)); nodeElement->LinkEndChild(newstat("nstat", "Last sent message", ndata.m_sentTS.substr(5).c_str())); nodeElement->LinkEndChild(newstat("nstat", "Last received message", ndata.m_receivedTS.substr(5).c_str())); nodeElement->LinkEndChild(newstat("nstat", "Last Request RTT", ndata.m_averageRequestRTT)); nodeElement->LinkEndChild(newstat("nstat", "Average Request RTT", ndata.m_averageRequestRTT)); nodeElement->LinkEndChild(newstat("nstat", "Last Response RTT", ndata.m_averageResponseRTT)); nodeElement->LinkEndChild(newstat("nstat", "Average Response RTT", ndata.m_averageResponseRTT)); nodeElement->LinkEndChild(newstat("nstat", "Quality", ndata.m_quality)); while (!ndata.m_ccData.empty()) { Node::CommandClassData ccd = ndata.m_ccData.front(); TiXmlElement* ccElement = new TiXmlElement("commandclass"); snprintf(str, sizeof(str), "%d", ccd.m_commandClassId); ccElement->SetAttribute("id", str); ccElement->SetAttribute("name", cclassStr(ccd.m_commandClassId)); ccElement->LinkEndChild(newstat("cstat", "Messages sent", ccd.m_sentCnt)); ccElement->LinkEndChild(newstat("cstat", "Messages received", ccd.m_receivedCnt)); nodeElement->LinkEndChild(ccElement); ndata.m_ccData.pop_front(); } statElement->LinkEndChild(nodeElement); j++; } i++; } } strncpy(fntemp, "/tmp/ozwcp.stat.XXXXXX", sizeof(fntemp)); fn = mktemp(fntemp); if (fn == NULL) return EMPTY; strncat(fntemp, ".xml", sizeof(fntemp)); if (debug) doc.Print(stdout, 0); doc.SaveFile(fn); return fn; } /* * SendTestHealResponse * Process network test and heal requests */ const char *Webserver::SendTestHealResponse (struct MHD_Connection *conn, const char *fun, const char *arg1, const char *arg2, const char *arg3) { TiXmlDocument doc; int node; int arg; bool healrrs = false; static char fntemp[32]; char *fn; TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "utf-8", "" ); doc.LinkEndChild(decl); TiXmlElement* testElement = new TiXmlElement("testheal"); doc.LinkEndChild(testElement); if (strcmp(fun, "test") == 0 && arg1 != NULL) { node = atoi((char *)arg1); arg = atoi((char *)arg2); if (node == 0) Manager::Get()->TestNetwork(homeId, arg); else Manager::Get()->TestNetworkNode(homeId, node, arg); } else if (strcmp(fun, "heal") == 0 && arg1 != NULL) { testElement = new TiXmlElement("heal"); node = atoi((char *)arg1); if (arg2 != NULL) { arg = atoi((char *)arg2); if (arg != 0) healrrs = true; } if (node == 0) Manager::Get()->HealNetwork(homeId, healrrs); else Manager::Get()->HealNetworkNode(homeId, node, healrrs); } strncpy(fntemp, "/tmp/ozwcp.testheal.XXXXXX", sizeof(fntemp)); fn = mktemp(fntemp); if (fn == NULL) return EMPTY; strncat(fntemp, ".xml", sizeof(fntemp)); if (debug) doc.Print(stdout, 0); doc.SaveFile(fn); return fn; } /* * SendSceneResponse * Process scene request and return appropiate scene data */ const char *Webserver::SendSceneResponse (struct MHD_Connection *conn, const char *fun, const char *arg1, const char *arg2, const char *arg3) { TiXmlDocument doc; char str[16]; string s; static char fntemp[32]; char *fn; int cnt; int i; uint8 sid; TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "utf-8", "" ); doc.LinkEndChild(decl); TiXmlElement* scenesElement = new TiXmlElement("scenes"); doc.LinkEndChild(scenesElement); if (strcmp(fun, "create") == 0) { sid = Manager::Get()->CreateScene(); if (sid == 0) { fprintf(stderr, "sid = 0, out of scene ids\n"); return EMPTY; } } if (strcmp(fun, "values") == 0 || strcmp(fun, "label") == 0 || strcmp(fun, "delete") == 0 || strcmp(fun, "execute") == 0 || strcmp(fun, "addvalue") == 0 || strcmp(fun, "update") == 0 || strcmp(fun, "remove") == 0) { sid = atoi((char *)arg1); if (strcmp(fun, "delete") == 0) Manager::Get()->RemoveScene(sid); if (strcmp(fun, "execute") == 0) Manager::Get()->ActivateScene(sid); if (strcmp(fun, "label") == 0) Manager::Get()->SetSceneLabel(sid, string(arg2)); if (strcmp(fun, "addvalue") == 0 || strcmp(fun, "update") == 0 || strcmp(fun, "remove") == 0) { MyValue *val = MyNode::lookup(string(arg2)); if (val != NULL) { if (strcmp(fun, "addvalue") == 0) { if (!Manager::Get()->AddSceneValue(sid, val->getId(), string(arg3))) fprintf(stderr, "AddSceneValue failure\n"); } else if (strcmp(fun, "update") == 0) { if (!Manager::Get()->SetSceneValue(sid, val->getId(), string(arg3))) fprintf(stderr, "SetSceneValue failure\n"); } else if (strcmp(fun, "remove") == 0) { if (!Manager::Get()->RemoveSceneValue(sid, val->getId())) fprintf(stderr, "RemoveSceneValue failure\n"); } } } } if (strcmp(fun, "load") == 0 || strcmp(fun, "create") == 0 || strcmp(fun, "label") == 0 || strcmp(fun, "delete") == 0) { // send all sceneids uint8 *sptr; cnt = Manager::Get()->GetAllScenes(&sptr); scenesElement->SetAttribute("sceneid", cnt); for (i = 0; i < cnt; i++) { TiXmlElement* sceneElement = new TiXmlElement("sceneid"); snprintf(str, sizeof(str), "%d", sptr[i]); sceneElement->SetAttribute("id", str); s = Manager::Get()->GetSceneLabel(sptr[i]); sceneElement->SetAttribute("label", s.c_str()); scenesElement->LinkEndChild(sceneElement); } delete [] sptr; } if (strcmp(fun, "values") == 0 || strcmp(fun, "addvalue") == 0 || strcmp(fun, "remove") == 0 || strcmp(fun, "update") == 0) { vector vids; cnt = Manager::Get()->SceneGetValues(sid, &vids); scenesElement->SetAttribute("scenevalue", cnt); for (vector::iterator it = vids.begin(); it != vids.end(); it++) { TiXmlElement* valueElement = new TiXmlElement("scenevalue"); valueElement->SetAttribute("id", sid); snprintf(str, sizeof(str), "0x%x", (*it).GetHomeId()); valueElement->SetAttribute("home", str); valueElement->SetAttribute("node", (*it).GetNodeId()); valueElement->SetAttribute("class", cclassStr((*it).GetCommandClassId())); valueElement->SetAttribute("instance", (*it).GetInstance()); valueElement->SetAttribute("index", (*it).GetIndex()); valueElement->SetAttribute("type", valueTypeStr((*it).GetType())); valueElement->SetAttribute("genre", valueGenreStr((*it).GetGenre())); valueElement->SetAttribute("label", Manager::Get()->GetValueLabel(*it).c_str()); valueElement->SetAttribute("units", Manager::Get()->GetValueUnits(*it).c_str()); Manager::Get()->SceneGetValueAsString(sid, *it, &s); TiXmlText *textElement = new TiXmlText(s.c_str()); valueElement->LinkEndChild(textElement); scenesElement->LinkEndChild(valueElement); } } strncpy(fntemp, "/tmp/ozwcp.scenes.XXXXXX", sizeof(fntemp)); fn = mktemp(fntemp); if (fn == NULL) return EMPTY; strncat(fntemp, ".xml", sizeof(fntemp)); if (debug) doc.Print(stdout, 0); doc.SaveFile(fn); return fn; } /* * SendPollResponse * Process poll request from client and return * data as xml. */ int Webserver::SendPollResponse (struct MHD_Connection *conn) { TiXmlDocument doc; struct stat buf; const int logbufsz = 1024; // max amount to send of log per poll char logbuffer[logbufsz+1]; off_t bcnt; char str[16]; int32 i, j; int32 logread = 0; char fntemp[32]; char *fn; FILE *fp; int32 ret; TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "utf-8", "" ); doc.LinkEndChild(decl); TiXmlElement* pollElement = new TiXmlElement("poll"); doc.LinkEndChild(pollElement); if (homeId != 0L) snprintf(str, sizeof(str), "%08x", homeId); else str[0] = '\0'; pollElement->SetAttribute("homeid", str); if (nodeId != 0) snprintf(str, sizeof(str), "%d", nodeId); else str[0] = '\0'; pollElement->SetAttribute("nodeid", str); snprintf(str, sizeof(str), "%d", SUCnodeId); pollElement->SetAttribute("sucnodeid", str); pollElement->SetAttribute("nodecount", MyNode::getNodeCount()); pollElement->SetAttribute("cmode", cmode); pollElement->SetAttribute("save", needsave); pollElement->SetAttribute("noop", noop); if (noop) noop = false; bcnt = logbytes; if (stat("./OZW_Log.txt", &buf) != -1 && buf.st_size > bcnt && (fp = fopen("./OZW_Log.txt", "r")) != NULL) { if (fseek(fp, bcnt, SEEK_SET) != -1) { logread = fread(logbuffer, 1, logbufsz, fp); while (logread > 0 && logbuffer[--logread] != '\n') ; logbytes = bcnt + logread; fclose(fp); } } logbuffer[logread] = '\0'; TiXmlElement* logElement = new TiXmlElement("log"); pollElement->LinkEndChild(logElement); logElement->SetAttribute("size", logread); logElement->SetAttribute("offset", logbytes - logread); TiXmlText *textElement = new TiXmlText(logbuffer); logElement->LinkEndChild(textElement); TiXmlElement* adminElement = new TiXmlElement("admin"); pollElement->LinkEndChild(adminElement); adminElement->SetAttribute("active", getAdminState() ? "true" : "false"); if (adminmsg.length() > 0) { string msg = getAdminFunction() + getAdminMessage(); TiXmlText *textElement = new TiXmlText(msg.c_str()); adminElement->LinkEndChild(textElement); adminmsg.clear(); } TiXmlElement* updateElement = new TiXmlElement("update"); pollElement->LinkEndChild(updateElement); i = MyNode::getRemovedCount(); if (i > 0) { logbuffer[0] = '\0'; while (i > 0) { uint8 node = MyNode::getRemoved(); snprintf(str, sizeof(str), "%d", node); strcat(logbuffer, str); i = MyNode::getRemovedCount(); if (i > 0) strcat(logbuffer, ","); } updateElement->SetAttribute("remove", logbuffer); } pthread_mutex_lock(&nlock); if (MyNode::getAnyChanged()) { i = 0; j = 1; while (j <= MyNode::getNodeCount() && i < MAX_NODES) { if (nodes[i] != NULL && nodes[i]->getChanged()) { bool listening; bool flirs; bool zwaveplus; TiXmlElement* nodeElement = new TiXmlElement("node"); pollElement->LinkEndChild(nodeElement); nodeElement->SetAttribute("id", i); zwaveplus = Manager::Get()->IsNodeZWavePlus(homeId, i); if (zwaveplus) { string value = Manager::Get()->GetNodePlusTypeString(homeId, i); value += " " + Manager::Get()->GetNodeRoleString(homeId, i); nodeElement->SetAttribute("btype", value.c_str()); nodeElement->SetAttribute("gtype", Manager::Get()->GetNodeDeviceTypeString(homeId, i).c_str()); } else { nodeElement->SetAttribute("btype", nodeBasicStr(Manager::Get()->GetNodeBasic(homeId, i))); nodeElement->SetAttribute("gtype", Manager::Get()->GetNodeType(homeId, i).c_str()); } nodeElement->SetAttribute("name", Manager::Get()->GetNodeName(homeId, i).c_str()); nodeElement->SetAttribute("location", Manager::Get()->GetNodeLocation(homeId, i).c_str()); nodeElement->SetAttribute("manufacturer", Manager::Get()->GetNodeManufacturerName(homeId, i).c_str()); nodeElement->SetAttribute("product", Manager::Get()->GetNodeProductName(homeId, i).c_str()); listening = Manager::Get()->IsNodeListeningDevice(homeId, i); nodeElement->SetAttribute("listening", listening ? "true" : "false"); flirs = Manager::Get()->IsNodeFrequentListeningDevice(homeId, i); nodeElement->SetAttribute("frequent", flirs ? "true" : "false"); nodeElement->SetAttribute("zwaveplus", zwaveplus ? "true" : "false"); nodeElement->SetAttribute("beam", Manager::Get()->IsNodeBeamingDevice(homeId, i) ? "true" : "false"); nodeElement->SetAttribute("routing", Manager::Get()->IsNodeRoutingDevice(homeId, i) ? "true" : "false"); nodeElement->SetAttribute("security", Manager::Get()->IsNodeSecurityDevice(homeId, i) ? "true" : "false"); nodeElement->SetAttribute("time", nodes[i]->getTime()); #if 0 fprintf(stderr, "i=%d failed=%d\n", i, Manager::Get()->IsNodeFailed(homeId, i)); fprintf(stderr, "i=%d awake=%d\n", i, Manager::Get()->IsNodeAwake(homeId, i)); fprintf(stderr, "i=%d state=%s\n", i, Manager::Get()->GetNodeQueryStage(homeId, i).c_str()); fprintf(stderr, "i=%d listening=%d flirs=%d\n", i, listening, flirs); #endif if (Manager::Get()->IsNodeFailed(homeId, i)) nodeElement->SetAttribute("status", "Dead"); else { string s = Manager::Get()->GetNodeQueryStage(homeId, i); if (s == "Complete") { if (i != nodeId && !listening && !flirs) nodeElement->SetAttribute("status", Manager::Get()->IsNodeAwake(homeId, i) ? "Awake" : "Sleeping" ); else nodeElement->SetAttribute("status", "Ready"); } else { if (i != nodeId && !listening && !flirs) s = s + (Manager::Get()->IsNodeAwake(homeId, i) ? " (awake)" : " (sleeping)"); nodeElement->SetAttribute("status", s.c_str()); } } web_get_groups(i, nodeElement); // Don't think the UI needs these //web_get_genre(ValueID::ValueGenre_Basic, i, nodeElement); web_get_values(i, nodeElement); nodes[i]->setChanged(false); j++; } i++; } } pthread_mutex_unlock(&nlock); strncpy(fntemp, "/tmp/ozwcp.poll.XXXXXX", sizeof(fntemp)); fn = mktemp(fntemp); if (fn == NULL) return MHD_YES; strncat(fntemp, ".xml", sizeof(fntemp)); if (debug) doc.Print(stdout, 0); doc.SaveFile(fn); ret = web_send_file(conn, fn, MHD_HTTP_OK, true); return ret; } /* * SendDeviceListResponse * Process request for Device List from client and return * data as xml. */ int Webserver::SendDeviceListResponse (struct MHD_Connection *conn) { TiXmlDocument doc; char str[16]; int32 i, j; char fntemp[32]; char *fn; int32 ret; TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "utf-8", "" ); doc.LinkEndChild(decl); TiXmlElement* pollElement = new TiXmlElement("devices"); doc.LinkEndChild(pollElement); if (homeId != 0L) snprintf(str, sizeof(str), "%08x", homeId); else str[0] = '\0'; pollElement->SetAttribute("homeid", str); if (nodeId != 0) snprintf(str, sizeof(str), "%d", nodeId); else str[0] = '\0'; pollElement->SetAttribute("nodeid", str); snprintf(str, sizeof(str), "%d", SUCnodeId); pollElement->SetAttribute("sucnodeid", str); pollElement->SetAttribute("nodecount", MyNode::getNodeCount()); pollElement->SetAttribute("cmode", cmode); pollElement->SetAttribute("save", needsave); pollElement->SetAttribute("noop", noop); pthread_mutex_lock(&nlock); i = 0; j = 1; while (j <= MyNode::getNodeCount() && i < MAX_NODES) { if (nodes[i] != NULL) { bool listening; bool flirs; bool zwaveplus; TiXmlElement* nodeElement = new TiXmlElement("node"); pollElement->LinkEndChild(nodeElement); nodeElement->SetAttribute("id", i); zwaveplus = Manager::Get()->IsNodeZWavePlus(homeId, i); if (zwaveplus) { string value = Manager::Get()->GetNodePlusTypeString(homeId, i); value += " " + Manager::Get()->GetNodeRoleString(homeId, i); nodeElement->SetAttribute("btype", value.c_str()); nodeElement->SetAttribute("gtype", Manager::Get()->GetNodeDeviceTypeString(homeId, i).c_str()); } else { nodeElement->SetAttribute("btype", nodeBasicStr(Manager::Get()->GetNodeBasic(homeId, i))); nodeElement->SetAttribute("gtype", Manager::Get()->GetNodeType(homeId, i).c_str()); } nodeElement->SetAttribute("name", Manager::Get()->GetNodeName(homeId, i).c_str()); nodeElement->SetAttribute("location", Manager::Get()->GetNodeLocation(homeId, i).c_str()); nodeElement->SetAttribute("manufacturer", Manager::Get()->GetNodeManufacturerName(homeId, i).c_str()); nodeElement->SetAttribute("product", Manager::Get()->GetNodeProductName(homeId, i).c_str()); listening = Manager::Get()->IsNodeListeningDevice(homeId, i); nodeElement->SetAttribute("listening", listening ? "true" : "false"); flirs = Manager::Get()->IsNodeFrequentListeningDevice(homeId, i); nodeElement->SetAttribute("frequent", flirs ? "true" : "false"); nodeElement->SetAttribute("zwaveplus", zwaveplus ? "true" : "false"); nodeElement->SetAttribute("beam", Manager::Get()->IsNodeBeamingDevice(homeId, i) ? "true" : "false"); nodeElement->SetAttribute("routing", Manager::Get()->IsNodeRoutingDevice(homeId, i) ? "true" : "false"); nodeElement->SetAttribute("security", Manager::Get()->IsNodeSecurityDevice(homeId, i) ? "true" : "false"); #if 0 fprintf(stderr, "i=%d failed=%d\n", i, Manager::Get()->IsNodeFailed(homeId, i)); fprintf(stderr, "i=%d awake=%d\n", i, Manager::Get()->IsNodeAwake(homeId, i)); fprintf(stderr, "i=%d state=%s\n", i, Manager::Get()->GetNodeQueryStage(homeId, i).c_str()); fprintf(stderr, "i=%d listening=%d flirs=%d\n", i, listening, flirs); #endif if (Manager::Get()->IsNodeFailed(homeId, i)) nodeElement->SetAttribute("status", "Dead"); else { string s = Manager::Get()->GetNodeQueryStage(homeId, i); if (s == "Complete") { if (i != nodeId && !listening && !flirs) nodeElement->SetAttribute("status", Manager::Get()->IsNodeAwake(homeId, i) ? "Awake" : "Sleeping" ); else nodeElement->SetAttribute("status", "Ready"); } else { if (i != nodeId && !listening && !flirs) s = s + (Manager::Get()->IsNodeAwake(homeId, i) ? " (awake)" : " (sleeping)"); nodeElement->SetAttribute("status", s.c_str()); } } web_get_groups(i, nodeElement); web_get_values(i, nodeElement); j++; } i++; } pthread_mutex_unlock(&nlock); strncpy(fntemp, "/tmp/ozwcp.devices.XXXXXX", sizeof(fntemp)); fn = mktemp(fntemp); if (fn == NULL) return MHD_YES; strncat(fntemp, ".xml", sizeof(fntemp)); if (debug) doc.Print(stdout, 0); doc.SaveFile(fn); ret = web_send_file(conn, fn, MHD_HTTP_OK, true); return ret; } /* * web_controller_update * Handle controller function feedback from library. */ void web_controller_update (Driver::ControllerState cs, Driver::ControllerError err, void *ct) { Webserver *cp = (Webserver *)ct; string s; bool more = true; switch (cs) { case Driver::ControllerState_Normal: s = ": no command in progress."; break; case Driver::ControllerState_Starting: s = ": starting controller command."; break; case Driver::ControllerState_Cancel: s = ": command was cancelled."; more = false; break; case Driver::ControllerState_Error: s = ": command returned an error: "; more = false; break; case Driver::ControllerState_Sleeping: s = ": device went to sleep."; more = false; break; case Driver::ControllerState_Waiting: s = ": waiting for a user action."; break; case Driver::ControllerState_InProgress: s = ": communicating with the other device."; break; case Driver::ControllerState_Completed: s = ": command has completed successfully."; more = false; break; case Driver::ControllerState_Failed: s = ": command has failed."; more = false; break; case Driver::ControllerState_NodeOK: s = ": the node is OK."; more = false; break; case Driver::ControllerState_NodeFailed: s = ": the node has failed."; more = false; break; default: s = ": unknown respose."; break; } if (err != Driver::ControllerError_None) s = s + controllerErrorStr(err); cp->setAdminMessage(s); cp->setAdminState(more); } /* * web_config_post * Handle the post of the updated data */ int web_config_post (void *cls, enum MHD_ValueKind kind, const char *key, const char *filename, const char *content_type, const char *transfer_encoding, const char *data, uint64_t off, size_t size) { conninfo_t *cp = (conninfo_t *)cls; fprintf(stderr, "post: key=%s data=%s size=%d\n", key, data, size); if (strcmp(cp->conn_url, "/devpost.html") == 0) { if (strcmp(key, "fn") == 0) cp->conn_arg1 = (void *)strdup(data); else if (strcmp(key, "dev") == 0) cp->conn_arg2 = (void *)strdup(data); else if (strcmp(key, "usb") == 0) cp->conn_arg3 = (void *)strdup(data); } else if (strcmp(cp->conn_url, "/valuepost.html") == 0) { cp->conn_arg1 = (void *)strdup(key); cp->conn_arg2 = (void *)strdup(data); } else if (strcmp(cp->conn_url, "/buttonpost.html") == 0) { cp->conn_arg1 = (void *)strdup(key); cp->conn_arg2 = (void *)strdup(data); } else if (strcmp(cp->conn_url, "/admpost.html") == 0) { if (strcmp(key, "fun") == 0) cp->conn_arg1 = (void *)strdup(data); else if (strcmp(key, "node") == 0) cp->conn_arg2 = (void *)strdup(data); else if (strcmp(key, "button") == 0) cp->conn_arg3 = (void *)strdup(data); } else if (strcmp(cp->conn_url, "/nodepost.html") == 0) { if (strcmp(key, "fun") == 0) cp->conn_arg1 = (void *)strdup(data); else if (strcmp(key, "node") == 0) cp->conn_arg2 = (void *)strdup(data); else if (strcmp(key, "value") == 0) cp->conn_arg3 = (void *)strdup(data); } else if (strcmp(cp->conn_url, "/grouppost.html") == 0) { if (strcmp(key, "fun") == 0) cp->conn_arg1 = (void *)strdup(data); else if (strcmp(key, "node") == 0) cp->conn_arg2 = (void *)strdup(data); else if (strcmp(key, "num") == 0) cp->conn_arg3 = (void *)strdup(data); else if (strcmp(key, "groups") == 0) cp->conn_arg4 = (void *)strdup(data); } else if (strcmp(cp->conn_url, "/pollpost.html") == 0) { if (strcmp(key, "fun") == 0) cp->conn_arg1 = (void *)strdup(data); else if (strcmp(key, "node") == 0) cp->conn_arg2 = (void *)strdup(data); else if (strcmp(key, "ids") == 0) cp->conn_arg3 = (void *)strdup(data); else if (strcmp(key, "poll") == 0) cp->conn_arg4 = (void *)strdup(data); } else if (strcmp(cp->conn_url, "/savepost.html") == 0) { if (strcmp(key, "fun") == 0) cp->conn_arg1 = (void *)strdup(data); } else if (strcmp(cp->conn_url, "/scenepost.html") == 0) { if (strcmp(key, "fun") == 0) cp->conn_arg1 = (void *)strdup(data); else if (strcmp(key, "id") == 0) cp->conn_arg2 = (void *)strdup(data); else if (strcmp(key, "vid") == 0) cp->conn_arg3 = (void *)strdup(data); else if (strcmp(key, "label") == 0) cp->conn_arg3 = (void *)strdup(data); else if (strcmp(key, "value") == 0) cp->conn_arg4 = (void *)strdup(data); } else if (strcmp(cp->conn_url, "/topopost.html") == 0) { if (strcmp(key, "fun") == 0) cp->conn_arg1 = (void *)strdup(data); } else if (strcmp(cp->conn_url, "/statpost.html") == 0) { if (strcmp(key, "fun") == 0) cp->conn_arg1 = (void *)strdup(data); } else if (strcmp(cp->conn_url, "/thpost.html") == 0) { if (strcmp(key, "fun") == 0) cp->conn_arg1 = (void *)strdup(data); if (strcmp(key, "num") == 0) cp->conn_arg2 = (void *)strdup(data); if (strcmp(key, "cnt") == 0) cp->conn_arg3 = (void *)strdup(data); if (strcmp(key, "healrrs") == 0) cp->conn_arg3 = (void *)strdup(data); } else if (strcmp(cp->conn_url, "/confparmpost.html") == 0) { if (strcmp(key, "fun") == 0) cp->conn_arg1 = (void *)strdup(data); else if (strcmp(key, "node") == 0) cp->conn_arg2 = (void *)strdup(data); } else if (strcmp(cp->conn_url, "/refreshpost.html") == 0) { if (strcmp(key, "fun") == 0) cp->conn_arg1 = (void *)strdup(data); else if (strcmp(key, "node") == 0) cp->conn_arg2 = (void *)strdup(data); } return MHD_YES; } /* * Process web requests */ int Webserver::HandlerEP (void *cls, struct MHD_Connection *conn, const char *url, const char *method, const char *version, const char *up_data, size_t *up_data_size, void **ptr) { Webserver *ws = (Webserver *)cls; return ws->Handler(conn, url, method, version, up_data, up_data_size, ptr); } int Webserver::Handler (struct MHD_Connection *conn, const char *url, const char *method, const char *version, const char *up_data, size_t *up_data_size, void **ptr) { int ret; conninfo_t *cp; if (debug) fprintf(stderr, "%x: %s: \"%s\" conn=%x size=%d *ptr=%x\n", pthread_self(), method, url, conn, *up_data_size, *ptr); if (*ptr == NULL) { /* do never respond on first call */ cp = (conninfo_t *)malloc(sizeof(conninfo_t)); if (cp == NULL) return MHD_NO; cp->conn_url = url; cp->conn_arg1 = NULL; cp->conn_arg2 = NULL; cp->conn_arg3 = NULL; cp->conn_arg4 = NULL; cp->conn_res = NULL; if (strcmp(method, MHD_HTTP_METHOD_POST) == 0) { cp->conn_pp = MHD_create_post_processor(conn, 1024, web_config_post, (void *)cp); if (cp->conn_pp == NULL) { free(cp); return MHD_NO; } cp->conn_type = CON_POST; } else if (strcmp(method, MHD_HTTP_METHOD_GET) == 0) { cp->conn_type = CON_GET; } else { free(cp); return MHD_NO; } *ptr = (void *)cp; return MHD_YES; } if (strcmp(method, MHD_HTTP_METHOD_GET) == 0) { if (strcmp(url, "/") == 0 || strcmp(url, "/index.html") == 0) ret = web_send_file(conn, "cp.html", MHD_HTTP_OK, false); else if (strcmp(url, "/scenes.html") == 0) ret = web_send_file(conn, "scenes.html", MHD_HTTP_OK, false); else if (strcmp(url, "/cp.js") == 0) ret = web_send_file(conn, "cp.js", MHD_HTTP_OK, false); else if (strcmp(url, "/favicon.png") == 0) ret = web_send_file(conn, "openzwavetinyicon.png", MHD_HTTP_OK, false); else if (strcmp(url, "/poll.xml") == 0 && (devname != NULL || usb)) ret = SendPollResponse(conn); else if (strcmp(url, "/devices.xml") == 0 && (devname != NULL || usb)) ret = SendDeviceListResponse(conn); else if (strcmp(url, "/currdev") == 0) ret = web_send_data(conn, devname ? devname : "NULL", MHD_HTTP_OK, false, false, "text/pain"); // no free, no copy else ret = web_send_data(conn, UNKNOWN, MHD_HTTP_NOT_FOUND, false, false, NULL); // no free, no copy return ret; } else if (strcmp(method, MHD_HTTP_METHOD_POST) == 0) { cp = (conninfo_t *)*ptr; if (strcmp(url, "/devpost.html") == 0) { if (*up_data_size != 0) { MHD_post_process(cp->conn_pp, up_data, *up_data_size); *up_data_size = 0; if (strcmp((char *)cp->conn_arg1, "open") == 0) { /* start connection */ if (devname != NULL || usb) { MyNode::setAllChanged(true); } else { if ((char *)cp->conn_arg3 != NULL && strcmp((char *)cp->conn_arg3, "true") == 0) { Manager::Get()->AddDriver("HID Controller", Driver::ControllerInterface_Hid ); usb = true; } else { devname = (char *)malloc(strlen((char *)cp->conn_arg2) + 1); if (devname == NULL) { fprintf(stderr, "Out of memory open devname\n"); exit(1); } usb = false; strcpy(devname, (char *)cp->conn_arg2); Manager::Get()->AddDriver(devname); } } } else if (strcmp((char *)cp->conn_arg1, "close") == 0) { /* terminate */ if (devname != NULL || usb) Manager::Get()->RemoveDriver(devname ? devname : "HID Controller"); if (devname != NULL) { free(devname); devname = NULL; } usb = false; homeId = 0; } else if (strcmp((char *)cp->conn_arg1, "reset") == 0) { /* reset */ Manager::Get()->ResetController(homeId); } else if (strcmp((char *)cp->conn_arg1, "sreset") == 0) { /* soft reset */ Manager::Get()->SoftReset(homeId); } else if (strcmp((char *)cp->conn_arg1, "exit") == 0) { /* exit */ pthread_mutex_lock(&glock); if (devname != NULL || usb) { Manager::Get()->RemoveDriver(devname ? devname : "HID Controller"); free(devname); devname = NULL; homeId = 0; usb = false; } done = true; // let main exit pthread_mutex_unlock(&glock); } return MHD_YES; } else ret = web_send_data(conn, DEFAULT, MHD_HTTP_OK, false, false, NULL); // no free, no copy } else if (strcmp(url, "/valuepost.html") == 0) { if (*up_data_size != 0) { MHD_post_process(cp->conn_pp, up_data, *up_data_size); *up_data_size = 0; MyValue *val = MyNode::lookup(string((char *)cp->conn_arg1)); if (val != NULL) { string arg = (char *)cp->conn_arg2; if (!Manager::Get()->SetValue(val->getId(), arg)) fprintf(stderr, "SetValue string failed type=%s\n", valueTypeStr(val->getId().GetType())); } else { fprintf(stderr, "Can't find ValueID for %s\n", (char *)cp->conn_arg1); } return MHD_YES; } else ret = web_send_data(conn, EMPTY, MHD_HTTP_OK, false, false, NULL); // no free, no copy } else if (strcmp(url, "/buttonpost.html") == 0) { if (*up_data_size != 0) { MHD_post_process(cp->conn_pp, up_data, *up_data_size); *up_data_size = 0; MyValue *val = MyNode::lookup(string((char *)cp->conn_arg1)); if (val != NULL) { string arg = (char *)cp->conn_arg2; if ((char *)cp->conn_arg2 != NULL && strcmp((char *)cp->conn_arg2, "true") == 0) { if (!Manager::Get()->PressButton(val->getId())) fprintf(stderr, "PressButton failed"); } else { if (!Manager::Get()->ReleaseButton(val->getId())) fprintf(stderr, "ReleaseButton failed"); } } return MHD_YES; } else ret = web_send_data(conn, EMPTY, MHD_HTTP_OK, false, false, NULL); // no free, no copy } else if (strcmp(url, "/scenepost.html") == 0) { if (*up_data_size != 0) { MHD_post_process(cp->conn_pp, up_data, *up_data_size); *up_data_size = 0; cp->conn_res = (void *)SendSceneResponse(conn, (char *)cp->conn_arg1, (char *)cp->conn_arg2, (char *)cp->conn_arg3, (char *)cp->conn_arg4); return MHD_YES; } else ret = web_send_file(conn, (char *)cp->conn_res, MHD_HTTP_OK, true); } else if (strcmp(url, "/topopost.html") == 0) { if (*up_data_size != 0) { MHD_post_process(cp->conn_pp, up_data, *up_data_size); *up_data_size = 0; cp->conn_res = (void *)SendTopoResponse(conn, (char *)cp->conn_arg1, (char *)cp->conn_arg2, (char *)cp->conn_arg3, (char *)cp->conn_arg4); return MHD_YES; } else ret = web_send_file(conn, (char *)cp->conn_res, MHD_HTTP_OK, true); } else if (strcmp(url, "/statpost.html") == 0) { if (*up_data_size != 0) { MHD_post_process(cp->conn_pp, up_data, *up_data_size); *up_data_size = 0; cp->conn_res = (void *)SendStatResponse(conn, (char *)cp->conn_arg1, (char *)cp->conn_arg2, (char *)cp->conn_arg3, (char *)cp->conn_arg4); } else ret = web_send_file(conn, (char *)cp->conn_res, MHD_HTTP_OK, true); return MHD_YES; } else if (strcmp(url, "/thpost.html") == 0) { if (*up_data_size != 0) { MHD_post_process(cp->conn_pp, up_data, *up_data_size); *up_data_size = 0; cp->conn_res = (void *)SendTestHealResponse(conn, (char *)cp->conn_arg1, (char *)cp->conn_arg2, (char *)cp->conn_arg3, (char *)cp->conn_arg4); return MHD_YES; } else ret = web_send_file(conn, (char *)cp->conn_res, MHD_HTTP_OK, true); } else if (strcmp(url, "/confparmpost.html") == 0) { if (*up_data_size != 0) { MHD_post_process(cp->conn_pp, up_data, *up_data_size); *up_data_size = 0; if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 0) { uint8 node = strtol((char *)cp->conn_arg2, NULL, 10); Manager::Get()->RequestAllConfigParams(homeId, node); } return MHD_YES; } else ret = web_send_data(conn, EMPTY, MHD_HTTP_OK, false, false, NULL); // no free, no copy } else if (strcmp(url, "/refreshpost.html") == 0) { if (*up_data_size != 0) { MHD_post_process(cp->conn_pp, up_data, *up_data_size); *up_data_size = 0; if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 0) { uint8 node = strtol((char *)cp->conn_arg2, NULL, 10); Manager::Get()->RequestNodeDynamic(homeId, node); } return MHD_YES; } else ret = web_send_data(conn, EMPTY, MHD_HTTP_OK, false, false, NULL); // no free, no copy } else if (strcmp(url, "/admpost.html") == 0) { if (*up_data_size != 0) { MHD_post_process(cp->conn_pp, up_data, *up_data_size); *up_data_size = 0; if (strcmp((char *)cp->conn_arg1, "cancel") == 0) { /* cancel controller function */ Manager::Get()->CancelControllerCommand(homeId); setAdminState(false); } else if (strcmp((char *)cp->conn_arg1, "addd") == 0) { setAdminFunction("Add Device"); setAdminState(Manager::Get()->AddNode(homeId, false)); } else if (strcmp((char *)cp->conn_arg1, "addds") == 0) { setAdminFunction("Add Device"); setAdminState(Manager::Get()->AddNode(homeId, true)); } else if (strcmp((char *)cp->conn_arg1, "cprim") == 0) { setAdminFunction("Create Primary"); setAdminState(Manager::Get()->CreateNewPrimary(homeId)); } else if (strcmp((char *)cp->conn_arg1, "rconf") == 0) { setAdminFunction("Receive Configuration"); setAdminState(Manager::Get()->ReceiveConfiguration(homeId)); } else if (strcmp((char *)cp->conn_arg1, "remd") == 0) { setAdminFunction("Remove Device"); setAdminState(Manager::Get()->RemoveNode(homeId)); } else if (strcmp((char *)cp->conn_arg1, "hnf") == 0) { if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 4) { uint8 node = strtol(((char *)cp->conn_arg2) + 4, NULL, 10); setAdminFunction("Has Node Failed"); setAdminState(Manager::Get()->HasNodeFailed(homeId, node)); } } else if (strcmp((char *)cp->conn_arg1, "remfn") == 0) { if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 4) { uint8 node = strtol(((char *)cp->conn_arg2) + 4, NULL, 10); setAdminFunction("Remove Failed Node"); setAdminState(Manager::Get()->RemoveFailedNode(homeId, node)); } } else if (strcmp((char *)cp->conn_arg1, "repfn") == 0) { if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 4) { uint8 node = strtol(((char *)cp->conn_arg2) + 4, NULL, 10); setAdminFunction("Replace Failed Node"); setAdminState(Manager::Get()->ReplaceFailedNode(homeId, node)); } } else if (strcmp((char *)cp->conn_arg1, "tranpr") == 0) { setAdminFunction("Transfer Primary Role"); setAdminState(Manager::Get()->TransferPrimaryRole(homeId)); } else if (strcmp((char *)cp->conn_arg1, "reqnu") == 0) { if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 4) { uint8 node = strtol(((char *)cp->conn_arg2) + 4, NULL, 10); setAdminFunction("Request Network Update"); setAdminState(Manager::Get()->RequestNetworkUpdate(homeId, node)); } } else if (strcmp((char *)cp->conn_arg1, "reqnnu") == 0) { if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 4) { uint8 node = strtol(((char *)cp->conn_arg2) + 4, NULL, 10); setAdminFunction("Request Node Neighbor Update"); setAdminState(Manager::Get()->RequestNodeNeighborUpdate(homeId, node)); } } else if (strcmp((char *)cp->conn_arg1, "assrr") == 0) { if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 4) { uint8 node = strtol(((char *)cp->conn_arg2) + 4, NULL, 10); setAdminFunction("Assign Return Route"); setAdminState(Manager::Get()->AssignReturnRoute(homeId, node)); } } else if (strcmp((char *)cp->conn_arg1, "delarr") == 0) { if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 4) { uint8 node = strtol(((char *)cp->conn_arg2) + 4, NULL, 10); setAdminFunction("Delete All Return Routes"); setAdminState(Manager::Get()->DeleteAllReturnRoutes(homeId, node)); } } else if (strcmp((char *)cp->conn_arg1, "snif") == 0) { if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 4) { uint8 node = strtol(((char *)cp->conn_arg2) + 4, NULL, 10); setAdminFunction("Send Node Information"); setAdminState(Manager::Get()->SendNodeInformation(homeId, node)); } } else if (strcmp((char *)cp->conn_arg1, "reps") == 0) { if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 4) { uint8 node = strtol(((char *)cp->conn_arg2) + 4, NULL, 10); setAdminFunction("Replication Send"); setAdminState(Manager::Get()->ReplicationSend(homeId, node)); } } else if (strcmp((char *)cp->conn_arg1, "addbtn") == 0) { if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 4 && cp->conn_arg3 != NULL) { uint8 node = strtol(((char *)cp->conn_arg2) + 4, NULL, 10); uint8 button = strtol(((char *)cp->conn_arg3), NULL, 10); setAdminFunction("Add Button"); setAdminState(Manager::Get()->CreateButton(homeId, node, button)); } } else if (strcmp((char *)cp->conn_arg1, "delbtn") == 0) { if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 4 && cp->conn_arg3 != NULL) { uint8 node = strtol(((char *)cp->conn_arg2) + 4, NULL, 10); uint8 button = strtol(((char *)cp->conn_arg3), NULL, 10); setAdminFunction("Delete Button"); setAdminState(Manager::Get()->DeleteButton(homeId, node, button)); } } else if (strcmp((char *)cp->conn_arg1, "refreshnode") == 0) { if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 4) { uint8 node = strtol(((char *)cp->conn_arg2) + 4, NULL, 10); Manager::Get()->RefreshNodeInfo(homeId, node); } } return MHD_YES; } else ret = web_send_data(conn, EMPTY, MHD_HTTP_OK, false, false, NULL); // no free, no copy } else if (strcmp(url, "/nodepost.html") == 0) { if (*up_data_size != 0) { uint8 node; MHD_post_process(cp->conn_pp, up_data, *up_data_size); *up_data_size = 0; if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 4 && cp->conn_arg3 != NULL) { node = strtol(((char *)cp->conn_arg2) + 4, NULL, 10); if (strcmp((char *)cp->conn_arg1, "nam") == 0) { /* Node naming */ Manager::Get()->SetNodeName(homeId, node, (char *)cp->conn_arg3); } else if (strcmp((char *)cp->conn_arg1, "loc") == 0) { /* Node location */ Manager::Get()->SetNodeLocation(homeId, node, (char *)cp->conn_arg3); } else if (strcmp((char *)cp->conn_arg1, "pol") == 0) { /* Node polling */ } } return MHD_YES; } else ret = web_send_data(conn, EMPTY, MHD_HTTP_OK, false, false, NULL); // no free, no copy } else if (strcmp(url, "/grouppost.html") == 0) { if (*up_data_size != 0) { uint8 node; uint8 grp; MHD_post_process(cp->conn_pp, up_data, *up_data_size); *up_data_size = 0; if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 4 && cp->conn_arg3 != NULL && strlen((char *)cp->conn_arg3) > 0) { node = strtol(((char *)cp->conn_arg2) + 4, NULL, 10); grp = strtol((char *)cp->conn_arg3, NULL, 10); if (strcmp((char *)cp->conn_arg1, "group") == 0) { /* Group update */ pthread_mutex_lock(&nlock); nodes[node]->updateGroup(node, grp, (char *)cp->conn_arg4); pthread_mutex_unlock(&nlock); } } return MHD_YES; } else ret = web_send_data(conn, EMPTY, MHD_HTTP_OK, false, false, NULL); // no free, no copy } else if (strcmp(url, "/pollpost.html") == 0) { if (*up_data_size != 0) { uint8 node; MHD_post_process(cp->conn_pp, up_data, *up_data_size); *up_data_size = 0; if (cp->conn_arg2 != NULL && strlen((char *)cp->conn_arg2) > 0 && cp->conn_arg3 != NULL && strlen((char *)cp->conn_arg3) > 0 && cp->conn_arg4 != NULL && strlen((char *)cp->conn_arg4) > 0) { node = strtol(((char *)cp->conn_arg2) + 4, NULL, 10) + 1; if (strcmp((char *)cp->conn_arg1, "poll") == 0) { /* Poll update */ pthread_mutex_lock(&nlock); nodes[node]->updatePoll((char *)cp->conn_arg3, (char *)cp->conn_arg4); pthread_mutex_unlock(&nlock); } } return MHD_YES; } else ret = web_send_data(conn, EMPTY, MHD_HTTP_OK, false, false, NULL); // no free, no copy } else if (strcmp(url, "/savepost.html") == 0) { if (*up_data_size != 0) { MHD_post_process(cp->conn_pp, up_data, *up_data_size); *up_data_size = 0; if (strcmp((char *)cp->conn_arg1, "save") == 0) { /* Save config */ Manager::Get()->WriteConfig(homeId); pthread_mutex_lock(&glock); needsave = false; pthread_mutex_unlock(&glock); } return MHD_YES; } else ret = web_send_data(conn, EMPTY, MHD_HTTP_OK, false, false, NULL); // no free, no copy } else ret = web_send_data(conn, UNKNOWN, MHD_HTTP_NOT_FOUND, false, false, NULL); // no free, no copy return ret; } else return MHD_NO; } /* * web_free * Free up any allocated data after connection closed */ void Webserver::FreeEP (void *cls, struct MHD_Connection *conn, void **ptr, enum MHD_RequestTerminationCode code) { Webserver *ws = (Webserver *)cls; ws->Free(conn, ptr, code); } void Webserver::Free (struct MHD_Connection *conn, void **ptr, enum MHD_RequestTerminationCode code) { conninfo_t *cp = (conninfo_t *)*ptr; if (cp != NULL) { if (cp->conn_arg1 != NULL) free(cp->conn_arg1); if (cp->conn_arg2 != NULL) free(cp->conn_arg2); if (cp->conn_arg3 != NULL) free(cp->conn_arg3); if (cp->conn_arg4 != NULL) free(cp->conn_arg4); if (cp->conn_type == CON_POST) { MHD_destroy_post_processor(cp->conn_pp); } free(cp); *ptr = NULL; } } /* * Constructor * Start up the web server */ Webserver::Webserver (int const wport) : sortcol(COL_NODE), logbytes(0), adminstate(false) { fprintf(stderr, "webserver starting port %d\n", wport); port = wport; wdata = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG, port, NULL, NULL, &Webserver::HandlerEP, this, MHD_OPTION_NOTIFY_COMPLETED, Webserver::FreeEP, this, MHD_OPTION_END); if (wdata != NULL) { ready = true; } if (devname == NULL){ char* default_device; default_device = getenv("OZW_DEFAULT_DEVICE"); if ( (default_device != NULL) && (strcmp(default_device, "") != 0)){ devname = (char *)malloc(strlen(default_device) + 1); if (devname == NULL) { fprintf(stderr, "Out of memory open devname\n"); exit(1); } usb=false; strcpy(devname, default_device); Manager::Get()->AddDriver(devname); } } } /* * Destructor * Stop the web server */ Webserver::~Webserver () { if (wdata != NULL) { MHD_stop_daemon((MHD_Daemon *)wdata); wdata = NULL; ready = false; } pthread_mutex_lock(&glock); if (devname != NULL || usb) { Manager::Get()->RemoveDriver(devname ? devname : "HID Controller"); free(devname); devname = NULL; homeId = 0; usb = false; } pthread_mutex_unlock(&glock); } openzwave-controlpanel-0.2a+git20161006.a390f35/webserver.h000066400000000000000000000075601277534354000230170ustar00rootroot00000000000000//----------------------------------------------------------------------------- // // webserver.h // // C++ embedded web server class wrapper for libmicrohttpd // // Copyright (c) 2010 Greg Satz // All rights reserved. // // SOFTWARE NOTICE AND LICENSE // This work (including software, documents, or other related items) is being // provided by the copyright holders under the following license. By obtaining, // using and/or copying this work, you (the licensee) agree that you have read, // understood, and will comply with the following terms and conditions: // // Permission to use, copy, and distribute this software and its documentation, // without modification, for any purpose and without fee or royalty is hereby // granted, provided that you include the full text of this NOTICE on ALL // copies of the software and documentation or portions thereof. // // THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS // MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT // LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR // PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE // ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. // // COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR // CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR // DOCUMENTATION. // // The name and trademarks of copyright holders may NOT be used in advertising // or publicity pertaining to the software without specific, written prior // permission. Title to copyright in this software and any associated // documentation will at all times remain with copyright holders. //----------------------------------------------------------------------------- typedef enum { COL_NODE, COL_TYPE, COL_NAME, COL_LOCATION } coltype_t; typedef enum { CON_UNK, CON_GET, CON_POST } conntype_t; class Webserver { public: Webserver(int const); ~Webserver(); bool isReady() { return ready; } bool getAdminState() { return adminstate; } void setAdminState(bool st) { adminstate = st; } string getAdminFunction() { return adminfun; } void setAdminFunction (string msg) { adminfun = msg; } string getAdminMessage() { return adminmsg; } void setAdminMessage (string msg) { adminmsg = msg; } private: static int HandlerEP(void *cls, struct MHD_Connection *conn, const char *url, const char *method, const char *version, const char *up_data, size_t *up_data_size, void **ptr); int Handler(struct MHD_Connection *conn, const char *url, const char *method, const char *version, const char *up_data, size_t *up_data_size, void **ptr); static void FreeEP(void *cls, struct MHD_Connection *conn, void **ptr, enum MHD_RequestTerminationCode code); void Free(struct MHD_Connection *conn, void **ptr, enum MHD_RequestTerminationCode code); void web_get_groups(int i, TiXmlElement *ep); void web_get_values(int i, TiXmlElement *ep); int SendPollResponse(struct MHD_Connection *conn); int SendDeviceListResponse(struct MHD_Connection *conn); const char *SendSceneResponse(struct MHD_Connection *conn, const char *fun, const char *arg1, const char *arg2, const char *arg3); const char *SendTopoResponse(struct MHD_Connection *conn, const char *fun, const char *arg1, const char *arg2, const char *arg3); const char *SendStatResponse(struct MHD_Connection *conn, const char *fun, const char *arg1, const char *arg2, const char *arg3); const char *SendTestHealResponse(struct MHD_Connection *conn, const char *fun, const char *arg1, const char *arg2, const char *arg3); static bool usb; static char *devname; static unsigned short port; static bool ready; coltype_t sortcol; unsigned long logbytes; bool adminstate; string adminmsg; string adminfun; void *wdata; }; void web_controller_update (Driver::ControllerState cs, Driver::ControllerError err, void *ct); openzwave-controlpanel-0.2a+git20161006.a390f35/zwavelib.cpp000066400000000000000000000341371277534354000231710ustar00rootroot00000000000000//----------------------------------------------------------------------------- // // zwavelib.cpp // // OpenZWave Control Panel support routines // // Copyright (c) 2010 Greg Satz // All rights reserved. // // SOFTWARE NOTICE AND LICENSE // This work (including software, documents, or other related items) is being // provided by the copyright holders under the following license. By obtaining, // using and/or copying this work, you (the licensee) agree that you have read, // understood, and will comply with the following terms and conditions: // // Permission to use, copy, and distribute this software and its documentation, // without modification, for any purpose and without fee or royalty is hereby // granted, provided that you include the full text of this NOTICE on ALL // copies of the software and documentation or portions thereof. // // THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS // MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT // LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR // PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE // ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. // // COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR // CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR // DOCUMENTATION. // // The name and trademarks of copyright holders may NOT be used in advertising // or publicity pertaining to the software without specific, written prior // permission. Title to copyright in this software and any associated // documentation will at all times remain with copyright holders. //----------------------------------------------------------------------------- #include #include "ozwcp.h" const char *valueGenreStr (ValueID::ValueGenre vg) { switch (vg) { case ValueID::ValueGenre_Basic: return "basic"; case ValueID::ValueGenre_User: return "user"; case ValueID::ValueGenre_Config: return "config"; case ValueID::ValueGenre_System: return "system"; case ValueID::ValueGenre_Count: return "count"; } return "unknown"; } ValueID::ValueGenre valueGenreNum (char const *str) { if (strcmp(str, "basic") == 0) return ValueID::ValueGenre_Basic; else if (strcmp(str, "user") == 0) return ValueID::ValueGenre_User; else if (strcmp(str, "config") == 0) return ValueID::ValueGenre_Config; else if (strcmp(str, "system") == 0) return ValueID::ValueGenre_System; else if (strcmp(str, "count") == 0) return ValueID::ValueGenre_Count; else return (ValueID::ValueGenre)255; } const char *valueTypeStr (ValueID::ValueType vt) { switch (vt) { case ValueID::ValueType_Bool: return "bool"; case ValueID::ValueType_Byte: return "byte"; case ValueID::ValueType_Decimal: return "decimal"; case ValueID::ValueType_Int: return "int"; case ValueID::ValueType_List: return "list"; case ValueID::ValueType_Schedule: return "schedule"; case ValueID::ValueType_String: return "string"; case ValueID::ValueType_Short: return "short"; case ValueID::ValueType_Button: return "button"; case ValueID::ValueType_Raw: return "raw"; } return "unknown"; } ValueID::ValueType valueTypeNum (char const *str) { if (strcmp(str, "bool") == 0) return ValueID::ValueType_Bool; else if (strcmp(str, "byte") == 0) return ValueID::ValueType_Byte; else if (strcmp(str, "decimal") == 0) return ValueID::ValueType_Decimal; else if (strcmp(str, "int") == 0) return ValueID::ValueType_Int; else if (strcmp(str, "list") == 0) return ValueID::ValueType_List; else if (strcmp(str, "schedule") == 0) return ValueID::ValueType_Schedule; else if (strcmp(str, "short") == 0) return ValueID::ValueType_Short; else if (strcmp(str, "string") == 0) return ValueID::ValueType_String; else if (strcmp(str, "button") == 0) return ValueID::ValueType_Button; else if (strcmp(str, "raw") == 0) return ValueID::ValueType_Raw; else return (ValueID::ValueType)255; } const char *nodeBasicStr (uint8 basic) { switch (basic) { case 1: return "Controller"; case 2: return "Static Controller"; case 3: return "Slave"; case 4: return "Routing Slave"; } return "unknown"; } const char *cclassStr (uint8 cc) { switch (cc) { case 0x00: return "NO OPERATION"; case 0x20: return "BASIC"; case 0x21: return "CONTROLLER REPLICATION"; case 0x22: return "APPLICATION STATUS"; case 0x23: return "ZIP SERVICES"; case 0x24: return "ZIP SERVER"; case 0x25: return "SWITCH BINARY"; case 0x26: return "SWITCH MULTILEVEL"; case 0x27: return "SWITCH ALL"; case 0x28: return "SWITCH TOGGLE BINARY"; case 0x29: return "SWITCH TOGGLE MULTILEVEL"; case 0x2A: return "CHIMNEY FAN"; case 0x2B: return "SCENE ACTIVATION"; case 0x2C: return "SCENE ACTUATOR CONF"; case 0x2D: return "SCENE CONTROLLER CONF"; case 0x2E: return "ZIP CLIENT"; case 0x2F: return "ZIP ADV SERVICES"; case 0x30: return "SENSOR BINARY"; case 0x31: return "SENSOR MULTILEVEL"; case 0x32: return "METER"; case 0x33: return "COLOR"; case 0x34: return "ZIP ADV CLIENT"; case 0x35: return "METER PULSE"; case 0x38: return "THERMOSTAT HEATING"; case 0x40: return "THERMOSTAT MODE"; case 0x42: return "THERMOSTAT OPERATING STATE"; case 0x43: return "THERMOSTAT SETPOINT"; case 0x44: return "THERMOSTAT FAN MODE"; case 0x45: return "THERMOSTAT FAN STATE"; case 0x46: return "CLIMATE CONTROL SCHEDULE"; case 0x47: return "THERMOSTAT SETBACK"; case 0x4C: return "DOOR LOCK LOGGING"; case 0x4E: return "SCHEDULE ENTRY LOCK"; case 0x50: return "BASIC WINDOW COVERING"; case 0x51: return "MTP WINDOW COVERING"; case 0x56: return "CRC16 ENCAP"; case 0x5A: return "DEVICE RESET LOCALLY"; case 0x5B: return "CENTRAL SCENE"; case 0x5E: return "ZWAVE PLUS INFO"; case 0x60: return "MULTI INSTANCE"; case 0x62: return "DOOR LOCK"; case 0x63: return "USER CODE"; case 0x70: return "CONFIGURATION"; case 0x71: return "ALARM"; case 0x72: return "MANUFACTURER SPECIFIC"; case 0x73: return "POWERLEVEL"; case 0x75: return "PROTECTION"; case 0x76: return "LOCK"; case 0x77: return "NODE NAMING"; case 0x7A: return "FIRMWARE UPDATE MD"; case 0x7B: return "GROUPING NAME"; case 0x7C: return "REMOTE ASSOCIATION ACTIVATE"; case 0x7D: return "REMOTE ASSOCIATION"; case 0x80: return "BATTERY"; case 0x81: return "CLOCK"; case 0x82: return "HAIL"; case 0x84: return "WAKE UP"; case 0x85: return "ASSOCIATION"; case 0x86: return "VERSION"; case 0x87: return "INDICATOR"; case 0x88: return "PROPRIETARY"; case 0x89: return "LANGUAGE"; case 0x8A: return "TIME"; case 0x8B: return "TIME PARAMETERS"; case 0x8C: return "GEOGRAPHIC LOCATION"; case 0x8D: return "COMPOSITE"; case 0x8E: return "MULTI INSTANCE ASSOCIATION"; case 0x8F: return "MULTI CMD"; case 0x90: return "ENERGY PRODUCTION"; case 0x91: return "MANUFACTURER PROPRIETARY"; case 0x92: return "SCREEN MD"; case 0x93: return "SCREEN ATTRIBUTES"; case 0x94: return "SIMPLE AV CONTROL"; case 0x95: return "AV CONTENT DIRECTORY MD"; case 0x96: return "AV RENDERER STATUS"; case 0x97: return "AV CONTENT SEARCH MD"; case 0x98: return "SECURITY"; case 0x99: return "AV TAGGING MD"; case 0x9A: return "IP CONFIGURATION"; case 0x9B: return "ASSOCIATION COMMAND CONFIGURATION"; case 0x9C: return "SENSOR ALARM"; case 0x9D: return "SILENCE ALARM"; case 0x9E: return "SENSOR CONFIGURATION"; case 0xEF: return "MARK"; case 0xF0: return "NON INTEROPERABLE"; } return "UNKNOWN"; } uint8 cclassNum (char const *str) { if (strcmp(str, "NO OPERATION") == 0) return 0x00; else if (strcmp(str, "BASIC") == 0) return 0x20; else if (strcmp(str, "CONTROLLER REPLICATION") == 0) return 0x21; else if (strcmp(str, "APPLICATION STATUS") == 0) return 0x22; else if (strcmp(str, "ZIP SERVICES") == 0) return 0x23; else if (strcmp(str, "ZIP SERVER") == 0) return 0x24; else if (strcmp(str, "SWITCH BINARY") == 0) return 0x25; else if (strcmp(str, "SWITCH MULTILEVEL") == 0) return 0x26; else if (strcmp(str, "SWITCH ALL") == 0) return 0x27; else if (strcmp(str, "SWITCH TOGGLE BINARY") == 0) return 0x28; else if (strcmp(str, "SWITCH TOGGLE MULTILEVEL") == 0) return 0x29; else if (strcmp(str, "CHIMNEY FAN") == 0) return 0x2A; else if (strcmp(str, "SCENE ACTIVATION") == 0) return 0x2B; else if (strcmp(str, "SCENE ACTUATOR CONF") == 0) return 0x2C; else if (strcmp(str, "SCENE CONTROLLER CONF") == 0) return 0x2D; else if (strcmp(str, "ZIP CLIENT") == 0) return 0x2E; else if (strcmp(str, "ZIP ADV SERVICES") == 0) return 0x2F; else if (strcmp(str, "SENSOR BINARY") == 0) return 0x30; else if (strcmp(str, "SENSOR MULTILEVEL") == 0) return 0x31; else if (strcmp(str, "METER") == 0) return 0x32; else if (strcmp(str, "ZIP ADV SERVER") == 0) return 0x33; else if (strcmp(str, "ZIP ADV CLIENT") == 0) return 0x34; else if (strcmp(str, "METER PULSE") == 0) return 0x35; else if (strcmp(str, "THERMOSTAT HEATING") == 0) return 0x38; else if (strcmp(str, "THERMOSTAT MODE") == 0) return 0x40; else if (strcmp(str, "THERMOSTAT OPERATING STATE") == 0) return 0x42; else if (strcmp(str, "THERMOSTAT SETPOINT") == 0) return 0x43; else if (strcmp(str, "THERMOSTAT FAN MODE") == 0) return 0x44; else if (strcmp(str, "THERMOSTAT FAN STATE") == 0) return 0x45; else if (strcmp(str, "CLIMATE CONTROL SCHEDULE") == 0) return 0x46; else if (strcmp(str, "THERMOSTAT SETBACK") == 0) return 0x47; else if (strcmp(str, "DOOR LOCK LOGGING") == 0) return 0x4C; else if (strcmp(str, "SCHEDULE ENTRY LOCK") == 0) return 0x4E; else if (strcmp(str, "BASIC WINDOW COVERING") == 0) return 0x50; else if (strcmp(str, "MTP WINDOW COVERING") == 0) return 0x51; else if (strcmp(str, "MULTI INSTANCE") == 0) return 0x60; else if (strcmp(str, "DOOR LOCK") == 0) return 0x62; else if (strcmp(str, "USER CODE") == 0) return 0x63; else if (strcmp(str, "CONFIGURATION") == 0) return 0x70; else if (strcmp(str, "ALARM") == 0) return 0x71; else if (strcmp(str, "MANUFACTURER SPECIFIC") == 0) return 0x72; else if (strcmp(str, "POWERLEVEL") == 0) return 0x73; else if (strcmp(str, "PROTECTION") == 0) return 0x75; else if (strcmp(str, "LOCK") == 0) return 0x76; else if (strcmp(str, "NODE NAMING") == 0) return 0x77; else if (strcmp(str, "FIRMWARE UPDATE MD") == 0) return 0x7A; else if (strcmp(str, "GROUPING NAME") == 0) return 0x7B; else if (strcmp(str, "REMOTE ASSOCIATION ACTIVATE") == 0) return 0x7C; else if (strcmp(str, "REMOTE ASSOCIATION") == 0) return 0x7D; else if (strcmp(str, "BATTERY") == 0) return 0x80; else if (strcmp(str, "CLOCK") == 0) return 0x81; else if (strcmp(str, "HAIL") == 0) return 0x82; else if (strcmp(str, "WAKE UP") == 0) return 0x84; else if (strcmp(str, "ASSOCIATION") == 0) return 0x85; else if (strcmp(str, "VERSION") == 0) return 0x86; else if (strcmp(str, "INDICATOR") == 0) return 0x87; else if (strcmp(str, "PROPRIETARY") == 0) return 0x88; else if (strcmp(str, "LANGUAGE") == 0) return 0x89; else if (strcmp(str, "TIME") == 0) return 0x8A; else if (strcmp(str, "TIME PARAMETERS") == 0) return 0x8B; else if (strcmp(str, "GEOGRAPHIC LOCATION") == 0) return 0x8C; else if (strcmp(str, "COMPOSITE") == 0) return 0x8D; else if (strcmp(str, "MULTI INSTANCE ASSOCIATION") == 0) return 0x8E; else if (strcmp(str, "MULTI CMD") == 0) return 0x8F; else if (strcmp(str, "ENERGY PRODUCTION") == 0) return 0x90; else if (strcmp(str, "MANUFACTURER PROPRIETARY") == 0) return 0x91; else if (strcmp(str, "SCREEN MD") == 0) return 0x92; else if (strcmp(str, "SCREEN ATTRIBUTES") == 0) return 0x93; else if (strcmp(str, "SIMPLE AV CONTROL") == 0) return 0x94; else if (strcmp(str, "AV CONTENT DIRECTORY MD") == 0) return 0x95; else if (strcmp(str, "AV RENDERER STATUS") == 0) return 0x96; else if (strcmp(str, "AV CONTENT SEARCH MD") == 0) return 0x97; else if (strcmp(str, "SECURITY") == 0) return 0x98; else if (strcmp(str, "AV TAGGING MD") == 0) return 0x99; else if (strcmp(str, "IP CONFIGURATION") == 0) return 0x9A; else if (strcmp(str, "ASSOCIATION COMMAND CONFIGURATION") == 0) return 0x9B; else if (strcmp(str, "SENSOR ALARM") == 0) return 0x9C; else if (strcmp(str, "SILENCE ALARM") == 0) return 0x9D; else if (strcmp(str, "SENSOR CONFIGURATION") == 0) return 0x9E; else if (strcmp(str, "MARK") == 0) return 0xEF; else if (strcmp(str, "NON INTEROPERABLE") == 0) return 0xF0; else if (strcmp(str, "COLOR") == 0) return 0x33; else return 0xFF; } const char *controllerErrorStr (Driver::ControllerError err) { switch (err) { case Driver::ControllerError_None: return "None"; case Driver::ControllerError_ButtonNotFound: return "Button Not Found"; case Driver::ControllerError_NodeNotFound: return "Node Not Found"; case Driver::ControllerError_NotBridge: return "Not a Bridge"; case Driver::ControllerError_NotPrimary: return "Not Primary Controller"; case Driver::ControllerError_IsPrimary: return "Is Primary Controller"; case Driver::ControllerError_NotSUC: return "Not Static Update Controller"; case Driver::ControllerError_NotSecondary: return "Not Secondary Controller"; case Driver::ControllerError_NotFound: return "Not Found"; case Driver::ControllerError_Busy: return "Controller Busy"; case Driver::ControllerError_Failed: return "Failed"; case Driver::ControllerError_Disabled: return "Disabled"; case Driver::ControllerError_Overflow: return "Overflow"; } return "Unknown"; }