Extended Demo

Important Note

This demo is setup as a demonstration of the MyURemote app. It is dedicated to the situation I have at home. It is by no means the ideal solution.

An ideal solution would dynamically generate the html pages. However, I do not have the time, nor the intention, to create a generic application that can be used by anyone. This means that the current demo has a very limited usability, but I hope you can use the javascript objects and the documentation below to quickly create your own application, corresponding to your needs.

Demo Setup

The extended demo is a real-life setup. In this setup, we have

  • a Global Caché (GC) extender (gc100-12), with 2 serial ports and 6 ir ports
  • a Logitech Squeezebox, connected to module ir1 of the GC extender
  • a Panasonic BDT-DMP110 blu-ray player, accessible through an undocumented Panasonic protocol
  • a Telenet Digicorder DC-AD2000, connected to module ir3 of the GC extender
  • a Bose V30 Home Cinema system, connected to the first serial port (ser1) of the GC extender. This Bose V30 system is master of a multi-room configuration with 2 zones and 5 rooms
    • Zone 1 : Living Room, Kitchen, Bedroom
    • Zone 2 : Office, Bathroom
  • a Philips 47PFL7606H/12 television, accessible through http (rest protocol)
  • a bTicino MyHome system, which allows to control lights and shutters
  • a Samsung SmartTV, accessible through an undocumented Samsung protocol

Some of the devices are input sources for the Bose V30 system :

  • Squeezebox
  • Blu-ray player
  • Digicorder
  • Television

The Philips Television can have other input sources as well. Only one is connected permanently (the Bose V30 system), but other sources can be added (PC media server, memory sticks, …)

Implementation

This demo is available at index.html, and introduces some new javascript objects that allow to build a more generic web application.

We need to explain some new concepts.

  • action : this is what you want a device to perform, e.g. “On/Off”, “Play”
  • action button : an action button is a button in your web interface that corresponds to a particular action, e.g. a “Play”-button. If you press the “Play”-button, you expect that the “Play” action is performed. But on which device ? That's were the context comes into play.
  • context : the same action can be performed on several devices. If you don't want to clutter your interface, you would like to limit the number of “Play” buttons to just one. You need to specify the context in which this action button will perform its action. The context thus corresponds to the active device.

e.g. if the active device is a blu-ray player, then the Play action will start playing the movie. If the active device is a media player, the Play action will start playing the playlist.

Html modifications

These concepts are translated into html through specific attributes, e.g.

<td class="oo_power" data-myuraction="OO_1"><div>&nbsp;</div></td>

The attributes are

  • Generic attributes
attribute info usage
data-myurcontext name of a context added to buttons that allow to switch the context
data-myuraction name of an action to perform, or an alias of this action added to an action button
data-myurcomponent name of a widget, to show or hide depending on the current context add to a div element grouping some action buttons. This is related to our simplified, static implementation of widgets.
data-myurparams additional parameters when performing this action added to some action buttons
data-myurcallbackparams additional parameters to pass to a callback function when performing this action added to some action buttons
  • Attributes for a Bose setup
attribute info usage
data-myurroom name of a room in a Bose multi-room setup added to buttons used dedicated to select a Bose room
data-myursource name of a source in a Bose setup added to source switching buttons, specifically for Bose
data-myurpower name of a source to power on/off in a Bose setup added to power buttons, specifically for Bose
  • Attributes for a bTicino MyHome system
attribute info usage
data-myurbticino name of a group of components in a bTicino MyHome setup added to a container containing lights or shutters

Javascript files

First we need to include some jquery files

<script src="js/jquery-1.7.2.min.js"></script>
<script src="js/jquery.base64.min.js"></script>
<script src="js/jquery.jsonp-2.1.2.js"></script>
<script src="js/jquery-ui-1.8.20.custom.min.js"></script>
file info mandatory ?
jquery-1.7.2.min.js jquery core file yes
jquery.base64.min.js used for base64 encoding only for Samsung SmartTV
jquery.jsonp-2.1.2.js used for communication with the MyURemote app yes
jquery-ui-1.8.20.custom.min.js used for layout components (tabs) yes (because our ui javascript object relies on this)
<!--  	Client-independent files
	Contain classes for communication with the MyURemote app 
-->
<script src="js/devices.js"></script>
<script src="js/myuremote.js"></script>
<script src="js/gc.js"></script>
<script src="js/rest.js"></script>
<script src="js/own.js"></script>
<script src="js/samsung.js"></script>
<script src="js/viera.js"></script>
 
<!-- 	Client-dependent configuration
	This will normally be created dynamically from a database
	The format is fixed. See the file clientconfig.js for a description of the format
 -->
<script src="js/clientconfig.js"></script>
file info
devices.js contains all commands and templates for known devices
myuremote.js defines javascript object responsible for working with contexts, sending commands, logging
gc.js javascript object for GlobalCaché communication
rest.js javascript object for rest and http communication (Philips, Panasonic blu-ray)
own.js javascript object for communication with an OpenWebNet gateway (bTicino)
samsung.js javascript object for communication with a Samsung SmartTv
viera.js javascript object for communication with a Panasonic Viera television
clientconfig.js defines javascript objects specific to the end-user, e.g. an object containing the ip and mac address of the current machine, an object containing all extender instances, an object containing all device configuration info
<!-- MyURemote ui setup -->
<script src="js/ui/myurbose.js"></script>
<script src="js/ui/myurbticino.js"></script>
<script src="js/ui/myuremote-ui.js"></script>
file info
myurbose.js javascript object used to initialize and interpret the html attributes data-myurroom, data-myursource, data-myurpower
myurbticino.js javascript object used to initialize and interpret the html attribute data-myurbticino
myuremote-ui.js javascript object used to initialize and interpret the html attributes data-myurcontext, data-myurcomponent, data-myuraction

Application Initialization

When the document is loaded, everything is initialized using the call

<script type="text/javascript">
	$(document).ready(function(){
		initMyURemoteUi();
	});
</script>		

This calls the initMyURemoteUi() function, defined in the file myuremote-ui.js.

What happens when the page is loaded ?

  1. The initMyURemoteUI function, defined in the file myuremote-ui.js, is called.

Because the content is defined in a tab, we need to initialize the content from the moment the tab has been loaded. This is done by defining a load handler for the tab.

function initMyURemoteUi() {
	$.myuremote.startProfile("init");
 
	$("#tabs").tabs({
	    load: function(event, ui) {
			initMyURemoteContexts();
	    }
	});
 
	$("#tabs").tabs('select',0);
 
	$.myuremote.endProfile("init");
};

Note that the first tab is explicitly selected after the load handler is defined. The load handler itself calls the initMyURemoteContexts function, found in the same file.

Perhaps I should avoid this dependency with the jquery tab component, i.e. move the initMyURemoteUi function out of the myuremote-ui.js file ?

Once the contents of the tab are loaded, the real work begins :

  • The initMyURemoteContexts function is called
    • We check if there is an OpenWebNet device or a Bose device, which needs further initialization (see further)
/**
 * Initialize the context buttons
 * For each button, we find the label as defined in the client_devices object, and add 
 * it as the value of the button.
 * Furthermore, we assign an onclick handler
 */
for (var device in client_devices) {
	if (client_devices[device].type == "BOSE V30") {
		// initialize the Bose system, if available
		var myurbose = new MyURBose(device);
		myurbose.init();
	} else if (client_devices[device].type == "OpenWebnet") {
		// initialize the bTicino domotics system, if available
		var myurbticino = new MyURBticino(device);
		myurbticino.init();
	}
}
  • We loop over all data-myuraction buttons to
    • bind the click event to a call to the $.myuremote.executeAction function. This is independent of the context, and has to be done only once.
$("[data-myuraction]").each(function(index, element) {
	$(this).bind('click', function(event) {
		$.myuremote.executeAction($(this).attr("data-myuraction"), $.parseJSON($(this).attr("data-myurparams")), $.parseJSON($(this).attr("data-myurcallbackparams")));
	});
});
  • We loop over all data-myurcontext buttons to
    • Set the label of the context button, based on the device label as defined in the client_devices object (clientconfig.js file)
    • Bind the click event to a context switch handler. This handler
      • finds out which widgets to show or hide (shows all groups with a value for the data-myurcomponent attribute in the components array for the device, as defined in the client_devices object (clientconfig.js file), and hides all groups with a value for the data-myurcomponent attribute not in this list
      • highlights the current context as selected
      • loops over all action buttons (buttons with the data-myuraction attribute) to hide all those that do not occur in the corresponding aliases array for the device, as defined in the client_devices object (clientconfig.js file)
$("[data-myurcontext]").each(function(index, element) {
	$.myuremote.startProfile("change context");
 
	var context = $(this).attr("data-myurcontext");
	$.myuremote.log("log", "setting up context " + context);
 
	var device = client_devices[context];
	$(this).prop('value',device.label);
	$(this).find("span").text(device.label);
 
	$(this).bind('click', function(event, ui) {
		var context = $(this).attr("data-myurcontext");
		$.myuremote.log("log", "clicked context " + context);
 
		$.myuremote.setContext(context);
		$("[data-myurcomponent]").each(function(index, element) {
			var component = $(this).attr("data-myurcomponent");
			if ($.myuremote.myuremoteContext.components.indexOf(component) >= 0) {
				$(this).show();
			} else {
				$(this).hide();
			}
		});
 
		// layout changes
		$("[data-myurcontext]").removeClass("selected");
		$("[data-myurcontext='"+context+"']").addClass("selected");
 
		// now we are going to evaluate all action buttons, and 
		// check which buttons to show or hide
		$("[data-myuraction]").each(function(index, element) {
			var action = $(this).attr("data-myuraction");
 
			// check if an alias has been defined for the action. 
			// if so, use the alias, otherwise continue with the action
			if ($.myuremote.myuremoteContext.aliases) {
				var alias = $.myuremote.checkAlias(action);
				if (alias) {
					action = alias;
				}
			}
 
			// check if the action is defined for this device, and enable/disable the button
			if (devices[$.myuremote.myuremoteContext.type][$.myuremote.myuremoteContext.technology] && devices[$.myuremote.myuremoteContext.type][$.myuremote.myuremoteContext.technology][action]) {
				$(this).removeClass("invisible");
				$(this).find("span").text(action);
			} else {
				$(this).addClass("invisible");
				$(this).find("span").text("");
			}
 
		});
	});
 
	$.myuremote.endProfile("change context");
});
  • We loop over all data-myurcomponent groups to hide all them before any context button has been clicked
$("[data-myurcomponent]").each(function(index, element) {
	$(this).hide();
});

Bose initialization

One of the steps described above consists of initializing the Bose system. When the function init() is called, the following happens (see the myurbose.js file) :

  • Loop over all data-myurroom attributes to
    • adjust the label of the corresponding button with the name found in the rooms object (client_devices object, file clientconfig.js)
    • define a click handler, to adjust the active room, and adjust the data-myurparams attribute for the right action buttons. This attribute contains the current room and source, and is used when the action button is clicked.
  • Loop over all data-myursource attributes to
    • adjust the label of the corresponding button with the name found in the sources object (client_devices object, file clientconfig.js)
    • define a click handler, to adjust the active source, and adjust the data-myurparams attribute for the right action buttons. This attribute contains the current room and source, and is used when the action button is clicked.
  • Loop over all data-myurpower attributes to
    • adjust the data-myurparams attribute for the right action buttons. This attribute contains the current room and source, and is used when the action button is clicked.

This initialization needs to be performed only once.

bTicino initialization

When the function init() is called, the following happens (see the myurbticino.js file) :

  • Loops over all containers (tables) having a data-myurbticino attribute. This attribute contains the component type (currently limited to lights and shutters)
  • For any such container, a row is added for each component of the given type, based on the templates defined here
this.templates = {
	"lights" : 	'<tr>'
			+	'<td>{0}</td><td><img src="img/default/light_on.png" data-myuraction="LIGHT" data-myurparams="[1,{1}]"></td>'
			+ 	'<td><img src="img/default/light_off.png" data-myuraction="LIGHT" data-myurparams="[0,{1}]"></td>'
			+	'<td>&nbsp;</td>'
			+	'</tr>',
	"shutters" : 	'<tr>'
			+	'<td>{0}</td><td><img src="img/default/shutter_up.png" data-myuraction="SHUTTER" data-myurparams="[1,{1}]"></td>'
			+	'<td><img src="img/default/shutter_stop.png" data-myuraction="SHUTTER" data-myurparams="[0,{1}]"></td>'
			+	'<td><img src="img/default/shutter_down.png" data-myuraction="SHUTTER" data-myurparams="[2,{1}]"></td>'
			+	'<td>&nbsp;</td>'
			+	'</tr>'
};

This initialization needs to be performed only once.

Back