<?php
/***************************************************************************
* Copyright (C) 2008, Paul Lutus *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
*
* Required: ISY-26 software version 2.6.2 or newer.
*
* Version 3.3, 10/03/2008: Modified one line to accommodate changes in firmware >= 2.6.7
*
* Version 3.2, 03/02/2008: Added a filter for control and scene names beginning
* with a double-underscore, (like __name) to exclude them from the display.
*
* Version 3.1, 03/02/2008: At the suggestion of a reader who has dimmers, changed
* regex to accommodate intermediate "percent" states between on and off,
* translating them all into "On".
*
*/
// BEGIN user definitions
// Show controls table
$show_controls = true;
// Show scenes table
$show_scenes = true;
// ISY-26 access name or IP
$isy26_address = "pl-isy26";
// ISY-26 logon name
$logon_name="admin";
// ISY-26 access password
$password="(your password)";
// Specify a convenient font size
$font_size = "12pt";
// Specify how many cells in table rows
$control_row_length = 4;
$scene_row_length = 4;
// Specify seconds between page refreshes
$refresh_interval = 5;
// END user definitions
$controls_path = "/devices";
$scenes_path = "/scenes";
$change_path = "/change";
function wrap_tag($tag,$content,$extension = "") {
return "<$tag $extension>$content</$tag>";
}
function fetch_http($host,$port,$method,$path,$args = null)
{
global $logon_name,$password;
$buf = "";
$auth = base64_encode("$logon_name:$password");
try {
$fp = fsockopen($host, $port);
if ($args && $method == 'GET') {
$path .= '?' . $args;
}
$content = "$method $path HTTP/1.1\r\n";
$content .= "Host: $host\r\n";
$content .= "Authorization: Basic $auth\r\n";
$content .= "Content-type: application/x-www-form-urlencoded\r\n";
$content .= "Content-length: " . strlen($args) . "\r\n";
$content .= "Connection: close\r\n\r\n";
if ($args && $method == 'POST') {
$content .= $args;
}
fputs($fp, $content);
while (!feof($fp)) {
$buf .= fgets($fp,1024);
}
fclose($fp);
}
catch(Exception $e) {
echo 'Error: ', $e->getMessage(), "<br/>\n";
}
return $buf;
}
// fetch ISY-26 data page
function fetch_table_data($path) {
global $isy26_address;
global $control_row_length,$scene_row_length,$controls_path;
$result = "";
// Fetch a page using GET method
$data = fetch_http($isy26_address,80,"GET",$path);
// Did the page load fail (sometimes happens)?
if(!preg_match("%</table>%ism",$data)) {
$result = $data;
}
else {
// isolate the data table
$data = preg_replace("%^.*<table.*?>(.*)</table>.*%is","$1",$data);
// make an array of row contents
$n = preg_match_all("%<tr>(.*?)</tr>%is",$data,$matches,PREG_PATTERN_ORDER);
$row_array = $matches[1];
// drop header row
array_shift($row_array);
if($path == $controls_path) {
$row_length = $control_row_length;
// create an array of device data: name,address,state (on/off)
$device_array = preg_replace("%.*?node=(.*?)\">(.*?)</a></td>.*?(\d+ percent|On|Off).*$%","$2,$1,$3",$row_array);
}
else {
$row_length = $scene_row_length;
// create an array of scene data: name,address,-
$device_array = preg_replace("%.*?node=(.*?)\">(.*?)</a></td>.*$%","$2,$1,-",$row_array);
}
// sort controls and scenes by name
sort($device_array);
// build the display Web page
$pa = array();
$n = 0;
$row = "";
foreach ($device_array as $record) {
list($name,$address,$action) = split(",",$record);
// To exclude a control or scene from the display,
// prefix its name with a double-underscore
if(!preg_match("/^__/",$name)) {
$n++;
// all percentage values count as "On"
$on_state = ($action != "Off" && $action != "-");
$class = ($on_state && !preg_match("/All/",$name))?"spy":"spn";
$next_state = $on_state?"Off":"On";
if($path == $controls_path) {
$arg = $address . "," . $next_state . ",d";
$title = "Turn " . $next_state . " control '" . $name . "'";
$span = wrap_tag("span",$name,"class=\"$class\" title=\"$title\" onClick=\"clicked('$arg');\"");
$row .= wrap_tag("td",$span);
}
else {
$arg = $address . ",On,s";
$title = "Turn On scene '" . $name . "'";
$span = wrap_tag("span",$name . " On","class=\"$class\" title=\"$title\" onClick=\"clicked('$arg');\"");
$row .= wrap_tag("td",$span);
$arg = $address . ",Off,s";
$title = "Turn Off scene '" . $name . "'";
$span = wrap_tag("span",$name . " Off","class=\"$class\" title=\"$title\" onClick=\"clicked('$arg');\"");
$row .= wrap_tag("td",$span);
$n++;
}
}
// Test for row length
if(!($n % $row_length)) {
$pa[] = wrap_tag("tr",$row);
$row = "";
}
}
// Add possible incomplete row
if(strlen($row) > 0) {
while($n % $row_length) {
$row .= wrap_tag("td"," ");
$len = strlen($row);
$n++;
}
$pa[] = wrap_tag("tr",$row);
}
$result = join("\n",$pa);
$result = wrap_tag("table", "\n" . $result . "\n","class=\"bordered\" cellpadding=\"0\" cellspacing=\"0\"");
$title = ($path == $controls_path)?"Controls":"Scenes";
$title = wrap_tag("b",$title . ":");
$title = wrap_tag("p",$title);
$result = $title . $result;
}
return $result;
}
// if there is POST data from a prior control input, then submit it to the ISY-26.
// The POST info has this form: node (address), action:On or Off, type: [d]evice or [s]cene
if(array_key_exists('info',$_POST)) {
list($node,$action,$type) = split(",",$_POST['info']);
$out = "node=$node&submit=$action";
$result = fetch_http($isy26_address,80,"POST",$change_path,$out);
// If this was a scene command, delay for ISY-26 to update its state
if($type == "s") {
usleep(250000);
}
}
$page = "";
if($show_scenes) {
$page .= fetch_table_data($scenes_path);
}
if($show_controls) {
$page .= fetch_table_data($controls_path);
}
?>
<html>
<head>
<title>Lighting Controls</title>
<script type="text/javascript" language="JavaScript">
function clicked(s) {
document.form1.info.value = s;
document.form1.submit();
}
</script>
<style type="text/css">
body {
background-color:#e0f0ff;
}
table.bordered,table.bordered td {
border-collapse: collapse;
border: 1px solid #000000;
}
* {
font-size:<?php echo $font_size; ?>;
}
span:hover {
color:black;
text-decoration:underline;
}
.spy,.spn {
color:#808080;
padding:2px;
display:block;
text-align:center;
cursor:hand;
cursor:pointer;
}
/* color for "On" */
.spy {
background:#ffff80;
}
/* color for "Off" */
.spn {
background:#ffffff;
}
</style>
<meta http-equiv="refresh" content="<?php echo $refresh_interval; ?>"/>
</head>
<body>
<div align="center"><?php echo $page; ?></div>
<form name="form1" method="POST" action="<?php echo $_SERVER['PHP_SELF']; ?>">
<input type="hidden" name="info" value=""/>
</form>
</body>
</html>