<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="http://wiki.mios.com/skins/common/feed.css?303"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
		<id>http://wiki.mios.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Gengen</id>
		<title>MiOS - User contributions [en]</title>
		<link rel="self" type="application/atom+xml" href="http://wiki.mios.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Gengen"/>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Special:Contributions/Gengen"/>
		<updated>2026-04-28T20:38:14Z</updated>
		<subtitle>User contributions</subtitle>
		<generator>MediaWiki 1.19.8</generator>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_tabs</id>
		<title>Luup plugin tabs</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_tabs"/>
				<updated>2016-06-12T17:33:30Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Root Keys=&lt;br /&gt;
Refer to [[Luup_plugins:_Static_JSON_file|Root Keys]].&lt;br /&gt;
&lt;br /&gt;
=Tabs Key=&lt;br /&gt;
The '''Tabs''' key in the [[Luup plugins: Static JSON file|static JSON file]] performs two duties in the UI4 HTML interface: &lt;br /&gt;
&lt;br /&gt;
*It controls how the device appears on the '''Dashboard''' and '''Devices''' web pages.&lt;br /&gt;
*It controls how each tab appears in the the device's detailed dialog.&lt;br /&gt;
&lt;br /&gt;
The value of the '''Tabs''' key is a JSON array. Each array element is a JSON object (associative array). One for each Tab. &lt;br /&gt;
&lt;br /&gt;
==Tab fields==&lt;br /&gt;
These fields must be present for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object containing two keys:&lt;br /&gt;
*'''lang_tag''' (a string) is not displayed in the HTML interface but may be used by localized UIs.&lt;br /&gt;
*'''text''' (a string) is displayed in the tab's handle at the top of the dialog. &lt;br /&gt;
;'''Position''' &lt;br /&gt;
:A string which contains a number matching the relative position of this tab in relation to the other tabs. The leftmost tab has value &amp;quot;0&amp;quot;, the next tab &amp;quot;1&amp;quot;, and so on. &lt;br /&gt;
;'''TabType''' &lt;br /&gt;
:A string which describes how the content of the tab is to be generated. The value may be one of '''javascript''' or '''flash'''. These are described in the following sections.&lt;br /&gt;
&lt;br /&gt;
These fields are optional for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''ControlGroup'''&lt;br /&gt;
:Required only if the first tab is also used to display elements on the dashboard. See [[Luup_plugin_tabs#Dashboard_appearance|''Dashboard appearance'']] below.&lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
:Added in UI5.&lt;br /&gt;
&lt;br /&gt;
;'''Control'''&lt;br /&gt;
:An array of controls for each tab. Each control consists of:&lt;br /&gt;
*'''ControlType''' - the Control Types are described further below.&lt;br /&gt;
*'''no_control_panel''' - Set to 1 to hide this control in the control panel but still show it in the dashboard if '''ControlGroup''' is set. &lt;br /&gt;
*'''ControlGroup''' - a reference to the control's ControlGroup &lt;br /&gt;
*'''text_align''' - align left or right &lt;br /&gt;
*'''top''' - dashboard position in button units&lt;br /&gt;
*'''left''' - dashboard position in button units&lt;br /&gt;
&lt;br /&gt;
eg a slider sitting above a two by two array of buttons:&lt;br /&gt;
&lt;br /&gt;
slider: top = 0, left = 0&amp;lt;br\&amp;gt;&lt;br /&gt;
top left button: top = 1.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
top right button: top = 1.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
bot left button: top = 2.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
bot right button: top = 2.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TabType javascript===&lt;br /&gt;
The '''javascript''' tab type executes client-side JavaScript, producing an HTML fragment which forms the body of the tab. These additional keys are required in the tab JSON object: &lt;br /&gt;
&lt;br /&gt;
;'''ScriptName''' &lt;br /&gt;
:The name of the JavaScript file. Conventionally, plugin JavaScript files are named '''J_PluginName.js'''. &lt;br /&gt;
;'''Function''' &lt;br /&gt;
:The function inside the JavaScript file which will be called to produce the HTML.&lt;br /&gt;
&lt;br /&gt;
The JavaScript code has access to most Luup variables through the [[JavaScript API]]. &lt;br /&gt;
&lt;br /&gt;
===TabType flash===&lt;br /&gt;
The '''flash''' tab type creates a tab body with a simple page-description language. Each component of the page is placed into the body at the position specified. You can't place a '''Control''' on the dashboard unless you place it in a '''ControlGroup''', which in turn must be placed in a '''SceneGroup''': &lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
&lt;br /&gt;
''SceneGroup'' (added in UI5) is similar to a ControlGroup, but with some key differences: it is customizable (by setting its position and dimensions) and it defines the behavior of the buttons assigned to the same state variable. If two buttons in the same SceneGroup are assigned to the same variable, only one of them can be activated in the Scene Editor (radio buttons behavior). If two buttons assigned to the same variable are in different SceneGroups, both can be activated at the same time in the Scene Editor (checkboxes behavior). &lt;br /&gt;
&lt;br /&gt;
It has the following tags: &lt;br /&gt;
&lt;br /&gt;
*'''id''': A numeric value (positive integer numbers). &lt;br /&gt;
*'''top''': The offset from the device header. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''left''': The offset from the device icon. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''x''', '''y''': the dimensions of the SceneGroup. e.g If I have two horizontally aligned buttons in the SceneGroup, ''x'' will be ''2'' and ''y'' will be ''1''.&lt;br /&gt;
&lt;br /&gt;
A ''SceneGroup'' can contain one or more ''ControlGroups''. &lt;br /&gt;
&lt;br /&gt;
===Dashboard appearance===&lt;br /&gt;
The first array element in the '''Tabs''' field is special. If it is of type '''flash''' then a subset of the controls on the tab can be chosen to display on the UI4 dashboard.&lt;br /&gt;
&lt;br /&gt;
The dashboard box for a device has room for two rows of information and controls. These are called ''Control Groups''.&lt;br /&gt;
&lt;br /&gt;
The tab object must contain the key '''ControlGroup''', which specifies how the dashboard's two available rows of information are to be handled. '''ControlGroup''' is a JSON array containing JSON objects (associative arrays). Each object has the following keys:&lt;br /&gt;
&lt;br /&gt;
;'''id''' &lt;br /&gt;
:A string containing a number, &amp;quot;1&amp;quot; for the first object, &amp;quot;2&amp;quot; for the second, and so on. &lt;br /&gt;
;'''isSingle''' &lt;br /&gt;
:UI4: If this key is present and contains the value &amp;quot;1&amp;quot;, then this control group is combined with other control groups that have '''isSingle''' similarly set onto the same row. For an example of this, see the On/Off buttons of the Binary Switch and Dimmable Switch static JSON files. &lt;br /&gt;
:UI5: Since controls cannot be grouped in drop-down lists anymore, this tag has a different role in UI5. When isSingle is &amp;quot;1&amp;quot;, the buttons in the ControlGroup will have a background (e.g. the BinaryLight buttons). If isSingle is missing, the buttons won't have a background. That is the button shape is not shown; only the button text will show. &lt;br /&gt;
;'''type''' &lt;br /&gt;
:If this key is present and contains the value &amp;quot;info&amp;quot;, then the controls displayed on this row are not clickable. If this key is absent, buttons may be used in the control group.&lt;br /&gt;
&lt;br /&gt;
===Making a control appear on the dashboard===&lt;br /&gt;
All control types (label, variable, button, input, slider) can appear in the dashboard. However, by default, controls in the first tab's detail view do not appear in the dashboard. To make a control's text appear in the dashboard, add a '''ControlGroup''' to the control's JSON object. The value is a string containing a number, matching the '''id''' of one of the Tab's overall '''ControlGroup''' key. The dashboard will concatenate all of a control group's controls in the order they are presented in the '''Control''' array, and display them in a single row of text. &lt;br /&gt;
&lt;br /&gt;
===Keeping controls together===&lt;br /&gt;
Some controls may not make sense if presented individually (for instance, an input for an alarm PIN and a &amp;quot;disarm&amp;quot; button). To keep these controls together, add a '''ControlPair''' key to all of the controls that have to appear together. Use the same value (a number in a string) for all of them.&lt;br /&gt;
&lt;br /&gt;
==Control Types==&lt;br /&gt;
&lt;br /&gt;
The '''Control''' array contains JSON objects (associative arrays), one per item in the tab body. An item is either a static text label, a variable, an input field, or a button or slider. This is handled by setting the '''ControlType''' key on the object. &lt;br /&gt;
&lt;br /&gt;
===ControlType label===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''label''', a fixed label is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys:&lt;br /&gt;
*'''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. The key is optional.&lt;br /&gt;
*'''text''' is displayed as the content of the label. In UI5 on the &amp;quot;Dashboard&amp;quot;, the text is rendered by passing it in the &amp;quot;Title&amp;quot; attribute of a Div statement, so it can only be plain text, (as opposed to say HTML). However in the tabs, it is placed in a Div statement so HTML, such as a URL link, can be rendered. &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys, which are strings containing numbers (in pixels?). This controls where the label is placed on the tab. The label is left aligned.&lt;br /&gt;
*'''Top''': The offset from the top of tab writable area.&lt;br /&gt;
*'''Left''': The offset from the left side of the tab writable area.&lt;br /&gt;
*'''Width''': Window width of the label.&lt;br /&gt;
*'''Height''': Window height of the label.&lt;br /&gt;
&lt;br /&gt;
===ControlType variable===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''variable''', the contents of a Luup variable are displayed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with six keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the variable's value is placed in the tab), '''Service''' (a string containing the Service Id of the variable to be displayed), and '''Variable''' (a string containing the name of the variable to be displayed).&lt;br /&gt;
&lt;br /&gt;
===ControlType input===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''input''', a text box for the user to type a string is placed in the tab. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''ID''' &lt;br /&gt;
:A string which other controls (buttons) will use to refer to the string that the user has typed into this field. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object containing: &lt;br /&gt;
* '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers). This controls where the text box is placed in the tab.&lt;br /&gt;
* '''Service''', '''Variable''' (optional) A UPnP service and variable from which a default value is obtained&lt;br /&gt;
&lt;br /&gt;
===ControlType button===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''button''', a clickable button is placed at the specified coordinates. The button will appear selected (depressed) or unselected based on the value of a variable, permitting sets of buttons to act as radio buttons. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys: '''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. '''text''' is displayed as the text inside the button. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with up to seven keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), and (optionally) '''Value''' (a string, &amp;quot;1&amp;quot; if omitted). The specified variable's value controls whether the button appears selected (if the variable matches '''Value''') or not selected (if the variable doesn't match). &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching an input control on the page (for a user-entered parameter value).&lt;br /&gt;
&lt;br /&gt;
===ControlType slider===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''slider''', a draggable slider is placed at the specified coordinates. &lt;br /&gt;
&lt;br /&gt;
* '''Note:''' in UI5 and in UI7 (1.7.541 - release 7.0.5 and earlier), there can only be one horizontal slider (0% - 100%) per device. &lt;br /&gt;
&lt;br /&gt;
The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
* '''Display''' A JSON object with nine keys: &lt;br /&gt;
** '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab)&lt;br /&gt;
** '''Service''' (a string containing the Service Id of a variable)&lt;br /&gt;
** '''Variable''' (a string containing the name of a variable), &lt;br /&gt;
** '''MinValue''', '''MaxValue''' (both strings containing numbers, controlling what range of values the slider takes)&lt;br /&gt;
*** '''Note:''' in firmware 1.7.541 and earlier, MinValue and MaxValue must be 0 and 100 % respectively.&lt;br /&gt;
** '''ID''' (a string, used in the '''Command''' to refer to the position the user has dragged the slider to). The specified variable's value is used to display the slider's current position. &lt;br /&gt;
* '''Command''' A JSON object with three keys: &lt;br /&gt;
** '''Service''' (a string containing the service Id of an action that this device can perform)&lt;br /&gt;
** '''Action''' (a string containing the name of the action)&lt;br /&gt;
** '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key &lt;br /&gt;
*** '''Name''' for the parameter name&amp;lt;br /&amp;gt;and either &lt;br /&gt;
*** '''Value''' (for a fixed parameter value)&amp;lt;br /&amp;gt;or &lt;br /&gt;
*** '''ID''' matching the slider's ID (for a value matching what the user has dragged the slider to). &lt;br /&gt;
* '''ControlHeader'''  (Optional) If present, must have string value &amp;quot;1&amp;quot;. When set, the slider's label and value are displayed. Otherwise only the slider is shown. (TODO: This is from reverse-engineering the JavaScript code. Test this.)&lt;br /&gt;
&lt;br /&gt;
===ControlType slider_vertical===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_Heater1.json, D_HVAC_ZoneThermostat1.json&lt;br /&gt;
&lt;br /&gt;
Parameters for slider_vertical are similar to slider. The first slider_vertical is red and used for heating. The second slider_vertical is blue and used for cooling.&lt;br /&gt;
&lt;br /&gt;
===ControlType checkbox===&lt;br /&gt;
&lt;br /&gt;
:As of UI5 firmware 1.5.622, checkboxes are only half-baked and not usable in &amp;quot;flash&amp;quot; style tabs. If you need check-boxes, radio buttons, pop-up menus, etc. in your tab, you need to create a Javascript tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType image===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType image_player===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType js_button===&lt;br /&gt;
&lt;br /&gt;
:'''js_button''' is used for certain objects baked into the Vera UI such as security camera controls in D_DigitalSecurityCamera1.json&lt;br /&gt;
Th UI7, '''js_button''' can be used in a control panel tab, but not in the dashboard. Use the same parameters as for '''button''' and add:&lt;br /&gt;
* '''Function''' - The Javascript function to call within the js file declared in the '''AfterInit''' object. It must be defined as a global function rather than a module.function. The function is passed the device ID as an argument.&lt;br /&gt;
&lt;br /&gt;
=UI7 Updates=&lt;br /&gt;
&lt;br /&gt;
===ControlType line_break===&lt;br /&gt;
This is used to force the next control to appear below the previous one rather than alongside it. See [[Luup plugins: Static JSON file]].&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘line_break’&lt;br /&gt;
&lt;br /&gt;
===ControlType multi_state_button=== &lt;br /&gt;
This is a new type of control in UI7 and works as a “toggle” button. It is used for On/Off, Armed/Disarmed etc buttons.&lt;br /&gt;
&lt;br /&gt;
As an example, here is the definition from the .json file of the Door/Window sensor needed to create this kind of control:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
&amp;quot;ControlType&amp;quot;: &amp;quot;multi_state_button&amp;quot;,&lt;br /&gt;
&amp;quot;top&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
&amp;quot;left&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;states&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
                  &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                      &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_arm&amp;quot;,&lt;br /&gt;
                      &amp;quot;text&amp;quot;: &amp;quot;Arm&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                         &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                           &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                   {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                           ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;arm&amp;quot;&lt;br /&gt;
              },&lt;br /&gt;
              {&lt;br /&gt;
                     &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                           &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_bypass&amp;quot;,&lt;br /&gt;
                           &amp;quot;text&amp;quot;: &amp;quot;Bypass&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                            ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;bypass&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
        ]&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘multi_state_button’&lt;br /&gt;
* '''top''' - an integer specifying the row from the control group where the control will be placed&lt;br /&gt;
* '''left''' - an integer which specifies which column will the control be placed in&lt;br /&gt;
* '''states''' - an array of objects, each object representing a different state for the button; each object has the following properties:&lt;br /&gt;
** '''Lable''' - a JSON object with two keys:&lt;br /&gt;
*** '''lang_tag''' - a string used for localization&lt;br /&gt;
*** '''text''' - the string displayed if localization fails&lt;br /&gt;
** '''Display''' - a JSON object with the following keys:&lt;br /&gt;
*** '''Service''' - a string containing the service id&lt;br /&gt;
*** '''Variable''' - a string containing the name of the variable&lt;br /&gt;
*** '''Value''' - a number which represents the value of the state in which the control must be in order for this state to be displayed/marked as selected&lt;br /&gt;
** '''Command''' - a JSON object with the following properties:&lt;br /&gt;
*** '''Service''' - a string with the service id of the action which this state will perform&lt;br /&gt;
*** '''Action''' - a string representing the name of the action to be executed&lt;br /&gt;
*** '''Parameters''' - an array of JSON objects holding the parameters to pass to the action&lt;br /&gt;
* '''ControlCode''' - a string containing the control code of this control (it is recommended to be unique for each device type)&lt;br /&gt;
&lt;br /&gt;
As you can see, the multi_state_button is a merge resulted from two controls of type button.&lt;br /&gt;
&lt;br /&gt;
[[File:MotionSensor.jpg|image|left]]&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugins:_Static_JSON_file</id>
		<title>Luup plugins: Static JSON file</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugins:_Static_JSON_file"/>
				<updated>2016-06-12T17:05:58Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
&lt;br /&gt;
Starting in UI4, Luup plugins can specify a ''static JSON'' file.  This file describes how the plugin appears in the web interface.&lt;br /&gt;
&lt;br /&gt;
=What the static JSON file controls=&lt;br /&gt;
&lt;br /&gt;
* The icon used by the plugin in the dashboard.&lt;br /&gt;
* The text that displays in the one-to-two-row status message in the dashboard.&lt;br /&gt;
* Whether the icon changes depending on the value of a variable in the device (for example, a binary light's icon changes from lit to unlit).&lt;br /&gt;
* The tabs that appear in the device's detail dialog (when you click the wrench/spanner).&lt;br /&gt;
* The content of these tabs.&lt;br /&gt;
* The triggers or events that are available for the device in the &amp;quot;Triggers&amp;quot; or &amp;quot;Events&amp;quot; tag of a scene.&lt;br /&gt;
&lt;br /&gt;
=Referencing the static JSON file from the Device XML file=&lt;br /&gt;
&lt;br /&gt;
The device XML file (customarly '''D_PluginName.xml''') contains a reference to the static JSON file, in the '''staticJson''' element.  Place the element as a child of the '''device''' element:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
  &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
  &amp;lt;root xmlns=&amp;quot;urn:schemas-upnp-org:device-1-0&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;specVersion&amp;gt;&amp;lt;!-- ... --&amp;gt;&amp;lt;/specVersion&amp;gt;&lt;br /&gt;
    &amp;lt;device&amp;gt;&lt;br /&gt;
      &amp;lt;deviceType&amp;gt;urn:schemas-futzle-com:device:holidayvirtualswitch:1&amp;lt;/deviceType&amp;gt;&lt;br /&gt;
      &amp;lt;staticJson&amp;gt;D_HolidayVirtualSwitch1.json&amp;lt;/staticJson&amp;gt;&lt;br /&gt;
      &amp;lt;!-- ... --&amp;gt;&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Capitalization is important, as with all XML.&lt;br /&gt;
&lt;br /&gt;
The static JSON file is customarily called '''D_PluginName.json'''.&lt;br /&gt;
&lt;br /&gt;
=The static JSON file=&lt;br /&gt;
&lt;br /&gt;
The static JSON file is a single JSON object (associative array).&lt;br /&gt;
&lt;br /&gt;
==Root keys==&lt;br /&gt;
&lt;br /&gt;
These keys have been seen at the top level in static JSON files in the wild:&lt;br /&gt;
&lt;br /&gt;
; flashicon&lt;br /&gt;
: The device's icon, as a string.  Despite the name, the icon is no longer related to Adobe Flash in UI4 or UI5. See [[Luup plugin icons]].&lt;br /&gt;
; imgIconBody&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconDimmable&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconTurnable&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconMin&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconMax&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; halloIconsDir&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; inScene&lt;br /&gt;
: When included and set equal to one, it enables any buttons, etc located on the &amp;quot;dashboard box&amp;quot;, so they can be selected in the scene editor for use in scenes, rather than being grayed out.&lt;br /&gt;
; DisplayStatus&lt;br /&gt;
: A JSON object (associative array).  For devices where the icon changes based on a variable's value, describes which variable, and the range of values that produce different icon images.  See [[Luup plugin icons]].&lt;br /&gt;
; state_icons&lt;br /&gt;
: A JSON array.  For devices where the icon changes based on a variable's value, describes which icon files exist.  See [[Luup plugin icons]]. Used only in firmware 1.5.401 or later.&lt;br /&gt;
; doc_url&lt;br /&gt;
: A JSON object (associative array). In UI5, the only item in this object which is used is doc_page. It controls which page in docs5.mios.com is brought up when you click on the help ('''?''') icon in the device control panel.&lt;br /&gt;
; Tabs&lt;br /&gt;
: A JSON array.  Describes the tabs in the detail dialog, and what subset of the first tab appears in the dashboard.  See [[Luup plugin tabs]].&lt;br /&gt;
; ToggleButton&lt;br /&gt;
: ?????&lt;br /&gt;
; DeviceType&lt;br /&gt;
: A JSON string.  Must match the '''deviceType''' element in the corresponding device XML ('''D_PluginName.xml''') file.&lt;br /&gt;
; eventList&lt;br /&gt;
: A JSON object (associative array).  Describes the events that this plugin can produce.  Events are triggers that can fire off actions in scenes.  This key is required for UI4, and obsoleted in UI5. For UI5, use the eventList2 tag, which has a different format. For compatibility with both UI4 and UI5 you need the same information in both keys. See [[UI4 UI5 Migration]] for more information.&lt;br /&gt;
; eventList2&lt;br /&gt;
: see eventList, above.&lt;br /&gt;
; sceneList&lt;br /&gt;
: A JSON object (associative array).  Describes the actions that this plugin can perform (for instance, as the action in a scene). This key is not required and is ignored in UI5, however it is needed for UI4 compatibility. See [[UI4 UI5 Migration]] for more information about its format.&lt;br /&gt;
&lt;br /&gt;
= UI7 Updates =&lt;br /&gt;
&lt;br /&gt;
* '''default_icon'''&lt;br /&gt;
: replace the old &amp;quot;flashicon&amp;quot; from UI5. See [[Luup plugin icons]]&lt;br /&gt;
* '''state_icons'''&lt;br /&gt;
: state_icons mechanism has changed in UI7. Please see [[Luup plugin icons]]&lt;br /&gt;
* '''TopNavigationTab''' = '''top_navigation_tab'''&lt;br /&gt;
&lt;br /&gt;
In device cpanel, tabs can be placed either in the top part or in the bottom. By default they are put in the bottom part. You can mark your tabs with a special flag so that they can be placed in top navigation bar of the cpanel.&lt;br /&gt;
If two or more tabs are marked with ‘'''TabType=”flash”'''’, only the first one found will be placed in top navigation bar. If you want all of them to be placed there, you have to add ‘'''top_navigation_tab'''’ to each one.&lt;br /&gt;
Let’s assume you have a plugin named ‘MyPlugin’ and you want to put a button in the top navigation bar, which, when clicked, will display a message in the cpanel.&lt;br /&gt;
&lt;br /&gt;
* Add the following lines to the .json device file (D_MyPlugin.json), in the Tabs section:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
          &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                &amp;quot;lang_tag&amp;quot;: &amp;quot;About&amp;quot;,&lt;br /&gt;
                &amp;quot;text&amp;quot;: &amp;quot;About&amp;quot;&lt;br /&gt;
          },&lt;br /&gt;
              &amp;quot;TopNavigationTab&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
          &amp;quot;Position&amp;quot;: &amp;quot;4&amp;quot;,&lt;br /&gt;
          &amp;quot;TabType&amp;quot;: &amp;quot;javascript&amp;quot;,&lt;br /&gt;
          &amp;quot;ScriptName&amp;quot;: &amp;quot;J_MyPlugin.js&amp;quot;,&lt;br /&gt;
          &amp;quot;Function&amp;quot;: &amp;quot;MyPlugin.about&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the property ‘'''TopNavigationTab'''’ set to the value of ‘1’. This is the line that specifies where to place the tab in cpanel. If you set a value different than 1, the tab will be placed in the bottom part of the cpanel, so always use ‘1’ if you want the tab in the top bar. You can also use ‘'''top_navigation_tab'''’.&lt;br /&gt;
&lt;br /&gt;
* Add the following lines of code to your .js file (J_MyPlugin.js):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var MyPlugin = (function(api){&lt;br /&gt;
    return {&lt;br /&gt;
        about: function() {&lt;br /&gt;
            try {              &lt;br /&gt;
                var html = '&amp;lt;div&amp;gt;This is all about me !&amp;lt;/div&amp;gt;';&lt;br /&gt;
             api.setCpanelContent(html);&lt;br /&gt;
            } catch (e) {&lt;br /&gt;
                Utils.logError('Error in MyPlugin.about(): ' + e);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
})(api);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Perform a lu reload, enter device cpanel and click on ‘About’ button (which is in the top navigation bar of the cpanel) and the result will be something like this:&lt;br /&gt;
&lt;br /&gt;
[[File:TopNavigationTab.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
== Adding Javascript code to Flash tabs ==&lt;br /&gt;
&lt;br /&gt;
In UI7, Each tab of type flash in a device json can have a property called ‘AfterInit’ which can be&lt;br /&gt;
used to execute some Javascript code. This way a developer can “insert” custom defined&lt;br /&gt;
content directly in a cpanel in the Control tab.&lt;br /&gt;
&lt;br /&gt;
[[File:AfterInit.png|image|center|AfterInit]]&lt;br /&gt;
&lt;br /&gt;
This image is taken from the MiOS implementation of the PhilipsHue plugin . As you can see the part&lt;br /&gt;
which is situated below the dashed line is inserted via Javascript code. In order to achieve this,&lt;br /&gt;
the following json was used for the ‘Control’ tab:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
          &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                    &amp;quot;lang_tag&amp;quot;: &amp;quot;control&amp;quot;,&lt;br /&gt;
                    &amp;quot;text&amp;quot;: &amp;quot;Control&amp;quot;&lt;br /&gt;
          },&lt;br /&gt;
          &amp;quot;Position&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
          &amp;quot;TabType&amp;quot;: &amp;quot;flash&amp;quot;,&lt;br /&gt;
          &amp;quot;TopNavigationTab&amp;quot;: 1,&lt;br /&gt;
          &amp;quot;AfterInit&amp;quot;: {&lt;br /&gt;
                    &amp;quot;ScriptName&amp;quot;: &amp;quot;J_PhilipsHueLamp2.js&amp;quot;,&lt;br /&gt;
                    &amp;quot;Function&amp;quot;: &amp;quot;PhilipsHueLamp2.color_picker&amp;quot;&lt;br /&gt;
          } ,&lt;br /&gt;
          ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''TopNavigationTab'''&lt;br /&gt;
: Must be set to 1 for AfterInit to work&lt;br /&gt;
* '''AfterInit'''&lt;br /&gt;
: object which contains two properties:&lt;br /&gt;
* '''ScriptName'''&lt;br /&gt;
: specifies the .js file where the function resides; the .js file is loaded when the user enters the cpanel&lt;br /&gt;
* '''Function'''&lt;br /&gt;
: specifies the name of the function to execute&lt;br /&gt;
&lt;br /&gt;
== Position device controls for generic devices ==&lt;br /&gt;
&lt;br /&gt;
In UI7, devices are split into two categories: specific and generic. Specific devices are those which have a predefined design (like binary light, thermostat, dimmable etc.). Generic devices are those created by plugin developers.&lt;br /&gt;
&lt;br /&gt;
=== Positioning controls in ‘device view’ ===&lt;br /&gt;
&lt;br /&gt;
The controls for generic devices are placed inside the device container based on the order in which are found in device .json file and so, each control is drawn to the right of the previous one. If a control doesn’t fit in the remaining space it will be drawn on the following line. This rule will guarantee that the look of a device will be the same on the Web, but also on the mobile apps.&lt;br /&gt;
&lt;br /&gt;
In order to achieve this, but also to maintain backward compatibility with UI5, the developer can add new properties to the device .json, but doesn’t have to change the existing ones.&lt;br /&gt;
&lt;br /&gt;
In the WEB UI, each control is contained in a div element which is positioned using float: left CSS property.&lt;br /&gt;
&lt;br /&gt;
The size of the controls can be altered using properties like ‘HorizontalMultiplier’ which multiplies the default width of a control with a given float number.&lt;br /&gt;
&lt;br /&gt;
To force a control to be positioned on the next line, a special device control was created: ‘line_break’.&lt;br /&gt;
&lt;br /&gt;
To insert a space between two controls, the developer can use the newly created device control: ‘spacer’, which also accepts ‘HorizontalMultiplier’ property.&lt;br /&gt;
&lt;br /&gt;
=== Tutorial ===&lt;br /&gt;
&lt;br /&gt;
We will start with a device that contains no controls.&lt;br /&gt;
&lt;br /&gt;
* First, we need a SceneGroup with id 1 and a ControlGroup with id 1 which belongs to the previous added scene group (this is the same as in UI5)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;SceneGroup&amp;quot;: [&lt;br /&gt;
   {&lt;br /&gt;
       &amp;quot;id&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;top&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;left&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
       &amp;quot;x&amp;quot;: &amp;quot;2&amp;quot;,&lt;br /&gt;
       &amp;quot;y&amp;quot;: &amp;quot;3&amp;quot;&lt;br /&gt;
   }&lt;br /&gt;
],&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: [&lt;br /&gt;
   {&lt;br /&gt;
       &amp;quot;id&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;isSingle&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;scenegroup&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
   }&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Then, add a control of type ‘button’ with a label: ‘My First Button’. Create it using the same definition as in UI5. Be sure to set the ControlGroup of the button to be equal to 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;my_button&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;my_first_button&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;My First Button&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* By now, your device will look something like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice1.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Notice that the text doesn’t fit properly inside the button. Add &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;2&amp;quot; property to your control definition. This will set the width of the button to double the default size. By now, your device will look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice2.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Add 6 more buttons using the same template as above, but don’t set the “HorizontalMultiplier” property. Use labels: ‘Button 1’, ‘Button 2’, … ‘Button 6’. It will look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice3.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Notice that the latest 6 buttons will be added to the right of the previously added ones; if the button doesn’t fit in the remaining space it will be placed on the next line&lt;br /&gt;
&lt;br /&gt;
* We decide to move buttons 1-3 on a separate line and buttons 4-6 on another line; to do so, add a control of type ‘line_break’ at the end of ‘My First Button’ and at the end of ‘Button 3’. The syntax is the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;line_break&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* And the result is like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice4.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* If you want some space between the buttons you can use ‘spacer’, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* The result:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice5.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Here is the full definition of the Control array (highlighted with blue you can see UI7 specific properties):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;2&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;my_button&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;my_first_button&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;My First Button&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;line_break&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_1&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button1&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button1&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;21&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;21&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_2&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button2&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button2&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;22&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;22&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_3&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button3&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button3&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;23&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;23&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;line_break&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_4&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button4&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button4&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;24&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;24&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_5&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button5&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button5&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;25&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;25&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_6&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button6&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button6&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;26&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;26&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugins:_Static_JSON_file</id>
		<title>Luup plugins: Static JSON file</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugins:_Static_JSON_file"/>
				<updated>2016-03-10T16:35:54Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
&lt;br /&gt;
Starting in UI4, Luup plugins can specify a ''static JSON'' file.  This file describes how the plugin appears in the web interface.&lt;br /&gt;
&lt;br /&gt;
=What the static JSON file controls=&lt;br /&gt;
&lt;br /&gt;
* The icon used by the plugin in the dashboard.&lt;br /&gt;
* The text that displays in the one-to-two-row status message in the dashboard.&lt;br /&gt;
* Whether the icon changes depending on the value of a variable in the device (for example, a binary light's icon changes from lit to unlit).&lt;br /&gt;
* The tabs that appear in the device's detail dialog (when you click the wrench/spanner).&lt;br /&gt;
* The content of these tabs.&lt;br /&gt;
* The triggers or events that are available for the device in the &amp;quot;Triggers&amp;quot; or &amp;quot;Events&amp;quot; tag of a scene.&lt;br /&gt;
&lt;br /&gt;
=Referencing the static JSON file from the Device XML file=&lt;br /&gt;
&lt;br /&gt;
The device XML file (customarly '''D_PluginName.xml''') contains a reference to the static JSON file, in the '''staticJson''' element.  Place the element as a child of the '''device''' element:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
  &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
  &amp;lt;root xmlns=&amp;quot;urn:schemas-upnp-org:device-1-0&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;specVersion&amp;gt;&amp;lt;!-- ... --&amp;gt;&amp;lt;/specVersion&amp;gt;&lt;br /&gt;
    &amp;lt;device&amp;gt;&lt;br /&gt;
      &amp;lt;deviceType&amp;gt;urn:schemas-futzle-com:device:holidayvirtualswitch:1&amp;lt;/deviceType&amp;gt;&lt;br /&gt;
      &amp;lt;staticJson&amp;gt;D_HolidayVirtualSwitch1.json&amp;lt;/staticJson&amp;gt;&lt;br /&gt;
      &amp;lt;!-- ... --&amp;gt;&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Capitalization is important, as with all XML.&lt;br /&gt;
&lt;br /&gt;
The static JSON file is customarily called '''D_PluginName.json'''.&lt;br /&gt;
&lt;br /&gt;
=The static JSON file=&lt;br /&gt;
&lt;br /&gt;
The static JSON file is a single JSON object (associative array).&lt;br /&gt;
&lt;br /&gt;
==Root keys==&lt;br /&gt;
&lt;br /&gt;
These keys have been seen at the top level in static JSON files in the wild:&lt;br /&gt;
&lt;br /&gt;
; flashicon&lt;br /&gt;
: The device's icon, as a string.  Despite the name, the icon is no longer related to Adobe Flash in UI4 or UI5. See [[Luup plugin icons]].&lt;br /&gt;
; imgIconBody&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconDimmable&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconTurnable&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconMin&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconMax&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; halloIconsDir&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; inScene&lt;br /&gt;
: When included and set equal to one, it enables any buttons, etc located on the &amp;quot;dashboard box&amp;quot;, so they can be selected in the scene editor for use in scenes, rather than being grayed out.&lt;br /&gt;
; DisplayStatus&lt;br /&gt;
: A JSON object (associative array).  For devices where the icon changes based on a variable's value, describes which variable, and the range of values that produce different icon images.  See [[Luup plugin icons]].&lt;br /&gt;
; state_icons&lt;br /&gt;
: A JSON array.  For devices where the icon changes based on a variable's value, describes which icon files exist.  See [[Luup plugin icons]]. Used only in firmware 1.5.401 or later.&lt;br /&gt;
; doc_url&lt;br /&gt;
: A JSON object (associative array). In UI5, the only item in this object which is used is doc_page. It controls which page in docs5.mios.com is brought up when you click on the help ('''?''') icon in the device control panel.&lt;br /&gt;
; Tabs&lt;br /&gt;
: A JSON array.  Describes the tabs in the detail dialog, and what subset of the first tab appears in the dashboard.  See [[Luup plugin tabs]].&lt;br /&gt;
; ToggleButton&lt;br /&gt;
: ?????&lt;br /&gt;
; DeviceType&lt;br /&gt;
: A JSON string.  Must match the '''deviceType''' element in the corresponding device XML ('''D_PluginName.xml''') file.&lt;br /&gt;
; eventList&lt;br /&gt;
: A JSON object (associative array).  Describes the events that this plugin can produce.  Events are triggers that can fire off actions in scenes.  This key is required for UI4, and obsoleted in UI5. For UI5, use the eventList2 tag, which has a different format. For compatibility with both UI4 and UI5 you need the same information in both keys. See [[UI4 UI5 Migration]] for more information.&lt;br /&gt;
; eventList2&lt;br /&gt;
: see eventList, above.&lt;br /&gt;
; sceneList&lt;br /&gt;
: A JSON object (associative array).  Describes the actions that this plugin can perform (for instance, as the action in a scene). This key is not required and is ignored in UI5, however it is needed for UI4 compatibility. See [[UI4 UI5 Migration]] for more information about its format.&lt;br /&gt;
&lt;br /&gt;
= UI7 Updates =&lt;br /&gt;
&lt;br /&gt;
* '''default_icon'''&lt;br /&gt;
: replace the old &amp;quot;flashicon&amp;quot; from UI5. See [[Luup plugin icons]]&lt;br /&gt;
* '''state_icons'''&lt;br /&gt;
: state_icons mechanism has changed in UI7. Please see [[Luup plugin icons]]&lt;br /&gt;
* '''TopNavigationTab''' = '''top_navigation_tab'''&lt;br /&gt;
&lt;br /&gt;
In device cpanel, tabs can be placed either in the top part or in the bottom. By default they are put in the bottom part. You can mark your tabs with a special flag so that they can be placed in top navigation bar of the cpanel.&lt;br /&gt;
If two or more tabs are marked with ‘'''TabType=”flash”'''’, only the first one found will be placed in top navigation bar. If you want all of them to be placed there, you have to add ‘'''top_navigation_tab'''’ to each one.&lt;br /&gt;
Let’s assume you have a plugin named ‘MyPlugin’ and you want to put a button in the top navigation bar, which, when clicked, will display a message in the cpanel.&lt;br /&gt;
&lt;br /&gt;
* Add the following lines to the .json device file (D_MyPlugin.json), in the Tabs section:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
          &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                &amp;quot;lang_tag&amp;quot;: &amp;quot;About&amp;quot;,&lt;br /&gt;
                &amp;quot;text&amp;quot;: &amp;quot;About&amp;quot;&lt;br /&gt;
          },&lt;br /&gt;
              &amp;quot;TopNavigationTab&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
          &amp;quot;Position&amp;quot;: &amp;quot;4&amp;quot;,&lt;br /&gt;
          &amp;quot;TabType&amp;quot;: &amp;quot;javascript&amp;quot;,&lt;br /&gt;
          &amp;quot;ScriptName&amp;quot;: &amp;quot;J_MyPlugin.js&amp;quot;,&lt;br /&gt;
          &amp;quot;Function&amp;quot;: &amp;quot;MyPlugin.about&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the property ‘'''TopNavigationTab'''’ set to the value of ‘1’. This is the line that specifies where to place the tab in cpanel. If you set a value different than 1, the tab will be placed in the bottom part of the cpanel, so always use ‘1’ if you want the tab in the top bar. You can also use ‘'''top_navigation_tab'''’.&lt;br /&gt;
&lt;br /&gt;
* Add the following lines of code to your .js file (J_MyPlugin.js):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var MyPlugin = (function(api){&lt;br /&gt;
    return {&lt;br /&gt;
        about: function() {&lt;br /&gt;
            try {              &lt;br /&gt;
                var html = '&amp;lt;div&amp;gt;This is all about me !&amp;lt;/div&amp;gt;';&lt;br /&gt;
             api.setCpanelContent(html);&lt;br /&gt;
            } catch (e) {&lt;br /&gt;
                Utils.logError('Error in MyPlugin.about(): ' + e);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
})(api);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Perform a lu reload, enter device cpanel and click on ‘About’ button (which is in the top navigation bar of the cpanel) and the result will be something like this:&lt;br /&gt;
&lt;br /&gt;
[[File:TopNavigationTab.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
== Adding Javascript code to Flash tabs ==&lt;br /&gt;
&lt;br /&gt;
In UI7, Each tab of type flash in a device json can have a property called ‘AfterInit’ which can be&lt;br /&gt;
used to execute some Javascript code. This way a developer can “insert” custom defined&lt;br /&gt;
content directly in a cpanel in the Control tab.&lt;br /&gt;
&lt;br /&gt;
[[File:AfterInit.png|image|center|AfterInit]]&lt;br /&gt;
&lt;br /&gt;
This image is taken from the MiOS implementation of the PhilipsHue plugin . As you can see the part&lt;br /&gt;
which is situated below the dashed line is inserted via Javascript code. In order to achieve this,&lt;br /&gt;
the following json was used for the ‘Control’ tab:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
          &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                    &amp;quot;lang_tag&amp;quot;: &amp;quot;control&amp;quot;,&lt;br /&gt;
                    &amp;quot;text&amp;quot;: &amp;quot;Control&amp;quot;&lt;br /&gt;
          },&lt;br /&gt;
          &amp;quot;Position&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
          &amp;quot;TabType&amp;quot;: &amp;quot;flash&amp;quot;,&lt;br /&gt;
          &amp;quot;AfterInit&amp;quot;: {&lt;br /&gt;
                    &amp;quot;ScriptName&amp;quot;: &amp;quot;J_PhilipsHueLamp2.js&amp;quot;,&lt;br /&gt;
                    &amp;quot;Function&amp;quot;: &amp;quot;PhilipsHueLamp2.color_picker&amp;quot;&lt;br /&gt;
          } ,&lt;br /&gt;
          ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''AfterInit'''&lt;br /&gt;
: object which contains two properties:&lt;br /&gt;
* '''ScriptName'''&lt;br /&gt;
: specifies the .js file where the function resides; the .js file is loaded when the user enters the cpanel&lt;br /&gt;
* '''Function'''&lt;br /&gt;
: specifies the name of the function to execute&lt;br /&gt;
&lt;br /&gt;
== Position device controls for generic devices ==&lt;br /&gt;
&lt;br /&gt;
In UI7, devices are split into two categories: specific and generic. Specific devices are those which have a predefined design (like binary light, thermostat, dimmable etc.). Generic devices are those created by plugin developers.&lt;br /&gt;
&lt;br /&gt;
=== Positioning controls in ‘device view’ ===&lt;br /&gt;
&lt;br /&gt;
The controls for generic devices are placed inside the device container based on the order in which are found in device .json file and so, each control is drawn to the right of the previous one. If a control doesn’t fit in the remaining space it will be drawn on the following line. This rule will guarantee that the look of a device will be the same on the Web, but also on the mobile apps.&lt;br /&gt;
&lt;br /&gt;
In order to achieve this, but also to maintain backward compatibility with UI5, the developer can add new properties to the device .json, but doesn’t have to change the existing ones.&lt;br /&gt;
&lt;br /&gt;
In the WEB UI, each control is contained in a div element which is positioned using float: left CSS property.&lt;br /&gt;
&lt;br /&gt;
The size of the controls can be altered using properties like ‘HorizontalMultiplier’ which multiplies the default width of a control with a given float number.&lt;br /&gt;
&lt;br /&gt;
To force a control to be positioned on the next line, a special device control was created: ‘line_break’.&lt;br /&gt;
&lt;br /&gt;
To insert a space between two controls, the developer can use the newly created device control: ‘spacer’, which also accepts ‘HorizontalMultiplier’ property.&lt;br /&gt;
&lt;br /&gt;
=== Tutorial ===&lt;br /&gt;
&lt;br /&gt;
We will start with a device that contains no controls.&lt;br /&gt;
&lt;br /&gt;
* First, we need a SceneGroup with id 1 and a ControlGroup with id 1 which belongs to the previous added scene group (this is the same as in UI5)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;SceneGroup&amp;quot;: [&lt;br /&gt;
   {&lt;br /&gt;
       &amp;quot;id&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;top&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;left&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
       &amp;quot;x&amp;quot;: &amp;quot;2&amp;quot;,&lt;br /&gt;
       &amp;quot;y&amp;quot;: &amp;quot;3&amp;quot;&lt;br /&gt;
   }&lt;br /&gt;
],&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: [&lt;br /&gt;
   {&lt;br /&gt;
       &amp;quot;id&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;isSingle&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;scenegroup&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
   }&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Then, add a control of type ‘button’ with a label: ‘My First Button’. Create it using the same definition as in UI5. Be sure to set the ControlGroup of the button to be equal to 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;my_button&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;my_first_button&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;My First Button&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* By now, your device will look something like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice1.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Notice that the text doesn’t fit properly inside the button. Add &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;2&amp;quot; property to your control definition. This will set the width of the button to double the default size. By now, your device will look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice2.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Add 6 more buttons using the same template as above, but don’t set the “HorizontalMultiplier” property. Use labels: ‘Button 1’, ‘Button 2’, … ‘Button 6’. It will look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice3.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Notice that the latest 6 buttons will be added to the right of the previously added ones; if the button doesn’t fit in the remaining space it will be placed on the next line&lt;br /&gt;
&lt;br /&gt;
* We decide to move buttons 1-3 on a separate line and buttons 4-6 on another line; to do so, add a control of type ‘line_break’ at the end of ‘My First Button’ and at the end of ‘Button 3’. The syntax is the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;line_break&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* And the result is like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice4.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* If you want some space between the buttons you can use ‘spacer’, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* The result:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice5.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Here is the full definition of the Control array (highlighted with blue you can see UI7 specific properties):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;2&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;my_button&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;my_first_button&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;My First Button&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;line_break&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_1&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button1&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button1&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;21&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;21&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_2&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button2&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button2&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;22&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;22&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_3&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button3&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button3&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;23&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;23&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;line_break&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_4&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button4&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button4&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;24&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;24&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_5&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button5&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button5&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;25&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;25&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_6&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button6&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button6&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;26&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;26&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugins:_Static_JSON_file</id>
		<title>Luup plugins: Static JSON file</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugins:_Static_JSON_file"/>
				<updated>2016-03-10T16:33:06Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
&lt;br /&gt;
Starting in UI4, Luup plugins can specify a ''static JSON'' file.  This file describes how the plugin appears in the web interface.&lt;br /&gt;
&lt;br /&gt;
=What the static JSON file controls=&lt;br /&gt;
&lt;br /&gt;
* The icon used by the plugin in the dashboard.&lt;br /&gt;
* The text that displays in the one-to-two-row status message in the dashboard.&lt;br /&gt;
* Whether the icon changes depending on the value of a variable in the device (for example, a binary light's icon changes from lit to unlit).&lt;br /&gt;
* The tabs that appear in the device's detail dialog (when you click the wrench/spanner).&lt;br /&gt;
* The content of these tabs.&lt;br /&gt;
* The triggers or events that are available for the device in the &amp;quot;Triggers&amp;quot; or &amp;quot;Events&amp;quot; tag of a scene.&lt;br /&gt;
&lt;br /&gt;
=Referencing the static JSON file from the Device XML file=&lt;br /&gt;
&lt;br /&gt;
The device XML file (customarly '''D_PluginName.xml''') contains a reference to the static JSON file, in the '''staticJson''' element.  Place the element as a child of the '''device''' element:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
  &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
  &amp;lt;root xmlns=&amp;quot;urn:schemas-upnp-org:device-1-0&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;specVersion&amp;gt;&amp;lt;!-- ... --&amp;gt;&amp;lt;/specVersion&amp;gt;&lt;br /&gt;
    &amp;lt;device&amp;gt;&lt;br /&gt;
      &amp;lt;deviceType&amp;gt;urn:schemas-futzle-com:device:holidayvirtualswitch:1&amp;lt;/deviceType&amp;gt;&lt;br /&gt;
      &amp;lt;staticJson&amp;gt;D_HolidayVirtualSwitch1.json&amp;lt;/staticJson&amp;gt;&lt;br /&gt;
      &amp;lt;!-- ... --&amp;gt;&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Capitalization is important, as with all XML.&lt;br /&gt;
&lt;br /&gt;
The static JSON file is customarily called '''D_PluginName.json'''.&lt;br /&gt;
&lt;br /&gt;
=The static JSON file=&lt;br /&gt;
&lt;br /&gt;
The static JSON file is a single JSON object (associative array).&lt;br /&gt;
&lt;br /&gt;
==Root keys==&lt;br /&gt;
&lt;br /&gt;
These keys have been seen at the top level in static JSON files in the wild:&lt;br /&gt;
&lt;br /&gt;
; flashicon&lt;br /&gt;
: The device's icon, as a string.  Despite the name, the icon is no longer related to Adobe Flash in UI4 or UI5. See [[Luup plugin icons]].&lt;br /&gt;
; imgIconBody&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconDimmable&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconTurnable&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconMin&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconMax&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; halloIconsDir&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; inScene&lt;br /&gt;
: When included and set equal to one, it enables any buttons, etc located on the &amp;quot;dashboard box&amp;quot;, so they can be selected in the scene editor for use in scenes, rather than being grayed out.&lt;br /&gt;
; DisplayStatus&lt;br /&gt;
: A JSON object (associative array).  For devices where the icon changes based on a variable's value, describes which variable, and the range of values that produce different icon images.  See [[Luup plugin icons]].&lt;br /&gt;
; state_icons&lt;br /&gt;
: A JSON array.  For devices where the icon changes based on a variable's value, describes which icon files exist.  See [[Luup plugin icons]]. Used only in firmware 1.5.401 or later.&lt;br /&gt;
; doc_url&lt;br /&gt;
: A JSON object (associative array). In UI5, the only item in this object which is used is doc_page. It controls which page in docs5.mios.com is brought up when you click on the help ('''?''') icon in the device control panel.&lt;br /&gt;
; Tabs&lt;br /&gt;
: A JSON array.  Describes the tabs in the detail dialog, and what subset of the first tab appears in the dashboard.  See [[Luup plugin tabs]].&lt;br /&gt;
; ToggleButton&lt;br /&gt;
: ?????&lt;br /&gt;
; DeviceType&lt;br /&gt;
: A JSON string.  Must match the '''deviceType''' element in the corresponding device XML ('''D_PluginName.xml''') file.&lt;br /&gt;
; eventList&lt;br /&gt;
: A JSON object (associative array).  Describes the events that this plugin can produce.  Events are triggers that can fire off actions in scenes.  This key is required for UI4, and obsoleted in UI5. For UI5, use the eventList2 tag, which has a different format. For compatibility with both UI4 and UI5 you need the same information in both keys. See [[UI4 UI5 Migration]] for more information.&lt;br /&gt;
; eventList2&lt;br /&gt;
: see eventList, above.&lt;br /&gt;
; sceneList&lt;br /&gt;
: A JSON object (associative array).  Describes the actions that this plugin can perform (for instance, as the action in a scene). This key is not required and is ignored in UI5, however it is needed for UI4 compatibility. See [[UI4 UI5 Migration]] for more information about its format.&lt;br /&gt;
&lt;br /&gt;
= UI7 Updates =&lt;br /&gt;
&lt;br /&gt;
* '''default_icon'''&lt;br /&gt;
: replace the old &amp;quot;flashicon&amp;quot; from UI5. See [[Luup plugin icons]]&lt;br /&gt;
* '''state_icons'''&lt;br /&gt;
: state_icons mechanism has changed in UI7. Please see [[Luup plugin icons]]&lt;br /&gt;
* '''TopNavigationTab''' = '''top_navigation_tab'''&lt;br /&gt;
&lt;br /&gt;
In device cpanel, tabs can be placed either in the top part or in the bottom. By default they are put in the bottom part. You can mark your tabs with a special flag so that they can be placed in top navigation bar of the cpanel.&lt;br /&gt;
If two or more tabs are marked with ‘'''TabType=”flash”'''’, only the first one found will be placed in top navigation bar. If you want all of them to be placed there, you have to add ‘'''top_navigation_tab'''’ to each one.&lt;br /&gt;
Let’s assume you have a plugin named ‘MyPlugin’ and you want to put a button in the top navigation bar, which, when clicked, will display a message in the cpanel.&lt;br /&gt;
&lt;br /&gt;
* Add the following lines to the .json device file (D_MyPlugin.json), in the Tabs section:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
          &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                &amp;quot;lang_tag&amp;quot;: &amp;quot;About&amp;quot;,&lt;br /&gt;
                &amp;quot;text&amp;quot;: &amp;quot;About&amp;quot;&lt;br /&gt;
          },&lt;br /&gt;
              &amp;quot;TopNavigationTab&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
          &amp;quot;Position&amp;quot;: &amp;quot;4&amp;quot;,&lt;br /&gt;
          &amp;quot;TabType&amp;quot;: &amp;quot;javascript&amp;quot;,&lt;br /&gt;
          &amp;quot;ScriptName&amp;quot;: &amp;quot;J_MyPlugin.js&amp;quot;,&lt;br /&gt;
          &amp;quot;Function&amp;quot;: &amp;quot;MyPlugin.about&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the property ‘'''TopNavigationTab'''’ set to the value of ‘1’. This is the line that specifies where to place the tab in cpanel. If you set a value different than 1, the tab will be placed in the bottom part of the cpanel, so always use ‘1’ if you want the tab in the top bar. You can also use ‘'''top_navigation_tab'''’.&lt;br /&gt;
&lt;br /&gt;
* Add the following lines of code to your .js file (J_MyPlugin.js):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var MyPlugin = (function(api){&lt;br /&gt;
    return {&lt;br /&gt;
        about: function() {&lt;br /&gt;
            try {              &lt;br /&gt;
                var html = '&amp;lt;div&amp;gt;This is all about me !&amp;lt;/div&amp;gt;';&lt;br /&gt;
             api.setCpanelContent(html);&lt;br /&gt;
            } catch (e) {&lt;br /&gt;
                Utils.logError('Error in MyPlugin.about(): ' + e);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
})(api);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Perform a lu reload, enter device cpanel and click on ‘About’ button (which is in the top navigation bar of the cpanel) and the result will be something like this:&lt;br /&gt;
&lt;br /&gt;
[[File:TopNavigationTab.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
== Adding Javascript code to Flash tabs ==&lt;br /&gt;
&lt;br /&gt;
In UI7, Each tab of type flash in a device json can have a property called ‘AfterInit’ which can be&lt;br /&gt;
used to execute some Javascript code. This way a developer can “insert” custom defined&lt;br /&gt;
content directly in a cpanel in the Control tab.&lt;br /&gt;
&lt;br /&gt;
[[File:AfterInit.png|image|center|AfterInit]]&lt;br /&gt;
&lt;br /&gt;
This image is taken from MiOS implementation of PhilipsHue plugin . As you can see the part&lt;br /&gt;
which is situated below the dashed line is inserted via Javascript code. In order to achieve this,&lt;br /&gt;
the following json was used for the ‘Control’ tab:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
          &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                    &amp;quot;lang_tag&amp;quot;: &amp;quot;control&amp;quot;,&lt;br /&gt;
                    &amp;quot;text&amp;quot;: &amp;quot;Control&amp;quot;&lt;br /&gt;
          },&lt;br /&gt;
          &amp;quot;Position&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
          &amp;quot;TabType&amp;quot;: &amp;quot;flash&amp;quot;,&lt;br /&gt;
          &amp;quot;AfterInit&amp;quot;: {&lt;br /&gt;
                    &amp;quot;ScriptName&amp;quot;: &amp;quot;J_PhilipsHueLamp2.js&amp;quot;,&lt;br /&gt;
                    &amp;quot;Function&amp;quot;: &amp;quot;PhilipsHueLamp2.color_picker&amp;quot;&lt;br /&gt;
          } ,&lt;br /&gt;
          ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''AfterInit'''&lt;br /&gt;
: object which contains two properties:&lt;br /&gt;
* '''ScriptName'''&lt;br /&gt;
: specifies the .js file where the function resides; the .js file is loaded when the user enters the cpanel&lt;br /&gt;
* '''Function'''&lt;br /&gt;
: specifies the name of the function to execute&lt;br /&gt;
&lt;br /&gt;
== Position device controls for generic devices ==&lt;br /&gt;
&lt;br /&gt;
In UI7, devices are split into two categories: specific and generic. Specific devices are those which have a predefined design (like binary light, thermostat, dimmable etc.). Generic devices are those created by plugin developers.&lt;br /&gt;
&lt;br /&gt;
=== Positioning controls in ‘device view’ ===&lt;br /&gt;
&lt;br /&gt;
The controls for generic devices are placed inside the device container based on the order in which are found in device .json file and so, each control is drawn to the right of the previous one. If a control doesn’t fit in the remaining space it will be drawn on the following line. This rule will guarantee that the look of a device will be the same on the Web, but also on the mobile apps.&lt;br /&gt;
&lt;br /&gt;
In order to achieve this, but also to maintain backward compatibility with UI5, the developer can add new properties to the device .json, but doesn’t have to change the existing ones.&lt;br /&gt;
&lt;br /&gt;
In the WEB UI, each control is contained in a div element which is positioned using float: left CSS property.&lt;br /&gt;
&lt;br /&gt;
The size of the controls can be altered using properties like ‘HorizontalMultiplier’ which multiplies the default width of a control with a given float number.&lt;br /&gt;
&lt;br /&gt;
To force a control to be positioned on the next line, a special device control was created: ‘line_break’.&lt;br /&gt;
&lt;br /&gt;
To insert a space between two controls, the developer can use the newly created device control: ‘spacer’, which also accepts ‘HorizontalMultiplier’ property.&lt;br /&gt;
&lt;br /&gt;
=== Tutorial ===&lt;br /&gt;
&lt;br /&gt;
We will start with a device that contains no controls.&lt;br /&gt;
&lt;br /&gt;
* First, we need a SceneGroup with id 1 and a ControlGroup with id 1 which belongs to the previous added scene group (this is the same as in UI5)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;SceneGroup&amp;quot;: [&lt;br /&gt;
   {&lt;br /&gt;
       &amp;quot;id&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;top&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;left&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
       &amp;quot;x&amp;quot;: &amp;quot;2&amp;quot;,&lt;br /&gt;
       &amp;quot;y&amp;quot;: &amp;quot;3&amp;quot;&lt;br /&gt;
   }&lt;br /&gt;
],&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: [&lt;br /&gt;
   {&lt;br /&gt;
       &amp;quot;id&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;isSingle&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;scenegroup&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
   }&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Then, add a control of type ‘button’ with a label: ‘My First Button’. Create it using the same definition as in UI5. Be sure to set the ControlGroup of the button to be equal to 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;my_button&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;my_first_button&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;My First Button&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* By now, your device will look something like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice1.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Notice that the text doesn’t fit properly inside the button. Add &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;2&amp;quot; property to your control definition. This will set the width of the button to double the default size. By now, your device will look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice2.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Add 6 more buttons using the same template as above, but don’t set the “HorizontalMultiplier” property. Use labels: ‘Button 1’, ‘Button 2’, … ‘Button 6’. It will look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice3.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Notice that the latest 6 buttons will be added to the right of the previously added ones; if the button doesn’t fit in the remaining space it will be placed on the next line&lt;br /&gt;
&lt;br /&gt;
* We decide to move buttons 1-3 on a separate line and buttons 4-6 on another line; to do so, add a control of type ‘line_break’ at the end of ‘My First Button’ and at the end of ‘Button 3’. The syntax is the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;line_break&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* And the result is like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice4.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* If you want some space between the buttons you can use ‘spacer’, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* The result:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice5.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Here is the full definition of the Control array (highlighted with blue you can see UI7 specific properties):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;2&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;my_button&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;my_first_button&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;My First Button&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;line_break&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_1&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button1&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button1&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;21&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;21&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_2&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button2&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button2&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;22&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;22&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_3&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button3&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button3&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;23&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;23&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;line_break&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_4&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button4&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button4&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;24&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;24&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_5&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button5&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button5&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;25&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;25&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_6&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button6&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button6&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;26&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;26&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/File:AfterInit.png</id>
		<title>File:AfterInit.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/File:AfterInit.png"/>
				<updated>2016-03-10T16:29:43Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: Gengen uploaded a new version of &amp;amp;quot;File:AfterInit.png&amp;amp;quot;: AfterInit JSON tag screen shot&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Screenshot of PhilipsHue plugin which uses AfterInit JSON tag&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/File:AfterInit.png</id>
		<title>File:AfterInit.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/File:AfterInit.png"/>
				<updated>2016-03-10T16:19:38Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: Screenshot of PhilipsHue plugin which uses AfterInit JSON tag&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Screenshot of PhilipsHue plugin which uses AfterInit JSON tag&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugins:_Static_JSON_file</id>
		<title>Luup plugins: Static JSON file</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugins:_Static_JSON_file"/>
				<updated>2016-03-10T16:17:47Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: Added AfterInit documentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
&lt;br /&gt;
Starting in UI4, Luup plugins can specify a ''static JSON'' file.  This file describes how the plugin appears in the web interface.&lt;br /&gt;
&lt;br /&gt;
=What the static JSON file controls=&lt;br /&gt;
&lt;br /&gt;
* The icon used by the plugin in the dashboard.&lt;br /&gt;
* The text that displays in the one-to-two-row status message in the dashboard.&lt;br /&gt;
* Whether the icon changes depending on the value of a variable in the device (for example, a binary light's icon changes from lit to unlit).&lt;br /&gt;
* The tabs that appear in the device's detail dialog (when you click the wrench/spanner).&lt;br /&gt;
* The content of these tabs.&lt;br /&gt;
* The triggers or events that are available for the device in the &amp;quot;Triggers&amp;quot; or &amp;quot;Events&amp;quot; tag of a scene.&lt;br /&gt;
&lt;br /&gt;
=Referencing the static JSON file from the Device XML file=&lt;br /&gt;
&lt;br /&gt;
The device XML file (customarly '''D_PluginName.xml''') contains a reference to the static JSON file, in the '''staticJson''' element.  Place the element as a child of the '''device''' element:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
  &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
  &amp;lt;root xmlns=&amp;quot;urn:schemas-upnp-org:device-1-0&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;specVersion&amp;gt;&amp;lt;!-- ... --&amp;gt;&amp;lt;/specVersion&amp;gt;&lt;br /&gt;
    &amp;lt;device&amp;gt;&lt;br /&gt;
      &amp;lt;deviceType&amp;gt;urn:schemas-futzle-com:device:holidayvirtualswitch:1&amp;lt;/deviceType&amp;gt;&lt;br /&gt;
      &amp;lt;staticJson&amp;gt;D_HolidayVirtualSwitch1.json&amp;lt;/staticJson&amp;gt;&lt;br /&gt;
      &amp;lt;!-- ... --&amp;gt;&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Capitalization is important, as with all XML.&lt;br /&gt;
&lt;br /&gt;
The static JSON file is customarily called '''D_PluginName.json'''.&lt;br /&gt;
&lt;br /&gt;
=The static JSON file=&lt;br /&gt;
&lt;br /&gt;
The static JSON file is a single JSON object (associative array).&lt;br /&gt;
&lt;br /&gt;
==Root keys==&lt;br /&gt;
&lt;br /&gt;
These keys have been seen at the top level in static JSON files in the wild:&lt;br /&gt;
&lt;br /&gt;
; flashicon&lt;br /&gt;
: The device's icon, as a string.  Despite the name, the icon is no longer related to Adobe Flash in UI4 or UI5. See [[Luup plugin icons]].&lt;br /&gt;
; imgIconBody&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconDimmable&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconTurnable&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconMin&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconMax&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; halloIconsDir&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; inScene&lt;br /&gt;
: When included and set equal to one, it enables any buttons, etc located on the &amp;quot;dashboard box&amp;quot;, so they can be selected in the scene editor for use in scenes, rather than being grayed out.&lt;br /&gt;
; DisplayStatus&lt;br /&gt;
: A JSON object (associative array).  For devices where the icon changes based on a variable's value, describes which variable, and the range of values that produce different icon images.  See [[Luup plugin icons]].&lt;br /&gt;
; state_icons&lt;br /&gt;
: A JSON array.  For devices where the icon changes based on a variable's value, describes which icon files exist.  See [[Luup plugin icons]]. Used only in firmware 1.5.401 or later.&lt;br /&gt;
; doc_url&lt;br /&gt;
: A JSON object (associative array). In UI5, the only item in this object which is used is doc_page. It controls which page in docs5.mios.com is brought up when you click on the help ('''?''') icon in the device control panel.&lt;br /&gt;
; Tabs&lt;br /&gt;
: A JSON array.  Describes the tabs in the detail dialog, and what subset of the first tab appears in the dashboard.  See [[Luup plugin tabs]].&lt;br /&gt;
; ToggleButton&lt;br /&gt;
: ?????&lt;br /&gt;
; DeviceType&lt;br /&gt;
: A JSON string.  Must match the '''deviceType''' element in the corresponding device XML ('''D_PluginName.xml''') file.&lt;br /&gt;
; eventList&lt;br /&gt;
: A JSON object (associative array).  Describes the events that this plugin can produce.  Events are triggers that can fire off actions in scenes.  This key is required for UI4, and obsoleted in UI5. For UI5, use the eventList2 tag, which has a different format. For compatibility with both UI4 and UI5 you need the same information in both keys. See [[UI4 UI5 Migration]] for more information.&lt;br /&gt;
; eventList2&lt;br /&gt;
: see eventList, above.&lt;br /&gt;
; sceneList&lt;br /&gt;
: A JSON object (associative array).  Describes the actions that this plugin can perform (for instance, as the action in a scene). This key is not required and is ignored in UI5, however it is needed for UI4 compatibility. See [[UI4 UI5 Migration]] for more information about its format.&lt;br /&gt;
&lt;br /&gt;
= UI7 Updates =&lt;br /&gt;
&lt;br /&gt;
* '''default_icon'''&lt;br /&gt;
: replace the old &amp;quot;flashicon&amp;quot; from UI5. See [[Luup plugin icons]]&lt;br /&gt;
* '''state_icons'''&lt;br /&gt;
: state_icons mechanism has changed in UI7. Please see [[Luup plugin icons]]&lt;br /&gt;
* '''TopNavigationTab''' = '''top_navigation_tab'''&lt;br /&gt;
&lt;br /&gt;
In device cpanel, tabs can be placed either in the top part or in the bottom. By default they are put in the bottom part. You can mark your tabs with a special flag so that they can be placed in top navigation bar of the cpanel.&lt;br /&gt;
If two or more tabs are marked with ‘'''TabType=”flash”'''’, only the first one found will be placed in top navigation bar. If you want all of them to be placed there, you have to add ‘'''top_navigation_tab'''’ to each one.&lt;br /&gt;
Let’s assume you have a plugin named ‘MyPlugin’ and you want to put a button in the top navigation bar, which, when clicked, will display a message in the cpanel.&lt;br /&gt;
&lt;br /&gt;
* Add the following lines to the .json device file (D_MyPlugin.json), in the Tabs section:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
          &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                &amp;quot;lang_tag&amp;quot;: &amp;quot;About&amp;quot;,&lt;br /&gt;
                &amp;quot;text&amp;quot;: &amp;quot;About&amp;quot;&lt;br /&gt;
          },&lt;br /&gt;
              &amp;quot;TopNavigationTab&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
          &amp;quot;Position&amp;quot;: &amp;quot;4&amp;quot;,&lt;br /&gt;
          &amp;quot;TabType&amp;quot;: &amp;quot;javascript&amp;quot;,&lt;br /&gt;
          &amp;quot;ScriptName&amp;quot;: &amp;quot;J_MyPlugin.js&amp;quot;,&lt;br /&gt;
          &amp;quot;Function&amp;quot;: &amp;quot;MyPlugin.about&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the property ‘'''TopNavigationTab'''’ set to the value of ‘1’. This is the line that specifies where to place the tab in cpanel. If you set a value different than 1, the tab will be placed in the bottom part of the cpanel, so always use ‘1’ if you want the tab in the top bar. You can also use ‘'''top_navigation_tab'''’.&lt;br /&gt;
&lt;br /&gt;
* Add the following lines of code to your .js file (J_MyPlugin.js):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var MyPlugin = (function(api){&lt;br /&gt;
    return {&lt;br /&gt;
        about: function() {&lt;br /&gt;
            try {              &lt;br /&gt;
                var html = '&amp;lt;div&amp;gt;This is all about me !&amp;lt;/div&amp;gt;';&lt;br /&gt;
             api.setCpanelContent(html);&lt;br /&gt;
            } catch (e) {&lt;br /&gt;
                Utils.logError('Error in MyPlugin.about(): ' + e);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
})(api);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Perform a lu reload, enter device cpanel and click on ‘About’ button (which is in the top navigation bar of the cpanel) and the result will be something like this:&lt;br /&gt;
&lt;br /&gt;
[[File:TopNavigationTab.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
== Adding Javascript code to Flash tabs ==&lt;br /&gt;
&lt;br /&gt;
In UI7, Each tab of type flash in a device json can have a property called ‘AfterInit’ which can be&lt;br /&gt;
used to execute some Javascript code. This way a developer can “insert” custom defined&lt;br /&gt;
content directly in a cpanel in the Control tab.&lt;br /&gt;
&lt;br /&gt;
[[File:AfterInt.png|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
This image is taken from MiOS implementation of PhilipsHue plugin . As you can see the part&lt;br /&gt;
which is situated below the dashed line is inserted via Javascript code. In order to achieve this,&lt;br /&gt;
the following json was used for the ‘Control’ tab:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
          &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                    &amp;quot;lang_tag&amp;quot;: &amp;quot;control&amp;quot;,&lt;br /&gt;
                    &amp;quot;text&amp;quot;: &amp;quot;Control&amp;quot;&lt;br /&gt;
          },&lt;br /&gt;
          &amp;quot;Position&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
          &amp;quot;TabType&amp;quot;: &amp;quot;flash&amp;quot;,&lt;br /&gt;
          &amp;quot;AfterInit&amp;quot;: {&lt;br /&gt;
                    &amp;quot;ScriptName&amp;quot;: &amp;quot;J_PhilipsHueLamp2.js&amp;quot;,&lt;br /&gt;
                    &amp;quot;Function&amp;quot;: &amp;quot;PhilipsHueLamp2.color_picker&amp;quot;&lt;br /&gt;
          } ,&lt;br /&gt;
          ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''AfterInit'''&lt;br /&gt;
: object which contains two properties:&lt;br /&gt;
* '''ScriptName'''&lt;br /&gt;
: specifies the .js file where the function resides; the .js file is loaded when the user enters the cpanel&lt;br /&gt;
* '''Function'''&lt;br /&gt;
: specifies the name of the function to execute&lt;br /&gt;
&lt;br /&gt;
== Position device controls for generic devices ==&lt;br /&gt;
&lt;br /&gt;
In UI7, devices are split into two categories: specific and generic. Specific devices are those which have a predefined design (like binary light, thermostat, dimmable etc.). Generic devices are those created by plugin developers.&lt;br /&gt;
&lt;br /&gt;
=== Positioning controls in ‘device view’ ===&lt;br /&gt;
&lt;br /&gt;
The controls for generic devices are placed inside the device container based on the order in which are found in device .json file and so, each control is drawn to the right of the previous one. If a control doesn’t fit in the remaining space it will be drawn on the following line. This rule will guarantee that the look of a device will be the same on the Web, but also on the mobile apps.&lt;br /&gt;
&lt;br /&gt;
In order to achieve this, but also to maintain backward compatibility with UI5, the developer can add new properties to the device .json, but doesn’t have to change the existing ones.&lt;br /&gt;
&lt;br /&gt;
In the WEB UI, each control is contained in a div element which is positioned using float: left CSS property.&lt;br /&gt;
&lt;br /&gt;
The size of the controls can be altered using properties like ‘HorizontalMultiplier’ which multiplies the default width of a control with a given float number.&lt;br /&gt;
&lt;br /&gt;
To force a control to be positioned on the next line, a special device control was created: ‘line_break’.&lt;br /&gt;
&lt;br /&gt;
To insert a space between two controls, the developer can use the newly created device control: ‘spacer’, which also accepts ‘HorizontalMultiplier’ property.&lt;br /&gt;
&lt;br /&gt;
=== Tutorial ===&lt;br /&gt;
&lt;br /&gt;
We will start with a device that contains no controls.&lt;br /&gt;
&lt;br /&gt;
* First, we need a SceneGroup with id 1 and a ControlGroup with id 1 which belongs to the previous added scene group (this is the same as in UI5)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;SceneGroup&amp;quot;: [&lt;br /&gt;
   {&lt;br /&gt;
       &amp;quot;id&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;top&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;left&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
       &amp;quot;x&amp;quot;: &amp;quot;2&amp;quot;,&lt;br /&gt;
       &amp;quot;y&amp;quot;: &amp;quot;3&amp;quot;&lt;br /&gt;
   }&lt;br /&gt;
],&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: [&lt;br /&gt;
   {&lt;br /&gt;
       &amp;quot;id&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;isSingle&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;scenegroup&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
   }&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Then, add a control of type ‘button’ with a label: ‘My First Button’. Create it using the same definition as in UI5. Be sure to set the ControlGroup of the button to be equal to 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;my_button&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;my_first_button&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;My First Button&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* By now, your device will look something like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice1.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Notice that the text doesn’t fit properly inside the button. Add &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;2&amp;quot; property to your control definition. This will set the width of the button to double the default size. By now, your device will look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice2.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Add 6 more buttons using the same template as above, but don’t set the “HorizontalMultiplier” property. Use labels: ‘Button 1’, ‘Button 2’, … ‘Button 6’. It will look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice3.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Notice that the latest 6 buttons will be added to the right of the previously added ones; if the button doesn’t fit in the remaining space it will be placed on the next line&lt;br /&gt;
&lt;br /&gt;
* We decide to move buttons 1-3 on a separate line and buttons 4-6 on another line; to do so, add a control of type ‘line_break’ at the end of ‘My First Button’ and at the end of ‘Button 3’. The syntax is the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;line_break&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* And the result is like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice4.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* If you want some space between the buttons you can use ‘spacer’, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* The result:&lt;br /&gt;
&lt;br /&gt;
[[File:MyDevice5.jpg|image|center|caption]]&lt;br /&gt;
&lt;br /&gt;
* Here is the full definition of the Control array (highlighted with blue you can see UI7 specific properties):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;2&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;my_button&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;my_first_button&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;My First Button&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;line_break&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_1&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button1&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button1&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;21&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;21&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_2&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button2&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button2&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;22&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;22&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_3&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button3&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button3&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;23&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;23&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;line_break&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_4&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button4&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button4&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;24&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;24&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_5&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button5&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button5&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;25&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;25&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;spacer&amp;quot;,&lt;br /&gt;
   &amp;quot;HorizontalMultiplier&amp;quot;: &amp;quot;0.7&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;ControlGroup&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;ControlType&amp;quot;: &amp;quot;button&amp;quot;,&lt;br /&gt;
   &amp;quot;ControlCode&amp;quot;: &amp;quot;button_6&amp;quot;,&lt;br /&gt;
   &amp;quot;top&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;left&amp;quot;: 0,&lt;br /&gt;
   &amp;quot;Label&amp;quot;: {&lt;br /&gt;
       &amp;quot;lang_tag&amp;quot;: &amp;quot;button6&amp;quot;,&lt;br /&gt;
       &amp;quot;text&amp;quot;: &amp;quot;Button6&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Display&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
       &amp;quot;Value&amp;quot;: &amp;quot;26&amp;quot;,&lt;br /&gt;
       &amp;quot;Top&amp;quot;: 200,&lt;br /&gt;
       &amp;quot;Left&amp;quot;: 50,&lt;br /&gt;
       &amp;quot;Width&amp;quot;: 100,&lt;br /&gt;
       &amp;quot;Height&amp;quot;: 20&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;Command&amp;quot;: {&lt;br /&gt;
       &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:MyPlugin1&amp;quot;,&lt;br /&gt;
       &amp;quot;Action&amp;quot;: &amp;quot;SetStatus&amp;quot;,&lt;br /&gt;
       &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
           {&lt;br /&gt;
               &amp;quot;Name&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
               &amp;quot;Value&amp;quot;: &amp;quot;26&amp;quot;&lt;br /&gt;
           }&lt;br /&gt;
       ]&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_tabs</id>
		<title>Luup plugin tabs</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_tabs"/>
				<updated>2015-04-04T18:13:51Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Root Keys=&lt;br /&gt;
Refer to [[Luup_plugins:_Static_JSON_file|Root Keys]].&lt;br /&gt;
&lt;br /&gt;
=Tabs Key=&lt;br /&gt;
The '''Tabs''' key in the [[Luup plugins: Static JSON file|static JSON file]] performs two duties in the UI4 HTML interface: &lt;br /&gt;
&lt;br /&gt;
*It controls how the device appears on the '''Dashboard''' and '''Devices''' web pages.&lt;br /&gt;
*It controls how each tab appears in the the device's detailed dialog.&lt;br /&gt;
&lt;br /&gt;
The value of the '''Tabs''' key is a JSON array. Each array element is a JSON object (associative array). One for each Tab. &lt;br /&gt;
&lt;br /&gt;
==Tab fields==&lt;br /&gt;
These fields must be present for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object containing two keys:&lt;br /&gt;
*'''lang_tag''' (a string) is not displayed in the HTML interface but may be used by localized UIs.&lt;br /&gt;
*'''text''' (a string) is displayed in the tab's handle at the top of the dialog. &lt;br /&gt;
;'''Position''' &lt;br /&gt;
:A string which contains a number matching the relative position of this tab in relation to the other tabs. The leftmost tab has value &amp;quot;0&amp;quot;, the next tab &amp;quot;1&amp;quot;, and so on. &lt;br /&gt;
;'''TabType''' &lt;br /&gt;
:A string which describes how the content of the tab is to be generated. The value may be one of '''javascript''' or '''flash'''. These are described in the following sections.&lt;br /&gt;
&lt;br /&gt;
These fields are optional for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''ControlGroup'''&lt;br /&gt;
:Required only if the first tab is also used to display elements on the dashboard. See [[Luup_plugin_tabs#Dashboard_appearance|''Dashboard appearance'']] below.&lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
:Added in UI5.&lt;br /&gt;
&lt;br /&gt;
;'''Control'''&lt;br /&gt;
:An array of controls for each tab. Each control consists of:&lt;br /&gt;
*'''ControlType''' - the Control Types are described further below. &lt;br /&gt;
*'''ControlGroup''' - a reference to the control's ControlGroup &lt;br /&gt;
*'''text_align''' - align left or right &lt;br /&gt;
*'''top''' - dashboard position in button units&lt;br /&gt;
*'''left''' - dashboard position in button units&lt;br /&gt;
&lt;br /&gt;
eg a slider sitting above a two by two array of buttons:&lt;br /&gt;
&lt;br /&gt;
slider: top = 0, left = 0&amp;lt;br\&amp;gt;&lt;br /&gt;
top left button: top = 1.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
top right button: top = 1.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
bot left button: top = 2.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
bot right button: top = 2.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TabType javascript===&lt;br /&gt;
The '''javascript''' tab type executes client-side JavaScript, producing an HTML fragment which forms the body of the tab. These additional keys are required in the tab JSON object: &lt;br /&gt;
&lt;br /&gt;
;'''ScriptName''' &lt;br /&gt;
:The name of the JavaScript file. Conventionally, plugin JavaScript files are named '''J_PluginName.js'''. &lt;br /&gt;
;'''Function''' &lt;br /&gt;
:The function inside the JavaScript file which will be called to produce the HTML.&lt;br /&gt;
&lt;br /&gt;
The JavaScript code has access to most Luup variables through the [[JavaScript API]]. &lt;br /&gt;
&lt;br /&gt;
===TabType flash===&lt;br /&gt;
The '''flash''' tab type creates a tab body with a simple page-description language. Each component of the page is placed into the body at the position specified. You can't place a '''Control''' on the dashboard unless you place it in a '''ControlGroup''', which in turn must be placed in a '''SceneGroup''': &lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
&lt;br /&gt;
''SceneGroup'' (added in UI5) is similar to a ControlGroup, but with some key differences: it is customizable (by setting its position and dimensions) and it defines the behavior of the buttons assigned to the same state variable. If two buttons in the same SceneGroup are assigned to the same variable, only one of them can be activated in the Scene Editor (radio buttons behavior). If two buttons assigned to the same variable are in different SceneGroups, both can be activated at the same time in the Scene Editor (checkboxes behavior). &lt;br /&gt;
&lt;br /&gt;
It has the following tags: &lt;br /&gt;
&lt;br /&gt;
*'''id''': A numeric value (positive integer numbers). &lt;br /&gt;
*'''top''': The offset from the device header. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''left''': The offset from the device icon. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''x''', '''y''': the dimensions of the SceneGroup. e.g If I have two horizontally aligned buttons in the SceneGroup, ''x'' will be ''2'' and ''y'' will be ''1''.&lt;br /&gt;
&lt;br /&gt;
A ''SceneGroup'' can contain one or more ''ControlGroups''. &lt;br /&gt;
&lt;br /&gt;
===Dashboard appearance===&lt;br /&gt;
The first array element in the '''Tabs''' field is special. If it is of type '''flash''' then a subset of the controls on the tab can be chosen to display on the UI4 dashboard.&lt;br /&gt;
&lt;br /&gt;
The dashboard box for a device has room for two rows of information and controls. These are called ''Control Groups''.&lt;br /&gt;
&lt;br /&gt;
The tab object must contain the key '''ControlGroup''', which specifies how the dashboard's two available rows of information are to be handled. '''ControlGroup''' is a JSON array containing JSON objects (associative arrays). Each object has the following keys:&lt;br /&gt;
&lt;br /&gt;
;'''id''' &lt;br /&gt;
:A string containing a number, &amp;quot;1&amp;quot; for the first object, &amp;quot;2&amp;quot; for the second, and so on. &lt;br /&gt;
;'''isSingle''' &lt;br /&gt;
:UI4: If this key is present and contains the value &amp;quot;1&amp;quot;, then this control group is combined with other control groups that have '''isSingle''' similarly set onto the same row. For an example of this, see the On/Off buttons of the Binary Switch and Dimmable Switch static JSON files. &lt;br /&gt;
:UI5: Since controls cannot be grouped in drop-down lists anymore, this tag has a different role in UI5. When isSingle is &amp;quot;1&amp;quot;, the buttons in the ControlGroup will have a background (e.g. the BinaryLight buttons). If isSingle is missing, the buttons won't have a background. That is the button shape is not shown; only the button text will show. &lt;br /&gt;
;'''type''' &lt;br /&gt;
:If this key is present and contains the value &amp;quot;info&amp;quot;, then the controls displayed on this row are not clickable. If this key is absent, buttons may be used in the control group.&lt;br /&gt;
&lt;br /&gt;
===Making a control appear on the dashboard===&lt;br /&gt;
All control types (label, variable, button, input, slider) can appear in the dashboard. However, by default, controls in the first tab's detail view do not appear in the dashboard. To make a control's text appear in the dashboard, add a '''ControlGroup''' to the control's JSON object. The value is a string containing a number, matching the '''id''' of one of the Tab's overall '''ControlGroup''' key. The dashboard will concatenate all of a control group's controls in the order they are presented in the '''Control''' array, and display them in a single row of text. &lt;br /&gt;
&lt;br /&gt;
===Keeping controls together===&lt;br /&gt;
Some controls may not make sense if presented individually (for instance, an input for an alarm PIN and a &amp;quot;disarm&amp;quot; button). To keep these controls together, add a '''ControlPair''' key to all of the controls that have to appear together. Use the same value (a number in a string) for all of them.&lt;br /&gt;
&lt;br /&gt;
==Control Types==&lt;br /&gt;
&lt;br /&gt;
The '''Control''' array contains JSON objects (associative arrays), one per item in the tab body. An item is either a static text label, a variable, an input field, or a button or slider. This is handled by setting the '''ControlType''' key on the object. &lt;br /&gt;
&lt;br /&gt;
===ControlType label===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''label''', a fixed label is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys:&lt;br /&gt;
*'''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. The key is optional.&lt;br /&gt;
*'''text''' is displayed as the content of the label. In UI5 on the &amp;quot;Dashboard&amp;quot;, the text is rendered by passing it in the &amp;quot;Title&amp;quot; attribute of a Div statement, so it can only be plain text, (as opposed to say HTML). However in the tabs, it is placed in a Div statement so HTML, such as a URL link, can be rendered. &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys, which are strings containing numbers (in pixels?). This controls where the label is placed on the tab. The label is left aligned.&lt;br /&gt;
*'''Top''': The offset from the top of tab writable area.&lt;br /&gt;
*'''Left''': The offset from the left side of the tab writable area.&lt;br /&gt;
*'''Width''': Window width of the label.&lt;br /&gt;
*'''Height''': Window height of the label.&lt;br /&gt;
&lt;br /&gt;
===ControlType variable===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''variable''', the contents of a Luup variable are displayed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with six keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the variable's value is placed in the tab), '''Service''' (a string containing the Service Id of the variable to be displayed), and '''Variable''' (a string containing the name of the variable to be displayed).&lt;br /&gt;
&lt;br /&gt;
===ControlType input===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''input''', a text box for the user to type a string is placed in the tab. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''ID''' &lt;br /&gt;
:A string which other controls (buttons) will use to refer to the string that the user has typed into this field. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object containing: &lt;br /&gt;
* '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers). This controls where the text box is placed in the tab.&lt;br /&gt;
* '''Service''', '''Variable''' (optional) A UPnP service and variable from which a default value is obtained&lt;br /&gt;
&lt;br /&gt;
===ControlType button===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''button''', a clickable button is placed at the specified coordinates. The button will appear selected (depressed) or unselected based on the value of a variable, permitting sets of buttons to act as radio buttons. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys: '''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. '''text''' is displayed as the text inside the button. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with up to seven keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), and (optionally) '''Value''' (a string, &amp;quot;1&amp;quot; if omitted). The specified variable's value controls whether the button appears selected (if the variable matches '''Value''') or not selected (if the variable doesn't match). &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching an input control on the page (for a user-entered parameter value).&lt;br /&gt;
&lt;br /&gt;
===ControlType slider===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''slider''', a draggable slider is placed at the specified coordinates. &lt;br /&gt;
&lt;br /&gt;
* '''Note:''' in UI5 and in UI7 (1.7.541 - release 7.0.5 and earlier), there can only be one horizontal slider (0% - 100%) per device. &lt;br /&gt;
&lt;br /&gt;
The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
* '''Display''' A JSON object with nine keys: &lt;br /&gt;
** '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab)&lt;br /&gt;
** '''Service''' (a string containing the Service Id of a variable)&lt;br /&gt;
** '''Variable''' (a string containing the name of a variable), &lt;br /&gt;
** '''MinValue''', '''MaxValue''' (both strings containing numbers, controlling what range of values the slider takes)&lt;br /&gt;
*** '''Note:''' in firmware 1.7.541 and earlier, MinValue and MaxValue must be 0 and 100 % respectively.&lt;br /&gt;
** '''ID''' (a string, used in the '''Command''' to refer to the position the user has dragged the slider to). The specified variable's value is used to display the slider's current position. &lt;br /&gt;
* '''Command''' A JSON object with three keys: &lt;br /&gt;
** '''Service''' (a string containing the service Id of an action that this device can perform)&lt;br /&gt;
** '''Action''' (a string containing the name of the action)&lt;br /&gt;
** '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key &lt;br /&gt;
*** '''Name''' for the parameter name&amp;lt;br /&amp;gt;and either &lt;br /&gt;
*** '''Value''' (for a fixed parameter value)&amp;lt;br /&amp;gt;or &lt;br /&gt;
*** '''ID''' matching the slider's ID (for a value matching what the user has dragged the slider to). &lt;br /&gt;
* '''ControlHeader'''  (Optional) If present, must have string value &amp;quot;1&amp;quot;. When set, the slider's label and value are displayed. Otherwise only the slider is shown. (TODO: This is from reverse-engineering the JavaScript code. Test this.)&lt;br /&gt;
&lt;br /&gt;
===ControlType slider_vertical===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_Heater1.json, D_HVAC_ZoneThermostat1.json&lt;br /&gt;
&lt;br /&gt;
Parameters for slider_vertical are similar to slider. The first slider_vertical is red and used for heating. The second slider_vertical is blue and used for cooling.&lt;br /&gt;
&lt;br /&gt;
===ControlType checkbox===&lt;br /&gt;
&lt;br /&gt;
:As of UI5 firmware 1.5.622, checkboxes are only half-baked and not usable in &amp;quot;flash&amp;quot; style tabs. If you need check-boxes, radio buttons, pop-up menus, etc. in your tab, you need to create a Javascript tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType image===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType image_player===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType js_button===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
=UI7 Updates=&lt;br /&gt;
&lt;br /&gt;
===ControlType line_break===&lt;br /&gt;
This is used to force the next control to appear below the previous one rather than alongside it. See [[Luup plugins: Static JSON file]].&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘line_break’&lt;br /&gt;
&lt;br /&gt;
===ControlType multi_state_button=== &lt;br /&gt;
This is a new type of control in UI7 and works as a “toggle” button. It is used for On/Off, Armed/Disarmed etc buttons.&lt;br /&gt;
&lt;br /&gt;
As an example, here is the definition from the .json file of the Door/Window sensor needed to create this kind of control:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
&amp;quot;ControlType&amp;quot;: &amp;quot;multi_state_button&amp;quot;,&lt;br /&gt;
&amp;quot;top&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
&amp;quot;left&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;states&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
                  &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                      &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_arm&amp;quot;,&lt;br /&gt;
                      &amp;quot;text&amp;quot;: &amp;quot;Arm&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                         &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                           &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                   {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                           ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;arm&amp;quot;&lt;br /&gt;
              },&lt;br /&gt;
              {&lt;br /&gt;
                     &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                           &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_bypass&amp;quot;,&lt;br /&gt;
                           &amp;quot;text&amp;quot;: &amp;quot;Bypass&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                            ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;bypass&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
        ]&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘multi_state_button’&lt;br /&gt;
* '''top''' - an integer specifying the row from the control group where the control will be placed&lt;br /&gt;
* '''left''' - an integer which specifies which column will the control be placed in&lt;br /&gt;
* '''states''' - an array of objects, each object representing a different state for the button; each object has the following properties:&lt;br /&gt;
** '''Lable''' - a JSON object with two keys:&lt;br /&gt;
*** '''lang_tag''' - a string used for localization&lt;br /&gt;
*** '''text''' - the string displayed if localization fails&lt;br /&gt;
** '''Display''' - a JSON object with the following keys:&lt;br /&gt;
*** '''Service''' - a string containing the service id&lt;br /&gt;
*** '''Variable''' - a string containing the name of the variable&lt;br /&gt;
*** '''Value''' - a number which represents the value of the state in which the control must be in order for this state to be displayed/marked as selected&lt;br /&gt;
** '''Command''' - a JSON object with the following properties:&lt;br /&gt;
*** '''Service''' - a string with the service id of the action which this state will perform&lt;br /&gt;
*** '''Action''' - a string representing the name of the action to be executed&lt;br /&gt;
*** '''Parameters''' - an array of JSON objects holding the parameters to pass to the action&lt;br /&gt;
* '''ControlCode''' - a string containing the control code of this control (it is recommended to be unique for each device type)&lt;br /&gt;
&lt;br /&gt;
As you can see, the multi_state_button is a merge resulted from two controls of type button.&lt;br /&gt;
&lt;br /&gt;
[[File:MotionSensor.jpg|image|left]]&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_tabs</id>
		<title>Luup plugin tabs</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_tabs"/>
				<updated>2015-03-29T00:15:39Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: /* ControlType slider */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Root Keys=&lt;br /&gt;
Refer to [[Luup_plugins:_Static_JSON_file|Root Keys]].&lt;br /&gt;
&lt;br /&gt;
=Tabs Key=&lt;br /&gt;
The '''Tabs''' key in the [[Luup plugins: Static JSON file|static JSON file]] performs two duties in the UI4 HTML interface: &lt;br /&gt;
&lt;br /&gt;
*It controls how the device appears on the '''Dashboard''' and '''Devices''' web pages.&lt;br /&gt;
*It controls how each tab appears in the the device's detailed dialog.&lt;br /&gt;
&lt;br /&gt;
The value of the '''Tabs''' key is a JSON array. Each array element is a JSON object (associative array). One for each Tab. &lt;br /&gt;
&lt;br /&gt;
==Tab fields==&lt;br /&gt;
These fields must be present for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object containing two keys:&lt;br /&gt;
*'''lang_tag''' (a string) is not displayed in the HTML interface but may be used by localized UIs.&lt;br /&gt;
*'''text''' (a string) is displayed in the tab's handle at the top of the dialog. &lt;br /&gt;
;'''Position''' &lt;br /&gt;
:A string which contains a number matching the relative position of this tab in relation to the other tabs. The leftmost tab has value &amp;quot;0&amp;quot;, the next tab &amp;quot;1&amp;quot;, and so on. &lt;br /&gt;
;'''TabType''' &lt;br /&gt;
:A string which describes how the content of the tab is to be generated. The value may be one of '''javascript''' or '''flash'''. These are described in the following sections.&lt;br /&gt;
&lt;br /&gt;
These fields are optional for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''ControlGroup'''&lt;br /&gt;
:Required only if the first tab is also used to display elements on the dashboard. See [[Luup_plugin_tabs#Dashboard_appearance|''Dashboard appearance'']] below.&lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
:Added in UI5.&lt;br /&gt;
&lt;br /&gt;
;'''Control'''&lt;br /&gt;
:An array of controls for each tab. Each control consists of:&lt;br /&gt;
*'''ControlType''' - the Control Types are described further below. &lt;br /&gt;
*'''ControlGroup''' - a reference to the control's ControlGroup &lt;br /&gt;
*'''text_align''' - align left or right &lt;br /&gt;
*'''top''' - dashboard position in button units&lt;br /&gt;
*'''left''' - dashboard position in button units&lt;br /&gt;
&lt;br /&gt;
eg a slider sitting above a two by two array of buttons:&lt;br /&gt;
&lt;br /&gt;
slider: top = 0, left = 0&amp;lt;br\&amp;gt;&lt;br /&gt;
top left button: top = 1.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
top right button: top = 1.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
bot left button: top = 2.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
bot right button: top = 2.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TabType javascript===&lt;br /&gt;
The '''javascript''' tab type executes client-side JavaScript, producing an HTML fragment which forms the body of the tab. These additional keys are required in the tab JSON object: &lt;br /&gt;
&lt;br /&gt;
;'''ScriptName''' &lt;br /&gt;
:The name of the JavaScript file. Conventionally, plugin JavaScript files are named '''J_PluginName.js'''. &lt;br /&gt;
;'''Function''' &lt;br /&gt;
:The function inside the JavaScript file which will be called to produce the HTML.&lt;br /&gt;
&lt;br /&gt;
The JavaScript code has access to most Luup variables through the [[JavaScript API]]. &lt;br /&gt;
&lt;br /&gt;
===TabType flash===&lt;br /&gt;
The '''flash''' tab type creates a tab body with a simple page-description language. Each component of the page is placed into the body at the position specified. You can't place a '''Control''' on the dashboard unless you place it in a '''ControlGroup''', which in turn must be placed in a '''SceneGroup''': &lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
&lt;br /&gt;
''SceneGroup'' (added in UI5) is similar to a ControlGroup, but with some key differences: it is customizable (by setting its position and dimensions) and it defines the behavior of the buttons assigned to the same state variable. If two buttons in the same SceneGroup are assigned to the same variable, only one of them can be activated in the Scene Editor (radio buttons behavior). If two buttons assigned to the same variable are in different SceneGroups, both can be activated at the same time in the Scene Editor (checkboxes behavior). &lt;br /&gt;
&lt;br /&gt;
It has the following tags: &lt;br /&gt;
&lt;br /&gt;
*'''id''': A numeric value (positive integer numbers). &lt;br /&gt;
*'''top''': The offset from the device header. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''left''': The offset from the device icon. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''x''', '''y''': the dimensions of the SceneGroup. e.g If I have two horizontally aligned buttons in the SceneGroup, ''x'' will be ''2'' and ''y'' will be ''1''.&lt;br /&gt;
&lt;br /&gt;
A ''SceneGroup'' can contain one or more ''ControlGroups''. &lt;br /&gt;
&lt;br /&gt;
===Dashboard appearance===&lt;br /&gt;
The first array element in the '''Tabs''' field is special. If it is of type '''flash''' then a subset of the controls on the tab can be chosen to display on the UI4 dashboard.&lt;br /&gt;
&lt;br /&gt;
The dashboard box for a device has room for two rows of information and controls. These are called ''Control Groups''.&lt;br /&gt;
&lt;br /&gt;
The tab object must contain the key '''ControlGroup''', which specifies how the dashboard's two available rows of information are to be handled. '''ControlGroup''' is a JSON array containing JSON objects (associative arrays). Each object has the following keys:&lt;br /&gt;
&lt;br /&gt;
;'''id''' &lt;br /&gt;
:A string containing a number, &amp;quot;1&amp;quot; for the first object, &amp;quot;2&amp;quot; for the second, and so on. &lt;br /&gt;
;'''isSingle''' &lt;br /&gt;
:UI4: If this key is present and contains the value &amp;quot;1&amp;quot;, then this control group is combined with other control groups that have '''isSingle''' similarly set onto the same row. For an example of this, see the On/Off buttons of the Binary Switch and Dimmable Switch static JSON files. &lt;br /&gt;
:UI5: Since controls cannot be grouped in drop-down lists anymore, this tag has a different role in UI5. When isSingle is &amp;quot;1&amp;quot;, the buttons in the ControlGroup will have a background (e.g. the BinaryLight buttons). If isSingle is missing, the buttons won't have a background. That is the button shape is not shown; only the button text will show. &lt;br /&gt;
;'''type''' &lt;br /&gt;
:If this key is present and contains the value &amp;quot;info&amp;quot;, then the controls displayed on this row are not clickable. If this key is absent, buttons may be used in the control group.&lt;br /&gt;
&lt;br /&gt;
===Making a control appear on the dashboard===&lt;br /&gt;
All control types (label, variable, button, input, slider) can appear in the dashboard. However, by default, controls in the first tab's detail view do not appear in the dashboard. To make a control's text appear in the dashboard, add a '''ControlGroup''' to the control's JSON object. The value is a string containing a number, matching the '''id''' of one of the Tab's overall '''ControlGroup''' key. The dashboard will concatenate all of a control group's controls in the order they are presented in the '''Control''' array, and display them in a single row of text. &lt;br /&gt;
&lt;br /&gt;
===Keeping controls together===&lt;br /&gt;
Some controls may not make sense if presented individually (for instance, an input for an alarm PIN and a &amp;quot;disarm&amp;quot; button). To keep these controls together, add a '''ControlPair''' key to all of the controls that have to appear together. Use the same value (a number in a string) for all of them.&lt;br /&gt;
&lt;br /&gt;
==Control Types==&lt;br /&gt;
&lt;br /&gt;
The '''Control''' array contains JSON objects (associative arrays), one per item in the tab body. An item is either a static text label, a variable, an input field, or a button or slider. This is handled by setting the '''ControlType''' key on the object. &lt;br /&gt;
&lt;br /&gt;
===ControlType label===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''label''', a fixed label is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys:&lt;br /&gt;
*'''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. The key is optional.&lt;br /&gt;
*'''text''' is displayed as the content of the label. In UI5 on the &amp;quot;Dashboard&amp;quot;, the text is rendered by passing it in the &amp;quot;Title&amp;quot; attribute of a Div statement, so it can only be plain text, (as opposed to say HTML). However in the tabs, it is placed in a Div statement so HTML, such as a URL link, can be rendered. &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys, which are strings containing numbers (in pixels?). This controls where the label is placed on the tab. The label is left aligned.&lt;br /&gt;
*'''Top''': The offset from the top of tab writable area.&lt;br /&gt;
*'''Left''': The offset from the left side of the tab writable area.&lt;br /&gt;
*'''Width''': Window width of the label.&lt;br /&gt;
*'''Height''': Window height of the label.&lt;br /&gt;
&lt;br /&gt;
===ControlType variable===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''variable''', the contents of a Luup variable are displayed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with six keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the variable's value is placed in the tab), '''Service''' (a string containing the Service Id of the variable to be displayed), and '''Variable''' (a string containing the name of the variable to be displayed).&lt;br /&gt;
&lt;br /&gt;
===ControlType input===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''input''', a text box for the user to type a string is placed in the tab. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''ID''' &lt;br /&gt;
:A string which other controls (buttons) will use to refer to the string that the user has typed into this field. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object containing: &lt;br /&gt;
* '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers). This controls where the text box is placed in the tab.&lt;br /&gt;
* '''Service''', '''Variable''' (optional) A UPnP service and variable from which a default value is obtained&lt;br /&gt;
&lt;br /&gt;
===ControlType button===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''button''', a clickable button is placed at the specified coordinates. The button will appear selected (depressed) or unselected based on the value of a variable, permitting sets of buttons to act as radio buttons. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys: '''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. '''text''' is displayed as the text inside the button. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with up to seven keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), and (optionally) '''Value''' (a string, &amp;quot;1&amp;quot; if omitted). The specified variable's value controls whether the button appears selected (if the variable matches '''Value''') or not selected (if the variable doesn't match). &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching an input control on the page (for a user-entered parameter value).&lt;br /&gt;
&lt;br /&gt;
===ControlType slider===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''slider''', a draggable slider is placed at the specified coordinates. &lt;br /&gt;
&lt;br /&gt;
* '''Note:''' in UI5 and in UI7 (1.7.541 - release 7.0.5 and earlier), there can only be one horizontal slider (0% - 100%) per device. &lt;br /&gt;
&lt;br /&gt;
The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
* '''Display''' A JSON object with nine keys: &lt;br /&gt;
** '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab)&lt;br /&gt;
** '''Service''' (a string containing the Service Id of a variable)&lt;br /&gt;
** '''Variable''' (a string containing the name of a variable), &lt;br /&gt;
** '''MinValue''', '''MaxValue''' (both strings containing numbers, controlling what range of values the slider takes)&lt;br /&gt;
*** '''Note:''' in firmware 1.7.541 and earlier, MinValue and MaxValue are ignored and taken to be 0 and 100 % respectively.&lt;br /&gt;
** '''ID''' (a string, used in the '''Command''' to refer to the position the user has dragged the slider to). The specified variable's value is used to display the slider's current position. &lt;br /&gt;
* '''Command''' A JSON object with three keys: &lt;br /&gt;
** '''Service''' (a string containing the service Id of an action that this device can perform)&lt;br /&gt;
** '''Action''' (a string containing the name of the action)&lt;br /&gt;
** '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key &lt;br /&gt;
*** '''Name''' for the parameter name&amp;lt;br /&amp;gt;and either &lt;br /&gt;
*** '''Value''' (for a fixed parameter value)&amp;lt;br /&amp;gt;or &lt;br /&gt;
*** '''ID''' matching the slider's ID (for a value matching what the user has dragged the slider to). &lt;br /&gt;
* '''ControlHeader'''  (Optional) If present, must have string value &amp;quot;1&amp;quot;. When set, the slider's label and value are displayed. Otherwise only the slider is shown. (TODO: This is from reverse-engineering the JavaScript code. Test this.)&lt;br /&gt;
&lt;br /&gt;
===ControlType slider_vertical===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_Heater1.json, D_HVAC_ZoneThermostat1.json&lt;br /&gt;
&lt;br /&gt;
Parameters for slider_vertical are similar to slider. The first slider_vertical is red and used for heating. The second slider_vertical is blue and used for cooling.&lt;br /&gt;
&lt;br /&gt;
===ControlType checkbox===&lt;br /&gt;
&lt;br /&gt;
:As of UI5 firmware 1.5.622, checkboxes are only half-baked and not usable in &amp;quot;flash&amp;quot; style tabs. If you need check-boxes, radio buttons, pop-up menus, etc. in your tab, you need to create a Javascript tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType image===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType image_player===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType js_button===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
=UI7 Updates=&lt;br /&gt;
&lt;br /&gt;
===ControlType line_break===&lt;br /&gt;
This is used to force the next control to appear below the previous one rather than alongside it. See [[Luup plugins: Static JSON file]].&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘line_break’&lt;br /&gt;
&lt;br /&gt;
===ControlType multi_state_button=== &lt;br /&gt;
This is a new type of control in UI7 and works as a “toggle” button. It is used for On/Off, Armed/Disarmed etc buttons.&lt;br /&gt;
&lt;br /&gt;
As an example, here is the definition from the .json file of the Door/Window sensor needed to create this kind of control:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
&amp;quot;ControlType&amp;quot;: &amp;quot;multi_state_button&amp;quot;,&lt;br /&gt;
&amp;quot;top&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
&amp;quot;left&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;states&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
                  &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                      &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_arm&amp;quot;,&lt;br /&gt;
                      &amp;quot;text&amp;quot;: &amp;quot;Arm&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                         &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                           &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                   {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                           ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;arm&amp;quot;&lt;br /&gt;
              },&lt;br /&gt;
              {&lt;br /&gt;
                     &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                           &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_bypass&amp;quot;,&lt;br /&gt;
                           &amp;quot;text&amp;quot;: &amp;quot;Bypass&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                            ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;bypass&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
        ]&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘multi_state_button’&lt;br /&gt;
* '''top''' - an integer specifying the row from the control group where the control will be placed&lt;br /&gt;
* '''left''' - an integer which specifies which column will the control be placed in&lt;br /&gt;
* '''states''' - an array of objects, each object representing a different state for the button; each object has the following properties:&lt;br /&gt;
** '''Lable''' - a JSON object with two keys:&lt;br /&gt;
*** '''lang_tag''' - a string used for localization&lt;br /&gt;
*** '''text''' - the string displayed if localization fails&lt;br /&gt;
** '''Display''' - a JSON object with the following keys:&lt;br /&gt;
*** '''Service''' - a string containing the service id&lt;br /&gt;
*** '''Variable''' - a string containing the name of the variable&lt;br /&gt;
*** '''Value''' - a number which represents the value of the state in which the control must be in order for this state to be displayed/marked as selected&lt;br /&gt;
** '''Command''' - a JSON object with the following properties:&lt;br /&gt;
*** '''Service''' - a string with the service id of the action which this state will perform&lt;br /&gt;
*** '''Action''' - a string representing the name of the action to be executed&lt;br /&gt;
*** '''Parameters''' - an array of JSON objects holding the parameters to pass to the action&lt;br /&gt;
* '''ControlCode''' - a string containing the control code of this control (it is recommended to be unique for each device type)&lt;br /&gt;
&lt;br /&gt;
As you can see, the multi_state_button is a merge resulted from two controls of type button.&lt;br /&gt;
&lt;br /&gt;
[[File:MotionSensor.jpg|image|left]]&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_tabs</id>
		<title>Luup plugin tabs</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_tabs"/>
				<updated>2015-03-29T00:11:45Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: /* ControlType input */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Root Keys=&lt;br /&gt;
Refer to [[Luup_plugins:_Static_JSON_file|Root Keys]].&lt;br /&gt;
&lt;br /&gt;
=Tabs Key=&lt;br /&gt;
The '''Tabs''' key in the [[Luup plugins: Static JSON file|static JSON file]] performs two duties in the UI4 HTML interface: &lt;br /&gt;
&lt;br /&gt;
*It controls how the device appears on the '''Dashboard''' and '''Devices''' web pages.&lt;br /&gt;
*It controls how each tab appears in the the device's detailed dialog.&lt;br /&gt;
&lt;br /&gt;
The value of the '''Tabs''' key is a JSON array. Each array element is a JSON object (associative array). One for each Tab. &lt;br /&gt;
&lt;br /&gt;
==Tab fields==&lt;br /&gt;
These fields must be present for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object containing two keys:&lt;br /&gt;
*'''lang_tag''' (a string) is not displayed in the HTML interface but may be used by localized UIs.&lt;br /&gt;
*'''text''' (a string) is displayed in the tab's handle at the top of the dialog. &lt;br /&gt;
;'''Position''' &lt;br /&gt;
:A string which contains a number matching the relative position of this tab in relation to the other tabs. The leftmost tab has value &amp;quot;0&amp;quot;, the next tab &amp;quot;1&amp;quot;, and so on. &lt;br /&gt;
;'''TabType''' &lt;br /&gt;
:A string which describes how the content of the tab is to be generated. The value may be one of '''javascript''' or '''flash'''. These are described in the following sections.&lt;br /&gt;
&lt;br /&gt;
These fields are optional for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''ControlGroup'''&lt;br /&gt;
:Required only if the first tab is also used to display elements on the dashboard. See [[Luup_plugin_tabs#Dashboard_appearance|''Dashboard appearance'']] below.&lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
:Added in UI5.&lt;br /&gt;
&lt;br /&gt;
;'''Control'''&lt;br /&gt;
:An array of controls for each tab. Each control consists of:&lt;br /&gt;
*'''ControlType''' - the Control Types are described further below. &lt;br /&gt;
*'''ControlGroup''' - a reference to the control's ControlGroup &lt;br /&gt;
*'''text_align''' - align left or right &lt;br /&gt;
*'''top''' - dashboard position in button units&lt;br /&gt;
*'''left''' - dashboard position in button units&lt;br /&gt;
&lt;br /&gt;
eg a slider sitting above a two by two array of buttons:&lt;br /&gt;
&lt;br /&gt;
slider: top = 0, left = 0&amp;lt;br\&amp;gt;&lt;br /&gt;
top left button: top = 1.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
top right button: top = 1.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
bot left button: top = 2.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
bot right button: top = 2.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TabType javascript===&lt;br /&gt;
The '''javascript''' tab type executes client-side JavaScript, producing an HTML fragment which forms the body of the tab. These additional keys are required in the tab JSON object: &lt;br /&gt;
&lt;br /&gt;
;'''ScriptName''' &lt;br /&gt;
:The name of the JavaScript file. Conventionally, plugin JavaScript files are named '''J_PluginName.js'''. &lt;br /&gt;
;'''Function''' &lt;br /&gt;
:The function inside the JavaScript file which will be called to produce the HTML.&lt;br /&gt;
&lt;br /&gt;
The JavaScript code has access to most Luup variables through the [[JavaScript API]]. &lt;br /&gt;
&lt;br /&gt;
===TabType flash===&lt;br /&gt;
The '''flash''' tab type creates a tab body with a simple page-description language. Each component of the page is placed into the body at the position specified. You can't place a '''Control''' on the dashboard unless you place it in a '''ControlGroup''', which in turn must be placed in a '''SceneGroup''': &lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
&lt;br /&gt;
''SceneGroup'' (added in UI5) is similar to a ControlGroup, but with some key differences: it is customizable (by setting its position and dimensions) and it defines the behavior of the buttons assigned to the same state variable. If two buttons in the same SceneGroup are assigned to the same variable, only one of them can be activated in the Scene Editor (radio buttons behavior). If two buttons assigned to the same variable are in different SceneGroups, both can be activated at the same time in the Scene Editor (checkboxes behavior). &lt;br /&gt;
&lt;br /&gt;
It has the following tags: &lt;br /&gt;
&lt;br /&gt;
*'''id''': A numeric value (positive integer numbers). &lt;br /&gt;
*'''top''': The offset from the device header. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''left''': The offset from the device icon. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''x''', '''y''': the dimensions of the SceneGroup. e.g If I have two horizontally aligned buttons in the SceneGroup, ''x'' will be ''2'' and ''y'' will be ''1''.&lt;br /&gt;
&lt;br /&gt;
A ''SceneGroup'' can contain one or more ''ControlGroups''. &lt;br /&gt;
&lt;br /&gt;
===Dashboard appearance===&lt;br /&gt;
The first array element in the '''Tabs''' field is special. If it is of type '''flash''' then a subset of the controls on the tab can be chosen to display on the UI4 dashboard.&lt;br /&gt;
&lt;br /&gt;
The dashboard box for a device has room for two rows of information and controls. These are called ''Control Groups''.&lt;br /&gt;
&lt;br /&gt;
The tab object must contain the key '''ControlGroup''', which specifies how the dashboard's two available rows of information are to be handled. '''ControlGroup''' is a JSON array containing JSON objects (associative arrays). Each object has the following keys:&lt;br /&gt;
&lt;br /&gt;
;'''id''' &lt;br /&gt;
:A string containing a number, &amp;quot;1&amp;quot; for the first object, &amp;quot;2&amp;quot; for the second, and so on. &lt;br /&gt;
;'''isSingle''' &lt;br /&gt;
:UI4: If this key is present and contains the value &amp;quot;1&amp;quot;, then this control group is combined with other control groups that have '''isSingle''' similarly set onto the same row. For an example of this, see the On/Off buttons of the Binary Switch and Dimmable Switch static JSON files. &lt;br /&gt;
:UI5: Since controls cannot be grouped in drop-down lists anymore, this tag has a different role in UI5. When isSingle is &amp;quot;1&amp;quot;, the buttons in the ControlGroup will have a background (e.g. the BinaryLight buttons). If isSingle is missing, the buttons won't have a background. That is the button shape is not shown; only the button text will show. &lt;br /&gt;
;'''type''' &lt;br /&gt;
:If this key is present and contains the value &amp;quot;info&amp;quot;, then the controls displayed on this row are not clickable. If this key is absent, buttons may be used in the control group.&lt;br /&gt;
&lt;br /&gt;
===Making a control appear on the dashboard===&lt;br /&gt;
All control types (label, variable, button, input, slider) can appear in the dashboard. However, by default, controls in the first tab's detail view do not appear in the dashboard. To make a control's text appear in the dashboard, add a '''ControlGroup''' to the control's JSON object. The value is a string containing a number, matching the '''id''' of one of the Tab's overall '''ControlGroup''' key. The dashboard will concatenate all of a control group's controls in the order they are presented in the '''Control''' array, and display them in a single row of text. &lt;br /&gt;
&lt;br /&gt;
===Keeping controls together===&lt;br /&gt;
Some controls may not make sense if presented individually (for instance, an input for an alarm PIN and a &amp;quot;disarm&amp;quot; button). To keep these controls together, add a '''ControlPair''' key to all of the controls that have to appear together. Use the same value (a number in a string) for all of them.&lt;br /&gt;
&lt;br /&gt;
==Control Types==&lt;br /&gt;
&lt;br /&gt;
The '''Control''' array contains JSON objects (associative arrays), one per item in the tab body. An item is either a static text label, a variable, an input field, or a button or slider. This is handled by setting the '''ControlType''' key on the object. &lt;br /&gt;
&lt;br /&gt;
===ControlType label===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''label''', a fixed label is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys:&lt;br /&gt;
*'''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. The key is optional.&lt;br /&gt;
*'''text''' is displayed as the content of the label. In UI5 on the &amp;quot;Dashboard&amp;quot;, the text is rendered by passing it in the &amp;quot;Title&amp;quot; attribute of a Div statement, so it can only be plain text, (as opposed to say HTML). However in the tabs, it is placed in a Div statement so HTML, such as a URL link, can be rendered. &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys, which are strings containing numbers (in pixels?). This controls where the label is placed on the tab. The label is left aligned.&lt;br /&gt;
*'''Top''': The offset from the top of tab writable area.&lt;br /&gt;
*'''Left''': The offset from the left side of the tab writable area.&lt;br /&gt;
*'''Width''': Window width of the label.&lt;br /&gt;
*'''Height''': Window height of the label.&lt;br /&gt;
&lt;br /&gt;
===ControlType variable===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''variable''', the contents of a Luup variable are displayed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with six keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the variable's value is placed in the tab), '''Service''' (a string containing the Service Id of the variable to be displayed), and '''Variable''' (a string containing the name of the variable to be displayed).&lt;br /&gt;
&lt;br /&gt;
===ControlType input===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''input''', a text box for the user to type a string is placed in the tab. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''ID''' &lt;br /&gt;
:A string which other controls (buttons) will use to refer to the string that the user has typed into this field. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object containing: &lt;br /&gt;
* '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers). This controls where the text box is placed in the tab.&lt;br /&gt;
* '''Service''', '''Variable''' (optional) A UPnP service and variable from which a default value is obtained&lt;br /&gt;
&lt;br /&gt;
===ControlType button===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''button''', a clickable button is placed at the specified coordinates. The button will appear selected (depressed) or unselected based on the value of a variable, permitting sets of buttons to act as radio buttons. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys: '''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. '''text''' is displayed as the text inside the button. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with up to seven keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), and (optionally) '''Value''' (a string, &amp;quot;1&amp;quot; if omitted). The specified variable's value controls whether the button appears selected (if the variable matches '''Value''') or not selected (if the variable doesn't match). &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching an input control on the page (for a user-entered parameter value).&lt;br /&gt;
&lt;br /&gt;
===ControlType slider===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''slider''', a draggable slider is placed at the specified coordinates. &lt;br /&gt;
&lt;br /&gt;
* '''Note:''' in UI5 and in UI7 (1.7.541 - release 7.0.5 and earlier), there can only be one horizontal slider (0% - 100%) per device. &lt;br /&gt;
&lt;br /&gt;
The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
* '''Display''' A JSON object with nine keys: &lt;br /&gt;
** '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab)&lt;br /&gt;
** '''Service''' (a string containing the Service Id of a variable)&lt;br /&gt;
** '''Variable''' (a string containing the name of a variable), &lt;br /&gt;
** '''MinValue''', '''MaxValue''' (both strings containing numbers, controlling what range of values the slider takes)&lt;br /&gt;
** '''ID''' (a string, used in the '''Command''' to refer to the position the user has dragged the slider to). The specified variable's value is used to display the slider's current position. &lt;br /&gt;
* '''Command''' A JSON object with three keys: &lt;br /&gt;
** '''Service''' (a string containing the service Id of an action that this device can perform)&lt;br /&gt;
** '''Action''' (a string containing the name of the action)&lt;br /&gt;
** '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key &lt;br /&gt;
*** '''Name''' for the parameter name&amp;lt;br /&amp;gt;and either &lt;br /&gt;
*** '''Value''' (for a fixed parameter value)&amp;lt;br /&amp;gt;or &lt;br /&gt;
*** '''ID''' matching the slider's ID (for a value matching what the user has dragged the slider to). &lt;br /&gt;
* '''ControlHeader'''  (Optional) If present, must have string value &amp;quot;1&amp;quot;. When set, the slider's label and value are displayed. Otherwise only the slider is shown. (TODO: This is from reverse-engineering the JavaScript code. Test this.)&lt;br /&gt;
&lt;br /&gt;
===ControlType slider_vertical===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_Heater1.json, D_HVAC_ZoneThermostat1.json&lt;br /&gt;
&lt;br /&gt;
Parameters for slider_vertical are similar to slider. The first slider_vertical is red and used for heating. The second slider_vertical is blue and used for cooling.&lt;br /&gt;
&lt;br /&gt;
===ControlType checkbox===&lt;br /&gt;
&lt;br /&gt;
:As of UI5 firmware 1.5.622, checkboxes are only half-baked and not usable in &amp;quot;flash&amp;quot; style tabs. If you need check-boxes, radio buttons, pop-up menus, etc. in your tab, you need to create a Javascript tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType image===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType image_player===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType js_button===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
=UI7 Updates=&lt;br /&gt;
&lt;br /&gt;
===ControlType line_break===&lt;br /&gt;
This is used to force the next control to appear below the previous one rather than alongside it. See [[Luup plugins: Static JSON file]].&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘line_break’&lt;br /&gt;
&lt;br /&gt;
===ControlType multi_state_button=== &lt;br /&gt;
This is a new type of control in UI7 and works as a “toggle” button. It is used for On/Off, Armed/Disarmed etc buttons.&lt;br /&gt;
&lt;br /&gt;
As an example, here is the definition from the .json file of the Door/Window sensor needed to create this kind of control:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
&amp;quot;ControlType&amp;quot;: &amp;quot;multi_state_button&amp;quot;,&lt;br /&gt;
&amp;quot;top&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
&amp;quot;left&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;states&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
                  &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                      &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_arm&amp;quot;,&lt;br /&gt;
                      &amp;quot;text&amp;quot;: &amp;quot;Arm&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                         &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                           &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                   {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                           ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;arm&amp;quot;&lt;br /&gt;
              },&lt;br /&gt;
              {&lt;br /&gt;
                     &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                           &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_bypass&amp;quot;,&lt;br /&gt;
                           &amp;quot;text&amp;quot;: &amp;quot;Bypass&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                            ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;bypass&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
        ]&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘multi_state_button’&lt;br /&gt;
* '''top''' - an integer specifying the row from the control group where the control will be placed&lt;br /&gt;
* '''left''' - an integer which specifies which column will the control be placed in&lt;br /&gt;
* '''states''' - an array of objects, each object representing a different state for the button; each object has the following properties:&lt;br /&gt;
** '''Lable''' - a JSON object with two keys:&lt;br /&gt;
*** '''lang_tag''' - a string used for localization&lt;br /&gt;
*** '''text''' - the string displayed if localization fails&lt;br /&gt;
** '''Display''' - a JSON object with the following keys:&lt;br /&gt;
*** '''Service''' - a string containing the service id&lt;br /&gt;
*** '''Variable''' - a string containing the name of the variable&lt;br /&gt;
*** '''Value''' - a number which represents the value of the state in which the control must be in order for this state to be displayed/marked as selected&lt;br /&gt;
** '''Command''' - a JSON object with the following properties:&lt;br /&gt;
*** '''Service''' - a string with the service id of the action which this state will perform&lt;br /&gt;
*** '''Action''' - a string representing the name of the action to be executed&lt;br /&gt;
*** '''Parameters''' - an array of JSON objects holding the parameters to pass to the action&lt;br /&gt;
* '''ControlCode''' - a string containing the control code of this control (it is recommended to be unique for each device type)&lt;br /&gt;
&lt;br /&gt;
As you can see, the multi_state_button is a merge resulted from two controls of type button.&lt;br /&gt;
&lt;br /&gt;
[[File:MotionSensor.jpg|image|left]]&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_tabs</id>
		<title>Luup plugin tabs</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_tabs"/>
				<updated>2015-03-28T23:29:10Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: /* ControlType slider */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Root Keys=&lt;br /&gt;
Refer to [[Luup_plugins:_Static_JSON_file|Root Keys]].&lt;br /&gt;
&lt;br /&gt;
=Tabs Key=&lt;br /&gt;
The '''Tabs''' key in the [[Luup plugins: Static JSON file|static JSON file]] performs two duties in the UI4 HTML interface: &lt;br /&gt;
&lt;br /&gt;
*It controls how the device appears on the '''Dashboard''' and '''Devices''' web pages.&lt;br /&gt;
*It controls how each tab appears in the the device's detailed dialog.&lt;br /&gt;
&lt;br /&gt;
The value of the '''Tabs''' key is a JSON array. Each array element is a JSON object (associative array). One for each Tab. &lt;br /&gt;
&lt;br /&gt;
==Tab fields==&lt;br /&gt;
These fields must be present for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object containing two keys:&lt;br /&gt;
*'''lang_tag''' (a string) is not displayed in the HTML interface but may be used by localized UIs.&lt;br /&gt;
*'''text''' (a string) is displayed in the tab's handle at the top of the dialog. &lt;br /&gt;
;'''Position''' &lt;br /&gt;
:A string which contains a number matching the relative position of this tab in relation to the other tabs. The leftmost tab has value &amp;quot;0&amp;quot;, the next tab &amp;quot;1&amp;quot;, and so on. &lt;br /&gt;
;'''TabType''' &lt;br /&gt;
:A string which describes how the content of the tab is to be generated. The value may be one of '''javascript''' or '''flash'''. These are described in the following sections.&lt;br /&gt;
&lt;br /&gt;
These fields are optional for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''ControlGroup'''&lt;br /&gt;
:Required only if the first tab is also used to display elements on the dashboard. See [[Luup_plugin_tabs#Dashboard_appearance|''Dashboard appearance'']] below.&lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
:Added in UI5.&lt;br /&gt;
&lt;br /&gt;
;'''Control'''&lt;br /&gt;
:An array of controls for each tab. Each control consists of:&lt;br /&gt;
*'''ControlType''' - the Control Types are described further below. &lt;br /&gt;
*'''ControlGroup''' - a reference to the control's ControlGroup &lt;br /&gt;
*'''text_align''' - align left or right &lt;br /&gt;
*'''top''' - dashboard position in button units&lt;br /&gt;
*'''left''' - dashboard position in button units&lt;br /&gt;
&lt;br /&gt;
eg a slider sitting above a two by two array of buttons:&lt;br /&gt;
&lt;br /&gt;
slider: top = 0, left = 0&amp;lt;br\&amp;gt;&lt;br /&gt;
top left button: top = 1.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
top right button: top = 1.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
bot left button: top = 2.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
bot right button: top = 2.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TabType javascript===&lt;br /&gt;
The '''javascript''' tab type executes client-side JavaScript, producing an HTML fragment which forms the body of the tab. These additional keys are required in the tab JSON object: &lt;br /&gt;
&lt;br /&gt;
;'''ScriptName''' &lt;br /&gt;
:The name of the JavaScript file. Conventionally, plugin JavaScript files are named '''J_PluginName.js'''. &lt;br /&gt;
;'''Function''' &lt;br /&gt;
:The function inside the JavaScript file which will be called to produce the HTML.&lt;br /&gt;
&lt;br /&gt;
The JavaScript code has access to most Luup variables through the [[JavaScript API]]. &lt;br /&gt;
&lt;br /&gt;
===TabType flash===&lt;br /&gt;
The '''flash''' tab type creates a tab body with a simple page-description language. Each component of the page is placed into the body at the position specified. You can't place a '''Control''' on the dashboard unless you place it in a '''ControlGroup''', which in turn must be placed in a '''SceneGroup''': &lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
&lt;br /&gt;
''SceneGroup'' (added in UI5) is similar to a ControlGroup, but with some key differences: it is customizable (by setting its position and dimensions) and it defines the behavior of the buttons assigned to the same state variable. If two buttons in the same SceneGroup are assigned to the same variable, only one of them can be activated in the Scene Editor (radio buttons behavior). If two buttons assigned to the same variable are in different SceneGroups, both can be activated at the same time in the Scene Editor (checkboxes behavior). &lt;br /&gt;
&lt;br /&gt;
It has the following tags: &lt;br /&gt;
&lt;br /&gt;
*'''id''': A numeric value (positive integer numbers). &lt;br /&gt;
*'''top''': The offset from the device header. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''left''': The offset from the device icon. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''x''', '''y''': the dimensions of the SceneGroup. e.g If I have two horizontally aligned buttons in the SceneGroup, ''x'' will be ''2'' and ''y'' will be ''1''.&lt;br /&gt;
&lt;br /&gt;
A ''SceneGroup'' can contain one or more ''ControlGroups''. &lt;br /&gt;
&lt;br /&gt;
===Dashboard appearance===&lt;br /&gt;
The first array element in the '''Tabs''' field is special. If it is of type '''flash''' then a subset of the controls on the tab can be chosen to display on the UI4 dashboard.&lt;br /&gt;
&lt;br /&gt;
The dashboard box for a device has room for two rows of information and controls. These are called ''Control Groups''.&lt;br /&gt;
&lt;br /&gt;
The tab object must contain the key '''ControlGroup''', which specifies how the dashboard's two available rows of information are to be handled. '''ControlGroup''' is a JSON array containing JSON objects (associative arrays). Each object has the following keys:&lt;br /&gt;
&lt;br /&gt;
;'''id''' &lt;br /&gt;
:A string containing a number, &amp;quot;1&amp;quot; for the first object, &amp;quot;2&amp;quot; for the second, and so on. &lt;br /&gt;
;'''isSingle''' &lt;br /&gt;
:UI4: If this key is present and contains the value &amp;quot;1&amp;quot;, then this control group is combined with other control groups that have '''isSingle''' similarly set onto the same row. For an example of this, see the On/Off buttons of the Binary Switch and Dimmable Switch static JSON files. &lt;br /&gt;
:UI5: Since controls cannot be grouped in drop-down lists anymore, this tag has a different role in UI5. When isSingle is &amp;quot;1&amp;quot;, the buttons in the ControlGroup will have a background (e.g. the BinaryLight buttons). If isSingle is missing, the buttons won't have a background. That is the button shape is not shown; only the button text will show. &lt;br /&gt;
;'''type''' &lt;br /&gt;
:If this key is present and contains the value &amp;quot;info&amp;quot;, then the controls displayed on this row are not clickable. If this key is absent, buttons may be used in the control group.&lt;br /&gt;
&lt;br /&gt;
===Making a control appear on the dashboard===&lt;br /&gt;
All control types (label, variable, button, input, slider) can appear in the dashboard. However, by default, controls in the first tab's detail view do not appear in the dashboard. To make a control's text appear in the dashboard, add a '''ControlGroup''' to the control's JSON object. The value is a string containing a number, matching the '''id''' of one of the Tab's overall '''ControlGroup''' key. The dashboard will concatenate all of a control group's controls in the order they are presented in the '''Control''' array, and display them in a single row of text. &lt;br /&gt;
&lt;br /&gt;
===Keeping controls together===&lt;br /&gt;
Some controls may not make sense if presented individually (for instance, an input for an alarm PIN and a &amp;quot;disarm&amp;quot; button). To keep these controls together, add a '''ControlPair''' key to all of the controls that have to appear together. Use the same value (a number in a string) for all of them.&lt;br /&gt;
&lt;br /&gt;
==Control Types==&lt;br /&gt;
&lt;br /&gt;
The '''Control''' array contains JSON objects (associative arrays), one per item in the tab body. An item is either a static text label, a variable, an input field, or a button or slider. This is handled by setting the '''ControlType''' key on the object. &lt;br /&gt;
&lt;br /&gt;
===ControlType label===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''label''', a fixed label is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys:&lt;br /&gt;
*'''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. The key is optional.&lt;br /&gt;
*'''text''' is displayed as the content of the label. In UI5 on the &amp;quot;Dashboard&amp;quot;, the text is rendered by passing it in the &amp;quot;Title&amp;quot; attribute of a Div statement, so it can only be plain text, (as opposed to say HTML). However in the tabs, it is placed in a Div statement so HTML, such as a URL link, can be rendered. &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys, which are strings containing numbers (in pixels?). This controls where the label is placed on the tab. The label is left aligned.&lt;br /&gt;
*'''Top''': The offset from the top of tab writable area.&lt;br /&gt;
*'''Left''': The offset from the left side of the tab writable area.&lt;br /&gt;
*'''Width''': Window width of the label.&lt;br /&gt;
*'''Height''': Window height of the label.&lt;br /&gt;
&lt;br /&gt;
===ControlType variable===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''variable''', the contents of a Luup variable are displayed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with six keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the variable's value is placed in the tab), '''Service''' (a string containing the Service Id of the variable to be displayed), and '''Variable''' (a string containing the name of the variable to be displayed).&lt;br /&gt;
&lt;br /&gt;
===ControlType input===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''input''', a text box for the user to type a string is placed in the tab. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''ID''' &lt;br /&gt;
:A string which other controls (buttons) will use to refer to the string that the user has typed into this field. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers). This controls where the text box is placed in the tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType button===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''button''', a clickable button is placed at the specified coordinates. The button will appear selected (depressed) or unselected based on the value of a variable, permitting sets of buttons to act as radio buttons. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys: '''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. '''text''' is displayed as the text inside the button. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with up to seven keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), and (optionally) '''Value''' (a string, &amp;quot;1&amp;quot; if omitted). The specified variable's value controls whether the button appears selected (if the variable matches '''Value''') or not selected (if the variable doesn't match). &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching an input control on the page (for a user-entered parameter value).&lt;br /&gt;
&lt;br /&gt;
===ControlType slider===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''slider''', a draggable slider is placed at the specified coordinates. &lt;br /&gt;
&lt;br /&gt;
* '''Note:''' in UI5 and in UI7 (1.7.541 - release 7.0.5 and earlier), there can only be one horizontal slider (0% - 100%) per device. &lt;br /&gt;
&lt;br /&gt;
The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
* '''Display''' A JSON object with nine keys: &lt;br /&gt;
** '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab)&lt;br /&gt;
** '''Service''' (a string containing the Service Id of a variable)&lt;br /&gt;
** '''Variable''' (a string containing the name of a variable), &lt;br /&gt;
** '''MinValue''', '''MaxValue''' (both strings containing numbers, controlling what range of values the slider takes)&lt;br /&gt;
** '''ID''' (a string, used in the '''Command''' to refer to the position the user has dragged the slider to). The specified variable's value is used to display the slider's current position. &lt;br /&gt;
* '''Command''' A JSON object with three keys: &lt;br /&gt;
** '''Service''' (a string containing the service Id of an action that this device can perform)&lt;br /&gt;
** '''Action''' (a string containing the name of the action)&lt;br /&gt;
** '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key &lt;br /&gt;
*** '''Name''' for the parameter name&amp;lt;br /&amp;gt;and either &lt;br /&gt;
*** '''Value''' (for a fixed parameter value)&amp;lt;br /&amp;gt;or &lt;br /&gt;
*** '''ID''' matching the slider's ID (for a value matching what the user has dragged the slider to). &lt;br /&gt;
* '''ControlHeader'''  (Optional) If present, must have string value &amp;quot;1&amp;quot;. When set, the slider's label and value are displayed. Otherwise only the slider is shown. (TODO: This is from reverse-engineering the JavaScript code. Test this.)&lt;br /&gt;
&lt;br /&gt;
===ControlType slider_vertical===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_Heater1.json, D_HVAC_ZoneThermostat1.json&lt;br /&gt;
&lt;br /&gt;
Parameters for slider_vertical are similar to slider. The first slider_vertical is red and used for heating. The second slider_vertical is blue and used for cooling.&lt;br /&gt;
&lt;br /&gt;
===ControlType checkbox===&lt;br /&gt;
&lt;br /&gt;
:As of UI5 firmware 1.5.622, checkboxes are only half-baked and not usable in &amp;quot;flash&amp;quot; style tabs. If you need check-boxes, radio buttons, pop-up menus, etc. in your tab, you need to create a Javascript tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType image===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType image_player===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType js_button===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
=UI7 Updates=&lt;br /&gt;
&lt;br /&gt;
===ControlType line_break===&lt;br /&gt;
This is used to force the next control to appear below the previous one rather than alongside it. See [[Luup plugins: Static JSON file]].&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘line_break’&lt;br /&gt;
&lt;br /&gt;
===ControlType multi_state_button=== &lt;br /&gt;
This is a new type of control in UI7 and works as a “toggle” button. It is used for On/Off, Armed/Disarmed etc buttons.&lt;br /&gt;
&lt;br /&gt;
As an example, here is the definition from the .json file of the Door/Window sensor needed to create this kind of control:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
&amp;quot;ControlType&amp;quot;: &amp;quot;multi_state_button&amp;quot;,&lt;br /&gt;
&amp;quot;top&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
&amp;quot;left&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;states&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
                  &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                      &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_arm&amp;quot;,&lt;br /&gt;
                      &amp;quot;text&amp;quot;: &amp;quot;Arm&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                         &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                           &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                   {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                           ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;arm&amp;quot;&lt;br /&gt;
              },&lt;br /&gt;
              {&lt;br /&gt;
                     &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                           &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_bypass&amp;quot;,&lt;br /&gt;
                           &amp;quot;text&amp;quot;: &amp;quot;Bypass&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                            ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;bypass&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
        ]&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘multi_state_button’&lt;br /&gt;
* '''top''' - an integer specifying the row from the control group where the control will be placed&lt;br /&gt;
* '''left''' - an integer which specifies which column will the control be placed in&lt;br /&gt;
* '''states''' - an array of objects, each object representing a different state for the button; each object has the following properties:&lt;br /&gt;
** '''Lable''' - a JSON object with two keys:&lt;br /&gt;
*** '''lang_tag''' - a string used for localization&lt;br /&gt;
*** '''text''' - the string displayed if localization fails&lt;br /&gt;
** '''Display''' - a JSON object with the following keys:&lt;br /&gt;
*** '''Service''' - a string containing the service id&lt;br /&gt;
*** '''Variable''' - a string containing the name of the variable&lt;br /&gt;
*** '''Value''' - a number which represents the value of the state in which the control must be in order for this state to be displayed/marked as selected&lt;br /&gt;
** '''Command''' - a JSON object with the following properties:&lt;br /&gt;
*** '''Service''' - a string with the service id of the action which this state will perform&lt;br /&gt;
*** '''Action''' - a string representing the name of the action to be executed&lt;br /&gt;
*** '''Parameters''' - an array of JSON objects holding the parameters to pass to the action&lt;br /&gt;
* '''ControlCode''' - a string containing the control code of this control (it is recommended to be unique for each device type)&lt;br /&gt;
&lt;br /&gt;
As you can see, the multi_state_button is a merge resulted from two controls of type button.&lt;br /&gt;
&lt;br /&gt;
[[File:MotionSensor.jpg|image|left]]&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_tabs</id>
		<title>Luup plugin tabs</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_tabs"/>
				<updated>2015-03-28T23:26:37Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: /* ControlType slider_vertical */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Root Keys=&lt;br /&gt;
Refer to [[Luup_plugins:_Static_JSON_file|Root Keys]].&lt;br /&gt;
&lt;br /&gt;
=Tabs Key=&lt;br /&gt;
The '''Tabs''' key in the [[Luup plugins: Static JSON file|static JSON file]] performs two duties in the UI4 HTML interface: &lt;br /&gt;
&lt;br /&gt;
*It controls how the device appears on the '''Dashboard''' and '''Devices''' web pages.&lt;br /&gt;
*It controls how each tab appears in the the device's detailed dialog.&lt;br /&gt;
&lt;br /&gt;
The value of the '''Tabs''' key is a JSON array. Each array element is a JSON object (associative array). One for each Tab. &lt;br /&gt;
&lt;br /&gt;
==Tab fields==&lt;br /&gt;
These fields must be present for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object containing two keys:&lt;br /&gt;
*'''lang_tag''' (a string) is not displayed in the HTML interface but may be used by localized UIs.&lt;br /&gt;
*'''text''' (a string) is displayed in the tab's handle at the top of the dialog. &lt;br /&gt;
;'''Position''' &lt;br /&gt;
:A string which contains a number matching the relative position of this tab in relation to the other tabs. The leftmost tab has value &amp;quot;0&amp;quot;, the next tab &amp;quot;1&amp;quot;, and so on. &lt;br /&gt;
;'''TabType''' &lt;br /&gt;
:A string which describes how the content of the tab is to be generated. The value may be one of '''javascript''' or '''flash'''. These are described in the following sections.&lt;br /&gt;
&lt;br /&gt;
These fields are optional for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''ControlGroup'''&lt;br /&gt;
:Required only if the first tab is also used to display elements on the dashboard. See [[Luup_plugin_tabs#Dashboard_appearance|''Dashboard appearance'']] below.&lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
:Added in UI5.&lt;br /&gt;
&lt;br /&gt;
;'''Control'''&lt;br /&gt;
:An array of controls for each tab. Each control consists of:&lt;br /&gt;
*'''ControlType''' - the Control Types are described further below. &lt;br /&gt;
*'''ControlGroup''' - a reference to the control's ControlGroup &lt;br /&gt;
*'''text_align''' - align left or right &lt;br /&gt;
*'''top''' - dashboard position in button units&lt;br /&gt;
*'''left''' - dashboard position in button units&lt;br /&gt;
&lt;br /&gt;
eg a slider sitting above a two by two array of buttons:&lt;br /&gt;
&lt;br /&gt;
slider: top = 0, left = 0&amp;lt;br\&amp;gt;&lt;br /&gt;
top left button: top = 1.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
top right button: top = 1.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
bot left button: top = 2.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
bot right button: top = 2.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TabType javascript===&lt;br /&gt;
The '''javascript''' tab type executes client-side JavaScript, producing an HTML fragment which forms the body of the tab. These additional keys are required in the tab JSON object: &lt;br /&gt;
&lt;br /&gt;
;'''ScriptName''' &lt;br /&gt;
:The name of the JavaScript file. Conventionally, plugin JavaScript files are named '''J_PluginName.js'''. &lt;br /&gt;
;'''Function''' &lt;br /&gt;
:The function inside the JavaScript file which will be called to produce the HTML.&lt;br /&gt;
&lt;br /&gt;
The JavaScript code has access to most Luup variables through the [[JavaScript API]]. &lt;br /&gt;
&lt;br /&gt;
===TabType flash===&lt;br /&gt;
The '''flash''' tab type creates a tab body with a simple page-description language. Each component of the page is placed into the body at the position specified. You can't place a '''Control''' on the dashboard unless you place it in a '''ControlGroup''', which in turn must be placed in a '''SceneGroup''': &lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
&lt;br /&gt;
''SceneGroup'' (added in UI5) is similar to a ControlGroup, but with some key differences: it is customizable (by setting its position and dimensions) and it defines the behavior of the buttons assigned to the same state variable. If two buttons in the same SceneGroup are assigned to the same variable, only one of them can be activated in the Scene Editor (radio buttons behavior). If two buttons assigned to the same variable are in different SceneGroups, both can be activated at the same time in the Scene Editor (checkboxes behavior). &lt;br /&gt;
&lt;br /&gt;
It has the following tags: &lt;br /&gt;
&lt;br /&gt;
*'''id''': A numeric value (positive integer numbers). &lt;br /&gt;
*'''top''': The offset from the device header. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''left''': The offset from the device icon. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''x''', '''y''': the dimensions of the SceneGroup. e.g If I have two horizontally aligned buttons in the SceneGroup, ''x'' will be ''2'' and ''y'' will be ''1''.&lt;br /&gt;
&lt;br /&gt;
A ''SceneGroup'' can contain one or more ''ControlGroups''. &lt;br /&gt;
&lt;br /&gt;
===Dashboard appearance===&lt;br /&gt;
The first array element in the '''Tabs''' field is special. If it is of type '''flash''' then a subset of the controls on the tab can be chosen to display on the UI4 dashboard.&lt;br /&gt;
&lt;br /&gt;
The dashboard box for a device has room for two rows of information and controls. These are called ''Control Groups''.&lt;br /&gt;
&lt;br /&gt;
The tab object must contain the key '''ControlGroup''', which specifies how the dashboard's two available rows of information are to be handled. '''ControlGroup''' is a JSON array containing JSON objects (associative arrays). Each object has the following keys:&lt;br /&gt;
&lt;br /&gt;
;'''id''' &lt;br /&gt;
:A string containing a number, &amp;quot;1&amp;quot; for the first object, &amp;quot;2&amp;quot; for the second, and so on. &lt;br /&gt;
;'''isSingle''' &lt;br /&gt;
:UI4: If this key is present and contains the value &amp;quot;1&amp;quot;, then this control group is combined with other control groups that have '''isSingle''' similarly set onto the same row. For an example of this, see the On/Off buttons of the Binary Switch and Dimmable Switch static JSON files. &lt;br /&gt;
:UI5: Since controls cannot be grouped in drop-down lists anymore, this tag has a different role in UI5. When isSingle is &amp;quot;1&amp;quot;, the buttons in the ControlGroup will have a background (e.g. the BinaryLight buttons). If isSingle is missing, the buttons won't have a background. That is the button shape is not shown; only the button text will show. &lt;br /&gt;
;'''type''' &lt;br /&gt;
:If this key is present and contains the value &amp;quot;info&amp;quot;, then the controls displayed on this row are not clickable. If this key is absent, buttons may be used in the control group.&lt;br /&gt;
&lt;br /&gt;
===Making a control appear on the dashboard===&lt;br /&gt;
All control types (label, variable, button, input, slider) can appear in the dashboard. However, by default, controls in the first tab's detail view do not appear in the dashboard. To make a control's text appear in the dashboard, add a '''ControlGroup''' to the control's JSON object. The value is a string containing a number, matching the '''id''' of one of the Tab's overall '''ControlGroup''' key. The dashboard will concatenate all of a control group's controls in the order they are presented in the '''Control''' array, and display them in a single row of text. &lt;br /&gt;
&lt;br /&gt;
===Keeping controls together===&lt;br /&gt;
Some controls may not make sense if presented individually (for instance, an input for an alarm PIN and a &amp;quot;disarm&amp;quot; button). To keep these controls together, add a '''ControlPair''' key to all of the controls that have to appear together. Use the same value (a number in a string) for all of them.&lt;br /&gt;
&lt;br /&gt;
==Control Types==&lt;br /&gt;
&lt;br /&gt;
The '''Control''' array contains JSON objects (associative arrays), one per item in the tab body. An item is either a static text label, a variable, an input field, or a button or slider. This is handled by setting the '''ControlType''' key on the object. &lt;br /&gt;
&lt;br /&gt;
===ControlType label===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''label''', a fixed label is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys:&lt;br /&gt;
*'''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. The key is optional.&lt;br /&gt;
*'''text''' is displayed as the content of the label. In UI5 on the &amp;quot;Dashboard&amp;quot;, the text is rendered by passing it in the &amp;quot;Title&amp;quot; attribute of a Div statement, so it can only be plain text, (as opposed to say HTML). However in the tabs, it is placed in a Div statement so HTML, such as a URL link, can be rendered. &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys, which are strings containing numbers (in pixels?). This controls where the label is placed on the tab. The label is left aligned.&lt;br /&gt;
*'''Top''': The offset from the top of tab writable area.&lt;br /&gt;
*'''Left''': The offset from the left side of the tab writable area.&lt;br /&gt;
*'''Width''': Window width of the label.&lt;br /&gt;
*'''Height''': Window height of the label.&lt;br /&gt;
&lt;br /&gt;
===ControlType variable===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''variable''', the contents of a Luup variable are displayed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with six keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the variable's value is placed in the tab), '''Service''' (a string containing the Service Id of the variable to be displayed), and '''Variable''' (a string containing the name of the variable to be displayed).&lt;br /&gt;
&lt;br /&gt;
===ControlType input===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''input''', a text box for the user to type a string is placed in the tab. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''ID''' &lt;br /&gt;
:A string which other controls (buttons) will use to refer to the string that the user has typed into this field. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers). This controls where the text box is placed in the tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType button===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''button''', a clickable button is placed at the specified coordinates. The button will appear selected (depressed) or unselected based on the value of a variable, permitting sets of buttons to act as radio buttons. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys: '''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. '''text''' is displayed as the text inside the button. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with up to seven keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), and (optionally) '''Value''' (a string, &amp;quot;1&amp;quot; if omitted). The specified variable's value controls whether the button appears selected (if the variable matches '''Value''') or not selected (if the variable doesn't match). &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching an input control on the page (for a user-entered parameter value).&lt;br /&gt;
&lt;br /&gt;
===ControlType slider===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''slider''', a draggable slider is placed at the specified coordinates. &lt;br /&gt;
&lt;br /&gt;
* '''Note:''' in UI5, there can only be one horizontal slider (0% - 100%) per device. &lt;br /&gt;
&lt;br /&gt;
The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
* '''Display''' A JSON object with nine keys: &lt;br /&gt;
** '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab)&lt;br /&gt;
** '''Service''' (a string containing the Service Id of a variable)&lt;br /&gt;
** '''Variable''' (a string containing the name of a variable), &lt;br /&gt;
** '''MinValue''', '''MaxValue''' (both strings containing numbers, controlling what range of values the slider takes)&lt;br /&gt;
** '''ID''' (a string, used in the '''Command''' to refer to the position the user has dragged the slider to). The specified variable's value is used to display the slider's current position. &lt;br /&gt;
* '''Command''' A JSON object with three keys: &lt;br /&gt;
** '''Service''' (a string containing the service Id of an action that this device can perform)&lt;br /&gt;
** '''Action''' (a string containing the name of the action)&lt;br /&gt;
** '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key &lt;br /&gt;
*** '''Name''' for the parameter name&amp;lt;br /&amp;gt;and either &lt;br /&gt;
*** '''Value''' (for a fixed parameter value)&amp;lt;br /&amp;gt;or &lt;br /&gt;
*** '''ID''' matching the slider's ID (for a value matching what the user has dragged the slider to). &lt;br /&gt;
* '''ControlHeader'''  (Optional) If present, must have string value &amp;quot;1&amp;quot;. When set, the slider's label and value are displayed. Otherwise only the slider is shown. (TODO: This is from reverse-engineering the JavaScript code. Test this.)&lt;br /&gt;
&lt;br /&gt;
===ControlType slider_vertical===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_Heater1.json, D_HVAC_ZoneThermostat1.json&lt;br /&gt;
&lt;br /&gt;
Parameters for slider_vertical are similar to slider. The first slider_vertical is red and used for heating. The second slider_vertical is blue and used for cooling.&lt;br /&gt;
&lt;br /&gt;
===ControlType checkbox===&lt;br /&gt;
&lt;br /&gt;
:As of UI5 firmware 1.5.622, checkboxes are only half-baked and not usable in &amp;quot;flash&amp;quot; style tabs. If you need check-boxes, radio buttons, pop-up menus, etc. in your tab, you need to create a Javascript tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType image===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType image_player===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType js_button===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
=UI7 Updates=&lt;br /&gt;
&lt;br /&gt;
===ControlType line_break===&lt;br /&gt;
This is used to force the next control to appear below the previous one rather than alongside it. See [[Luup plugins: Static JSON file]].&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘line_break’&lt;br /&gt;
&lt;br /&gt;
===ControlType multi_state_button=== &lt;br /&gt;
This is a new type of control in UI7 and works as a “toggle” button. It is used for On/Off, Armed/Disarmed etc buttons.&lt;br /&gt;
&lt;br /&gt;
As an example, here is the definition from the .json file of the Door/Window sensor needed to create this kind of control:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
&amp;quot;ControlType&amp;quot;: &amp;quot;multi_state_button&amp;quot;,&lt;br /&gt;
&amp;quot;top&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
&amp;quot;left&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;states&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
                  &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                      &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_arm&amp;quot;,&lt;br /&gt;
                      &amp;quot;text&amp;quot;: &amp;quot;Arm&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                         &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                           &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                   {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                           ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;arm&amp;quot;&lt;br /&gt;
              },&lt;br /&gt;
              {&lt;br /&gt;
                     &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                           &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_bypass&amp;quot;,&lt;br /&gt;
                           &amp;quot;text&amp;quot;: &amp;quot;Bypass&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                            ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;bypass&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
        ]&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘multi_state_button’&lt;br /&gt;
* '''top''' - an integer specifying the row from the control group where the control will be placed&lt;br /&gt;
* '''left''' - an integer which specifies which column will the control be placed in&lt;br /&gt;
* '''states''' - an array of objects, each object representing a different state for the button; each object has the following properties:&lt;br /&gt;
** '''Lable''' - a JSON object with two keys:&lt;br /&gt;
*** '''lang_tag''' - a string used for localization&lt;br /&gt;
*** '''text''' - the string displayed if localization fails&lt;br /&gt;
** '''Display''' - a JSON object with the following keys:&lt;br /&gt;
*** '''Service''' - a string containing the service id&lt;br /&gt;
*** '''Variable''' - a string containing the name of the variable&lt;br /&gt;
*** '''Value''' - a number which represents the value of the state in which the control must be in order for this state to be displayed/marked as selected&lt;br /&gt;
** '''Command''' - a JSON object with the following properties:&lt;br /&gt;
*** '''Service''' - a string with the service id of the action which this state will perform&lt;br /&gt;
*** '''Action''' - a string representing the name of the action to be executed&lt;br /&gt;
*** '''Parameters''' - an array of JSON objects holding the parameters to pass to the action&lt;br /&gt;
* '''ControlCode''' - a string containing the control code of this control (it is recommended to be unique for each device type)&lt;br /&gt;
&lt;br /&gt;
As you can see, the multi_state_button is a merge resulted from two controls of type button.&lt;br /&gt;
&lt;br /&gt;
[[File:MotionSensor.jpg|image|left]]&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_tabs</id>
		<title>Luup plugin tabs</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_tabs"/>
				<updated>2015-03-28T23:25:08Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: /* ControlType slider */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Root Keys=&lt;br /&gt;
Refer to [[Luup_plugins:_Static_JSON_file|Root Keys]].&lt;br /&gt;
&lt;br /&gt;
=Tabs Key=&lt;br /&gt;
The '''Tabs''' key in the [[Luup plugins: Static JSON file|static JSON file]] performs two duties in the UI4 HTML interface: &lt;br /&gt;
&lt;br /&gt;
*It controls how the device appears on the '''Dashboard''' and '''Devices''' web pages.&lt;br /&gt;
*It controls how each tab appears in the the device's detailed dialog.&lt;br /&gt;
&lt;br /&gt;
The value of the '''Tabs''' key is a JSON array. Each array element is a JSON object (associative array). One for each Tab. &lt;br /&gt;
&lt;br /&gt;
==Tab fields==&lt;br /&gt;
These fields must be present for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object containing two keys:&lt;br /&gt;
*'''lang_tag''' (a string) is not displayed in the HTML interface but may be used by localized UIs.&lt;br /&gt;
*'''text''' (a string) is displayed in the tab's handle at the top of the dialog. &lt;br /&gt;
;'''Position''' &lt;br /&gt;
:A string which contains a number matching the relative position of this tab in relation to the other tabs. The leftmost tab has value &amp;quot;0&amp;quot;, the next tab &amp;quot;1&amp;quot;, and so on. &lt;br /&gt;
;'''TabType''' &lt;br /&gt;
:A string which describes how the content of the tab is to be generated. The value may be one of '''javascript''' or '''flash'''. These are described in the following sections.&lt;br /&gt;
&lt;br /&gt;
These fields are optional for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''ControlGroup'''&lt;br /&gt;
:Required only if the first tab is also used to display elements on the dashboard. See [[Luup_plugin_tabs#Dashboard_appearance|''Dashboard appearance'']] below.&lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
:Added in UI5.&lt;br /&gt;
&lt;br /&gt;
;'''Control'''&lt;br /&gt;
:An array of controls for each tab. Each control consists of:&lt;br /&gt;
*'''ControlType''' - the Control Types are described further below. &lt;br /&gt;
*'''ControlGroup''' - a reference to the control's ControlGroup &lt;br /&gt;
*'''text_align''' - align left or right &lt;br /&gt;
*'''top''' - dashboard position in button units&lt;br /&gt;
*'''left''' - dashboard position in button units&lt;br /&gt;
&lt;br /&gt;
eg a slider sitting above a two by two array of buttons:&lt;br /&gt;
&lt;br /&gt;
slider: top = 0, left = 0&amp;lt;br\&amp;gt;&lt;br /&gt;
top left button: top = 1.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
top right button: top = 1.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
bot left button: top = 2.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
bot right button: top = 2.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TabType javascript===&lt;br /&gt;
The '''javascript''' tab type executes client-side JavaScript, producing an HTML fragment which forms the body of the tab. These additional keys are required in the tab JSON object: &lt;br /&gt;
&lt;br /&gt;
;'''ScriptName''' &lt;br /&gt;
:The name of the JavaScript file. Conventionally, plugin JavaScript files are named '''J_PluginName.js'''. &lt;br /&gt;
;'''Function''' &lt;br /&gt;
:The function inside the JavaScript file which will be called to produce the HTML.&lt;br /&gt;
&lt;br /&gt;
The JavaScript code has access to most Luup variables through the [[JavaScript API]]. &lt;br /&gt;
&lt;br /&gt;
===TabType flash===&lt;br /&gt;
The '''flash''' tab type creates a tab body with a simple page-description language. Each component of the page is placed into the body at the position specified. You can't place a '''Control''' on the dashboard unless you place it in a '''ControlGroup''', which in turn must be placed in a '''SceneGroup''': &lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
&lt;br /&gt;
''SceneGroup'' (added in UI5) is similar to a ControlGroup, but with some key differences: it is customizable (by setting its position and dimensions) and it defines the behavior of the buttons assigned to the same state variable. If two buttons in the same SceneGroup are assigned to the same variable, only one of them can be activated in the Scene Editor (radio buttons behavior). If two buttons assigned to the same variable are in different SceneGroups, both can be activated at the same time in the Scene Editor (checkboxes behavior). &lt;br /&gt;
&lt;br /&gt;
It has the following tags: &lt;br /&gt;
&lt;br /&gt;
*'''id''': A numeric value (positive integer numbers). &lt;br /&gt;
*'''top''': The offset from the device header. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''left''': The offset from the device icon. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''x''', '''y''': the dimensions of the SceneGroup. e.g If I have two horizontally aligned buttons in the SceneGroup, ''x'' will be ''2'' and ''y'' will be ''1''.&lt;br /&gt;
&lt;br /&gt;
A ''SceneGroup'' can contain one or more ''ControlGroups''. &lt;br /&gt;
&lt;br /&gt;
===Dashboard appearance===&lt;br /&gt;
The first array element in the '''Tabs''' field is special. If it is of type '''flash''' then a subset of the controls on the tab can be chosen to display on the UI4 dashboard.&lt;br /&gt;
&lt;br /&gt;
The dashboard box for a device has room for two rows of information and controls. These are called ''Control Groups''.&lt;br /&gt;
&lt;br /&gt;
The tab object must contain the key '''ControlGroup''', which specifies how the dashboard's two available rows of information are to be handled. '''ControlGroup''' is a JSON array containing JSON objects (associative arrays). Each object has the following keys:&lt;br /&gt;
&lt;br /&gt;
;'''id''' &lt;br /&gt;
:A string containing a number, &amp;quot;1&amp;quot; for the first object, &amp;quot;2&amp;quot; for the second, and so on. &lt;br /&gt;
;'''isSingle''' &lt;br /&gt;
:UI4: If this key is present and contains the value &amp;quot;1&amp;quot;, then this control group is combined with other control groups that have '''isSingle''' similarly set onto the same row. For an example of this, see the On/Off buttons of the Binary Switch and Dimmable Switch static JSON files. &lt;br /&gt;
:UI5: Since controls cannot be grouped in drop-down lists anymore, this tag has a different role in UI5. When isSingle is &amp;quot;1&amp;quot;, the buttons in the ControlGroup will have a background (e.g. the BinaryLight buttons). If isSingle is missing, the buttons won't have a background. That is the button shape is not shown; only the button text will show. &lt;br /&gt;
;'''type''' &lt;br /&gt;
:If this key is present and contains the value &amp;quot;info&amp;quot;, then the controls displayed on this row are not clickable. If this key is absent, buttons may be used in the control group.&lt;br /&gt;
&lt;br /&gt;
===Making a control appear on the dashboard===&lt;br /&gt;
All control types (label, variable, button, input, slider) can appear in the dashboard. However, by default, controls in the first tab's detail view do not appear in the dashboard. To make a control's text appear in the dashboard, add a '''ControlGroup''' to the control's JSON object. The value is a string containing a number, matching the '''id''' of one of the Tab's overall '''ControlGroup''' key. The dashboard will concatenate all of a control group's controls in the order they are presented in the '''Control''' array, and display them in a single row of text. &lt;br /&gt;
&lt;br /&gt;
===Keeping controls together===&lt;br /&gt;
Some controls may not make sense if presented individually (for instance, an input for an alarm PIN and a &amp;quot;disarm&amp;quot; button). To keep these controls together, add a '''ControlPair''' key to all of the controls that have to appear together. Use the same value (a number in a string) for all of them.&lt;br /&gt;
&lt;br /&gt;
==Control Types==&lt;br /&gt;
&lt;br /&gt;
The '''Control''' array contains JSON objects (associative arrays), one per item in the tab body. An item is either a static text label, a variable, an input field, or a button or slider. This is handled by setting the '''ControlType''' key on the object. &lt;br /&gt;
&lt;br /&gt;
===ControlType label===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''label''', a fixed label is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys:&lt;br /&gt;
*'''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. The key is optional.&lt;br /&gt;
*'''text''' is displayed as the content of the label. In UI5 on the &amp;quot;Dashboard&amp;quot;, the text is rendered by passing it in the &amp;quot;Title&amp;quot; attribute of a Div statement, so it can only be plain text, (as opposed to say HTML). However in the tabs, it is placed in a Div statement so HTML, such as a URL link, can be rendered. &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys, which are strings containing numbers (in pixels?). This controls where the label is placed on the tab. The label is left aligned.&lt;br /&gt;
*'''Top''': The offset from the top of tab writable area.&lt;br /&gt;
*'''Left''': The offset from the left side of the tab writable area.&lt;br /&gt;
*'''Width''': Window width of the label.&lt;br /&gt;
*'''Height''': Window height of the label.&lt;br /&gt;
&lt;br /&gt;
===ControlType variable===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''variable''', the contents of a Luup variable are displayed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with six keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the variable's value is placed in the tab), '''Service''' (a string containing the Service Id of the variable to be displayed), and '''Variable''' (a string containing the name of the variable to be displayed).&lt;br /&gt;
&lt;br /&gt;
===ControlType input===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''input''', a text box for the user to type a string is placed in the tab. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''ID''' &lt;br /&gt;
:A string which other controls (buttons) will use to refer to the string that the user has typed into this field. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers). This controls where the text box is placed in the tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType button===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''button''', a clickable button is placed at the specified coordinates. The button will appear selected (depressed) or unselected based on the value of a variable, permitting sets of buttons to act as radio buttons. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys: '''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. '''text''' is displayed as the text inside the button. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with up to seven keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), and (optionally) '''Value''' (a string, &amp;quot;1&amp;quot; if omitted). The specified variable's value controls whether the button appears selected (if the variable matches '''Value''') or not selected (if the variable doesn't match). &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching an input control on the page (for a user-entered parameter value).&lt;br /&gt;
&lt;br /&gt;
===ControlType slider===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''slider''', a draggable slider is placed at the specified coordinates. &lt;br /&gt;
&lt;br /&gt;
* '''Note:''' in UI5, there can only be one horizontal slider (0% - 100%) per device. &lt;br /&gt;
&lt;br /&gt;
The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
* '''Display''' A JSON object with nine keys: &lt;br /&gt;
** '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab)&lt;br /&gt;
** '''Service''' (a string containing the Service Id of a variable)&lt;br /&gt;
** '''Variable''' (a string containing the name of a variable), &lt;br /&gt;
** '''MinValue''', '''MaxValue''' (both strings containing numbers, controlling what range of values the slider takes)&lt;br /&gt;
** '''ID''' (a string, used in the '''Command''' to refer to the position the user has dragged the slider to). The specified variable's value is used to display the slider's current position. &lt;br /&gt;
* '''Command''' A JSON object with three keys: &lt;br /&gt;
** '''Service''' (a string containing the service Id of an action that this device can perform)&lt;br /&gt;
** '''Action''' (a string containing the name of the action)&lt;br /&gt;
** '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key &lt;br /&gt;
*** '''Name''' for the parameter name&amp;lt;br /&amp;gt;and either &lt;br /&gt;
*** '''Value''' (for a fixed parameter value)&amp;lt;br /&amp;gt;or &lt;br /&gt;
*** '''ID''' matching the slider's ID (for a value matching what the user has dragged the slider to). &lt;br /&gt;
* '''ControlHeader'''  (Optional) If present, must have string value &amp;quot;1&amp;quot;. When set, the slider's label and value are displayed. Otherwise only the slider is shown. (TODO: This is from reverse-engineering the JavaScript code. Test this.)&lt;br /&gt;
&lt;br /&gt;
===ControlType slider_vertical===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_Heater1.json, D_HVAC_ZoneThermostat1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType checkbox===&lt;br /&gt;
&lt;br /&gt;
:As of UI5 firmware 1.5.622, checkboxes are only half-baked and not usable in &amp;quot;flash&amp;quot; style tabs. If you need check-boxes, radio buttons, pop-up menus, etc. in your tab, you need to create a Javascript tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType image===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType image_player===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType js_button===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
=UI7 Updates=&lt;br /&gt;
&lt;br /&gt;
===ControlType line_break===&lt;br /&gt;
This is used to force the next control to appear below the previous one rather than alongside it. See [[Luup plugins: Static JSON file]].&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘line_break’&lt;br /&gt;
&lt;br /&gt;
===ControlType multi_state_button=== &lt;br /&gt;
This is a new type of control in UI7 and works as a “toggle” button. It is used for On/Off, Armed/Disarmed etc buttons.&lt;br /&gt;
&lt;br /&gt;
As an example, here is the definition from the .json file of the Door/Window sensor needed to create this kind of control:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
&amp;quot;ControlType&amp;quot;: &amp;quot;multi_state_button&amp;quot;,&lt;br /&gt;
&amp;quot;top&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
&amp;quot;left&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;states&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
                  &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                      &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_arm&amp;quot;,&lt;br /&gt;
                      &amp;quot;text&amp;quot;: &amp;quot;Arm&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                         &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                           &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                   {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                           ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;arm&amp;quot;&lt;br /&gt;
              },&lt;br /&gt;
              {&lt;br /&gt;
                     &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                           &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_bypass&amp;quot;,&lt;br /&gt;
                           &amp;quot;text&amp;quot;: &amp;quot;Bypass&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                            ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;bypass&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
        ]&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘multi_state_button’&lt;br /&gt;
* '''top''' - an integer specifying the row from the control group where the control will be placed&lt;br /&gt;
* '''left''' - an integer which specifies which column will the control be placed in&lt;br /&gt;
* '''states''' - an array of objects, each object representing a different state for the button; each object has the following properties:&lt;br /&gt;
** '''Lable''' - a JSON object with two keys:&lt;br /&gt;
*** '''lang_tag''' - a string used for localization&lt;br /&gt;
*** '''text''' - the string displayed if localization fails&lt;br /&gt;
** '''Display''' - a JSON object with the following keys:&lt;br /&gt;
*** '''Service''' - a string containing the service id&lt;br /&gt;
*** '''Variable''' - a string containing the name of the variable&lt;br /&gt;
*** '''Value''' - a number which represents the value of the state in which the control must be in order for this state to be displayed/marked as selected&lt;br /&gt;
** '''Command''' - a JSON object with the following properties:&lt;br /&gt;
*** '''Service''' - a string with the service id of the action which this state will perform&lt;br /&gt;
*** '''Action''' - a string representing the name of the action to be executed&lt;br /&gt;
*** '''Parameters''' - an array of JSON objects holding the parameters to pass to the action&lt;br /&gt;
* '''ControlCode''' - a string containing the control code of this control (it is recommended to be unique for each device type)&lt;br /&gt;
&lt;br /&gt;
As you can see, the multi_state_button is a merge resulted from two controls of type button.&lt;br /&gt;
&lt;br /&gt;
[[File:MotionSensor.jpg|image|left]]&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_tabs</id>
		<title>Luup plugin tabs</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_tabs"/>
				<updated>2015-03-28T23:24:29Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: /* ControlType slider */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Root Keys=&lt;br /&gt;
Refer to [[Luup_plugins:_Static_JSON_file|Root Keys]].&lt;br /&gt;
&lt;br /&gt;
=Tabs Key=&lt;br /&gt;
The '''Tabs''' key in the [[Luup plugins: Static JSON file|static JSON file]] performs two duties in the UI4 HTML interface: &lt;br /&gt;
&lt;br /&gt;
*It controls how the device appears on the '''Dashboard''' and '''Devices''' web pages.&lt;br /&gt;
*It controls how each tab appears in the the device's detailed dialog.&lt;br /&gt;
&lt;br /&gt;
The value of the '''Tabs''' key is a JSON array. Each array element is a JSON object (associative array). One for each Tab. &lt;br /&gt;
&lt;br /&gt;
==Tab fields==&lt;br /&gt;
These fields must be present for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object containing two keys:&lt;br /&gt;
*'''lang_tag''' (a string) is not displayed in the HTML interface but may be used by localized UIs.&lt;br /&gt;
*'''text''' (a string) is displayed in the tab's handle at the top of the dialog. &lt;br /&gt;
;'''Position''' &lt;br /&gt;
:A string which contains a number matching the relative position of this tab in relation to the other tabs. The leftmost tab has value &amp;quot;0&amp;quot;, the next tab &amp;quot;1&amp;quot;, and so on. &lt;br /&gt;
;'''TabType''' &lt;br /&gt;
:A string which describes how the content of the tab is to be generated. The value may be one of '''javascript''' or '''flash'''. These are described in the following sections.&lt;br /&gt;
&lt;br /&gt;
These fields are optional for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''ControlGroup'''&lt;br /&gt;
:Required only if the first tab is also used to display elements on the dashboard. See [[Luup_plugin_tabs#Dashboard_appearance|''Dashboard appearance'']] below.&lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
:Added in UI5.&lt;br /&gt;
&lt;br /&gt;
;'''Control'''&lt;br /&gt;
:An array of controls for each tab. Each control consists of:&lt;br /&gt;
*'''ControlType''' - the Control Types are described further below. &lt;br /&gt;
*'''ControlGroup''' - a reference to the control's ControlGroup &lt;br /&gt;
*'''text_align''' - align left or right &lt;br /&gt;
*'''top''' - dashboard position in button units&lt;br /&gt;
*'''left''' - dashboard position in button units&lt;br /&gt;
&lt;br /&gt;
eg a slider sitting above a two by two array of buttons:&lt;br /&gt;
&lt;br /&gt;
slider: top = 0, left = 0&amp;lt;br\&amp;gt;&lt;br /&gt;
top left button: top = 1.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
top right button: top = 1.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
bot left button: top = 2.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
bot right button: top = 2.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TabType javascript===&lt;br /&gt;
The '''javascript''' tab type executes client-side JavaScript, producing an HTML fragment which forms the body of the tab. These additional keys are required in the tab JSON object: &lt;br /&gt;
&lt;br /&gt;
;'''ScriptName''' &lt;br /&gt;
:The name of the JavaScript file. Conventionally, plugin JavaScript files are named '''J_PluginName.js'''. &lt;br /&gt;
;'''Function''' &lt;br /&gt;
:The function inside the JavaScript file which will be called to produce the HTML.&lt;br /&gt;
&lt;br /&gt;
The JavaScript code has access to most Luup variables through the [[JavaScript API]]. &lt;br /&gt;
&lt;br /&gt;
===TabType flash===&lt;br /&gt;
The '''flash''' tab type creates a tab body with a simple page-description language. Each component of the page is placed into the body at the position specified. You can't place a '''Control''' on the dashboard unless you place it in a '''ControlGroup''', which in turn must be placed in a '''SceneGroup''': &lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
&lt;br /&gt;
''SceneGroup'' (added in UI5) is similar to a ControlGroup, but with some key differences: it is customizable (by setting its position and dimensions) and it defines the behavior of the buttons assigned to the same state variable. If two buttons in the same SceneGroup are assigned to the same variable, only one of them can be activated in the Scene Editor (radio buttons behavior). If two buttons assigned to the same variable are in different SceneGroups, both can be activated at the same time in the Scene Editor (checkboxes behavior). &lt;br /&gt;
&lt;br /&gt;
It has the following tags: &lt;br /&gt;
&lt;br /&gt;
*'''id''': A numeric value (positive integer numbers). &lt;br /&gt;
*'''top''': The offset from the device header. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''left''': The offset from the device icon. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''x''', '''y''': the dimensions of the SceneGroup. e.g If I have two horizontally aligned buttons in the SceneGroup, ''x'' will be ''2'' and ''y'' will be ''1''.&lt;br /&gt;
&lt;br /&gt;
A ''SceneGroup'' can contain one or more ''ControlGroups''. &lt;br /&gt;
&lt;br /&gt;
===Dashboard appearance===&lt;br /&gt;
The first array element in the '''Tabs''' field is special. If it is of type '''flash''' then a subset of the controls on the tab can be chosen to display on the UI4 dashboard.&lt;br /&gt;
&lt;br /&gt;
The dashboard box for a device has room for two rows of information and controls. These are called ''Control Groups''.&lt;br /&gt;
&lt;br /&gt;
The tab object must contain the key '''ControlGroup''', which specifies how the dashboard's two available rows of information are to be handled. '''ControlGroup''' is a JSON array containing JSON objects (associative arrays). Each object has the following keys:&lt;br /&gt;
&lt;br /&gt;
;'''id''' &lt;br /&gt;
:A string containing a number, &amp;quot;1&amp;quot; for the first object, &amp;quot;2&amp;quot; for the second, and so on. &lt;br /&gt;
;'''isSingle''' &lt;br /&gt;
:UI4: If this key is present and contains the value &amp;quot;1&amp;quot;, then this control group is combined with other control groups that have '''isSingle''' similarly set onto the same row. For an example of this, see the On/Off buttons of the Binary Switch and Dimmable Switch static JSON files. &lt;br /&gt;
:UI5: Since controls cannot be grouped in drop-down lists anymore, this tag has a different role in UI5. When isSingle is &amp;quot;1&amp;quot;, the buttons in the ControlGroup will have a background (e.g. the BinaryLight buttons). If isSingle is missing, the buttons won't have a background. That is the button shape is not shown; only the button text will show. &lt;br /&gt;
;'''type''' &lt;br /&gt;
:If this key is present and contains the value &amp;quot;info&amp;quot;, then the controls displayed on this row are not clickable. If this key is absent, buttons may be used in the control group.&lt;br /&gt;
&lt;br /&gt;
===Making a control appear on the dashboard===&lt;br /&gt;
All control types (label, variable, button, input, slider) can appear in the dashboard. However, by default, controls in the first tab's detail view do not appear in the dashboard. To make a control's text appear in the dashboard, add a '''ControlGroup''' to the control's JSON object. The value is a string containing a number, matching the '''id''' of one of the Tab's overall '''ControlGroup''' key. The dashboard will concatenate all of a control group's controls in the order they are presented in the '''Control''' array, and display them in a single row of text. &lt;br /&gt;
&lt;br /&gt;
===Keeping controls together===&lt;br /&gt;
Some controls may not make sense if presented individually (for instance, an input for an alarm PIN and a &amp;quot;disarm&amp;quot; button). To keep these controls together, add a '''ControlPair''' key to all of the controls that have to appear together. Use the same value (a number in a string) for all of them.&lt;br /&gt;
&lt;br /&gt;
==Control Types==&lt;br /&gt;
&lt;br /&gt;
The '''Control''' array contains JSON objects (associative arrays), one per item in the tab body. An item is either a static text label, a variable, an input field, or a button or slider. This is handled by setting the '''ControlType''' key on the object. &lt;br /&gt;
&lt;br /&gt;
===ControlType label===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''label''', a fixed label is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys:&lt;br /&gt;
*'''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. The key is optional.&lt;br /&gt;
*'''text''' is displayed as the content of the label. In UI5 on the &amp;quot;Dashboard&amp;quot;, the text is rendered by passing it in the &amp;quot;Title&amp;quot; attribute of a Div statement, so it can only be plain text, (as opposed to say HTML). However in the tabs, it is placed in a Div statement so HTML, such as a URL link, can be rendered. &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys, which are strings containing numbers (in pixels?). This controls where the label is placed on the tab. The label is left aligned.&lt;br /&gt;
*'''Top''': The offset from the top of tab writable area.&lt;br /&gt;
*'''Left''': The offset from the left side of the tab writable area.&lt;br /&gt;
*'''Width''': Window width of the label.&lt;br /&gt;
*'''Height''': Window height of the label.&lt;br /&gt;
&lt;br /&gt;
===ControlType variable===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''variable''', the contents of a Luup variable are displayed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with six keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the variable's value is placed in the tab), '''Service''' (a string containing the Service Id of the variable to be displayed), and '''Variable''' (a string containing the name of the variable to be displayed).&lt;br /&gt;
&lt;br /&gt;
===ControlType input===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''input''', a text box for the user to type a string is placed in the tab. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''ID''' &lt;br /&gt;
:A string which other controls (buttons) will use to refer to the string that the user has typed into this field. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers). This controls where the text box is placed in the tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType button===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''button''', a clickable button is placed at the specified coordinates. The button will appear selected (depressed) or unselected based on the value of a variable, permitting sets of buttons to act as radio buttons. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys: '''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. '''text''' is displayed as the text inside the button. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with up to seven keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), and (optionally) '''Value''' (a string, &amp;quot;1&amp;quot; if omitted). The specified variable's value controls whether the button appears selected (if the variable matches '''Value''') or not selected (if the variable doesn't match). &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching an input control on the page (for a user-entered parameter value).&lt;br /&gt;
&lt;br /&gt;
===ControlType slider===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''slider''', a draggable slider is placed at the specified coordinates. &lt;br /&gt;
&lt;br /&gt;
* '''Note:''' in UI5, there can only be one horizontal slider (0% - 100%) per device. There can be at most two verical sliders. The first is red and used for heating. The second is blue and used for cooling. &lt;br /&gt;
&lt;br /&gt;
The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
* '''Display''' A JSON object with nine keys: &lt;br /&gt;
** '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab)&lt;br /&gt;
** '''Service''' (a string containing the Service Id of a variable)&lt;br /&gt;
** '''Variable''' (a string containing the name of a variable), &lt;br /&gt;
** '''MinValue''', '''MaxValue''' (both strings containing numbers, controlling what range of values the slider takes)&lt;br /&gt;
** '''ID''' (a string, used in the '''Command''' to refer to the position the user has dragged the slider to). The specified variable's value is used to display the slider's current position. &lt;br /&gt;
* '''Command''' A JSON object with three keys: &lt;br /&gt;
** '''Service''' (a string containing the service Id of an action that this device can perform)&lt;br /&gt;
** '''Action''' (a string containing the name of the action)&lt;br /&gt;
** '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key &lt;br /&gt;
*** '''Name''' for the parameter name&amp;lt;br /&amp;gt;and either &lt;br /&gt;
*** '''Value''' (for a fixed parameter value)&amp;lt;br /&amp;gt;or &lt;br /&gt;
*** '''ID''' matching the slider's ID (for a value matching what the user has dragged the slider to). &lt;br /&gt;
* '''ControlHeader'''  (Optional) If present, must have string value &amp;quot;1&amp;quot;. When set, the slider's label and value are displayed. Otherwise only the slider is shown. (TODO: This is from reverse-engineering the JavaScript code. Test this.)&lt;br /&gt;
&lt;br /&gt;
===ControlType slider_vertical===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_Heater1.json, D_HVAC_ZoneThermostat1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType checkbox===&lt;br /&gt;
&lt;br /&gt;
:As of UI5 firmware 1.5.622, checkboxes are only half-baked and not usable in &amp;quot;flash&amp;quot; style tabs. If you need check-boxes, radio buttons, pop-up menus, etc. in your tab, you need to create a Javascript tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType image===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType image_player===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType js_button===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
=UI7 Updates=&lt;br /&gt;
&lt;br /&gt;
===ControlType line_break===&lt;br /&gt;
This is used to force the next control to appear below the previous one rather than alongside it. See [[Luup plugins: Static JSON file]].&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘line_break’&lt;br /&gt;
&lt;br /&gt;
===ControlType multi_state_button=== &lt;br /&gt;
This is a new type of control in UI7 and works as a “toggle” button. It is used for On/Off, Armed/Disarmed etc buttons.&lt;br /&gt;
&lt;br /&gt;
As an example, here is the definition from the .json file of the Door/Window sensor needed to create this kind of control:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
&amp;quot;ControlType&amp;quot;: &amp;quot;multi_state_button&amp;quot;,&lt;br /&gt;
&amp;quot;top&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
&amp;quot;left&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;states&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
                  &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                      &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_arm&amp;quot;,&lt;br /&gt;
                      &amp;quot;text&amp;quot;: &amp;quot;Arm&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                         &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                           &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                   {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                           ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;arm&amp;quot;&lt;br /&gt;
              },&lt;br /&gt;
              {&lt;br /&gt;
                     &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                           &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_bypass&amp;quot;,&lt;br /&gt;
                           &amp;quot;text&amp;quot;: &amp;quot;Bypass&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                            ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;bypass&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
        ]&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘multi_state_button’&lt;br /&gt;
* '''top''' - an integer specifying the row from the control group where the control will be placed&lt;br /&gt;
* '''left''' - an integer which specifies which column will the control be placed in&lt;br /&gt;
* '''states''' - an array of objects, each object representing a different state for the button; each object has the following properties:&lt;br /&gt;
** '''Lable''' - a JSON object with two keys:&lt;br /&gt;
*** '''lang_tag''' - a string used for localization&lt;br /&gt;
*** '''text''' - the string displayed if localization fails&lt;br /&gt;
** '''Display''' - a JSON object with the following keys:&lt;br /&gt;
*** '''Service''' - a string containing the service id&lt;br /&gt;
*** '''Variable''' - a string containing the name of the variable&lt;br /&gt;
*** '''Value''' - a number which represents the value of the state in which the control must be in order for this state to be displayed/marked as selected&lt;br /&gt;
** '''Command''' - a JSON object with the following properties:&lt;br /&gt;
*** '''Service''' - a string with the service id of the action which this state will perform&lt;br /&gt;
*** '''Action''' - a string representing the name of the action to be executed&lt;br /&gt;
*** '''Parameters''' - an array of JSON objects holding the parameters to pass to the action&lt;br /&gt;
* '''ControlCode''' - a string containing the control code of this control (it is recommended to be unique for each device type)&lt;br /&gt;
&lt;br /&gt;
As you can see, the multi_state_button is a merge resulted from two controls of type button.&lt;br /&gt;
&lt;br /&gt;
[[File:MotionSensor.jpg|image|left]]&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_tabs</id>
		<title>Luup plugin tabs</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_tabs"/>
				<updated>2015-03-28T23:08:08Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: /* ControlType slider */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Root Keys=&lt;br /&gt;
Refer to [[Luup_plugins:_Static_JSON_file|Root Keys]].&lt;br /&gt;
&lt;br /&gt;
=Tabs Key=&lt;br /&gt;
The '''Tabs''' key in the [[Luup plugins: Static JSON file|static JSON file]] performs two duties in the UI4 HTML interface: &lt;br /&gt;
&lt;br /&gt;
*It controls how the device appears on the '''Dashboard''' and '''Devices''' web pages.&lt;br /&gt;
*It controls how each tab appears in the the device's detailed dialog.&lt;br /&gt;
&lt;br /&gt;
The value of the '''Tabs''' key is a JSON array. Each array element is a JSON object (associative array). One for each Tab. &lt;br /&gt;
&lt;br /&gt;
==Tab fields==&lt;br /&gt;
These fields must be present for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object containing two keys:&lt;br /&gt;
*'''lang_tag''' (a string) is not displayed in the HTML interface but may be used by localized UIs.&lt;br /&gt;
*'''text''' (a string) is displayed in the tab's handle at the top of the dialog. &lt;br /&gt;
;'''Position''' &lt;br /&gt;
:A string which contains a number matching the relative position of this tab in relation to the other tabs. The leftmost tab has value &amp;quot;0&amp;quot;, the next tab &amp;quot;1&amp;quot;, and so on. &lt;br /&gt;
;'''TabType''' &lt;br /&gt;
:A string which describes how the content of the tab is to be generated. The value may be one of '''javascript''' or '''flash'''. These are described in the following sections.&lt;br /&gt;
&lt;br /&gt;
These fields are optional for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''ControlGroup'''&lt;br /&gt;
:Required only if the first tab is also used to display elements on the dashboard. See [[Luup_plugin_tabs#Dashboard_appearance|''Dashboard appearance'']] below.&lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
:Added in UI5.&lt;br /&gt;
&lt;br /&gt;
;'''Control'''&lt;br /&gt;
:An array of controls for each tab. Each control consists of:&lt;br /&gt;
*'''ControlType''' - the Control Types are described further below. &lt;br /&gt;
*'''ControlGroup''' - a reference to the control's ControlGroup &lt;br /&gt;
*'''text_align''' - align left or right &lt;br /&gt;
*'''top''' - dashboard position in button units&lt;br /&gt;
*'''left''' - dashboard position in button units&lt;br /&gt;
&lt;br /&gt;
eg a slider sitting above a two by two array of buttons:&lt;br /&gt;
&lt;br /&gt;
slider: top = 0, left = 0&amp;lt;br\&amp;gt;&lt;br /&gt;
top left button: top = 1.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
top right button: top = 1.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
bot left button: top = 2.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
bot right button: top = 2.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TabType javascript===&lt;br /&gt;
The '''javascript''' tab type executes client-side JavaScript, producing an HTML fragment which forms the body of the tab. These additional keys are required in the tab JSON object: &lt;br /&gt;
&lt;br /&gt;
;'''ScriptName''' &lt;br /&gt;
:The name of the JavaScript file. Conventionally, plugin JavaScript files are named '''J_PluginName.js'''. &lt;br /&gt;
;'''Function''' &lt;br /&gt;
:The function inside the JavaScript file which will be called to produce the HTML.&lt;br /&gt;
&lt;br /&gt;
The JavaScript code has access to most Luup variables through the [[JavaScript API]]. &lt;br /&gt;
&lt;br /&gt;
===TabType flash===&lt;br /&gt;
The '''flash''' tab type creates a tab body with a simple page-description language. Each component of the page is placed into the body at the position specified. You can't place a '''Control''' on the dashboard unless you place it in a '''ControlGroup''', which in turn must be placed in a '''SceneGroup''': &lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
&lt;br /&gt;
''SceneGroup'' (added in UI5) is similar to a ControlGroup, but with some key differences: it is customizable (by setting its position and dimensions) and it defines the behavior of the buttons assigned to the same state variable. If two buttons in the same SceneGroup are assigned to the same variable, only one of them can be activated in the Scene Editor (radio buttons behavior). If two buttons assigned to the same variable are in different SceneGroups, both can be activated at the same time in the Scene Editor (checkboxes behavior). &lt;br /&gt;
&lt;br /&gt;
It has the following tags: &lt;br /&gt;
&lt;br /&gt;
*'''id''': A numeric value (positive integer numbers). &lt;br /&gt;
*'''top''': The offset from the device header. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''left''': The offset from the device icon. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''x''', '''y''': the dimensions of the SceneGroup. e.g If I have two horizontally aligned buttons in the SceneGroup, ''x'' will be ''2'' and ''y'' will be ''1''.&lt;br /&gt;
&lt;br /&gt;
A ''SceneGroup'' can contain one or more ''ControlGroups''. &lt;br /&gt;
&lt;br /&gt;
===Dashboard appearance===&lt;br /&gt;
The first array element in the '''Tabs''' field is special. If it is of type '''flash''' then a subset of the controls on the tab can be chosen to display on the UI4 dashboard.&lt;br /&gt;
&lt;br /&gt;
The dashboard box for a device has room for two rows of information and controls. These are called ''Control Groups''.&lt;br /&gt;
&lt;br /&gt;
The tab object must contain the key '''ControlGroup''', which specifies how the dashboard's two available rows of information are to be handled. '''ControlGroup''' is a JSON array containing JSON objects (associative arrays). Each object has the following keys:&lt;br /&gt;
&lt;br /&gt;
;'''id''' &lt;br /&gt;
:A string containing a number, &amp;quot;1&amp;quot; for the first object, &amp;quot;2&amp;quot; for the second, and so on. &lt;br /&gt;
;'''isSingle''' &lt;br /&gt;
:UI4: If this key is present and contains the value &amp;quot;1&amp;quot;, then this control group is combined with other control groups that have '''isSingle''' similarly set onto the same row. For an example of this, see the On/Off buttons of the Binary Switch and Dimmable Switch static JSON files. &lt;br /&gt;
:UI5: Since controls cannot be grouped in drop-down lists anymore, this tag has a different role in UI5. When isSingle is &amp;quot;1&amp;quot;, the buttons in the ControlGroup will have a background (e.g. the BinaryLight buttons). If isSingle is missing, the buttons won't have a background. That is the button shape is not shown; only the button text will show. &lt;br /&gt;
;'''type''' &lt;br /&gt;
:If this key is present and contains the value &amp;quot;info&amp;quot;, then the controls displayed on this row are not clickable. If this key is absent, buttons may be used in the control group.&lt;br /&gt;
&lt;br /&gt;
===Making a control appear on the dashboard===&lt;br /&gt;
All control types (label, variable, button, input, slider) can appear in the dashboard. However, by default, controls in the first tab's detail view do not appear in the dashboard. To make a control's text appear in the dashboard, add a '''ControlGroup''' to the control's JSON object. The value is a string containing a number, matching the '''id''' of one of the Tab's overall '''ControlGroup''' key. The dashboard will concatenate all of a control group's controls in the order they are presented in the '''Control''' array, and display them in a single row of text. &lt;br /&gt;
&lt;br /&gt;
===Keeping controls together===&lt;br /&gt;
Some controls may not make sense if presented individually (for instance, an input for an alarm PIN and a &amp;quot;disarm&amp;quot; button). To keep these controls together, add a '''ControlPair''' key to all of the controls that have to appear together. Use the same value (a number in a string) for all of them.&lt;br /&gt;
&lt;br /&gt;
==Control Types==&lt;br /&gt;
&lt;br /&gt;
The '''Control''' array contains JSON objects (associative arrays), one per item in the tab body. An item is either a static text label, a variable, an input field, or a button or slider. This is handled by setting the '''ControlType''' key on the object. &lt;br /&gt;
&lt;br /&gt;
===ControlType label===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''label''', a fixed label is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys:&lt;br /&gt;
*'''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. The key is optional.&lt;br /&gt;
*'''text''' is displayed as the content of the label. In UI5 on the &amp;quot;Dashboard&amp;quot;, the text is rendered by passing it in the &amp;quot;Title&amp;quot; attribute of a Div statement, so it can only be plain text, (as opposed to say HTML). However in the tabs, it is placed in a Div statement so HTML, such as a URL link, can be rendered. &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys, which are strings containing numbers (in pixels?). This controls where the label is placed on the tab. The label is left aligned.&lt;br /&gt;
*'''Top''': The offset from the top of tab writable area.&lt;br /&gt;
*'''Left''': The offset from the left side of the tab writable area.&lt;br /&gt;
*'''Width''': Window width of the label.&lt;br /&gt;
*'''Height''': Window height of the label.&lt;br /&gt;
&lt;br /&gt;
===ControlType variable===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''variable''', the contents of a Luup variable are displayed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with six keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the variable's value is placed in the tab), '''Service''' (a string containing the Service Id of the variable to be displayed), and '''Variable''' (a string containing the name of the variable to be displayed).&lt;br /&gt;
&lt;br /&gt;
===ControlType input===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''input''', a text box for the user to type a string is placed in the tab. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''ID''' &lt;br /&gt;
:A string which other controls (buttons) will use to refer to the string that the user has typed into this field. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers). This controls where the text box is placed in the tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType button===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''button''', a clickable button is placed at the specified coordinates. The button will appear selected (depressed) or unselected based on the value of a variable, permitting sets of buttons to act as radio buttons. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys: '''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. '''text''' is displayed as the text inside the button. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with up to seven keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), and (optionally) '''Value''' (a string, &amp;quot;1&amp;quot; if omitted). The specified variable's value controls whether the button appears selected (if the variable matches '''Value''') or not selected (if the variable doesn't match). &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching an input control on the page (for a user-entered parameter value).&lt;br /&gt;
&lt;br /&gt;
===ControlType slider===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''slider''', a draggable slider is placed at the specified coordinates. &lt;br /&gt;
&lt;br /&gt;
*Note: in UI5, there can only be one horizontal slider (0% - 100%) per device. There can be at most two verical sliders. The first is red and used for heating. The second is blue and used for cooling. &lt;br /&gt;
&lt;br /&gt;
The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with nine keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), '''MinValue''' and '''MaxValue''' (both strings containing numbers, controlling what range of values the slider takes) and '''ID''' (a string, used in the '''Command''' to refer to the position the user has dragged the slider to). The specified variable's value is used to display the slider's current position. &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching the slider's ID (for a value matching what the user has dragged the slider to). &lt;br /&gt;
;'''ControlHeader''' &lt;br /&gt;
:Optional. If present, must have string value &amp;quot;1&amp;quot;. When set, the slider's label and value are displayed. Otherwise only the slider is shown. (TODO: This is from reverse-engineering the JavaScript code. Test this.)&lt;br /&gt;
&lt;br /&gt;
===ControlType slider_vertical===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_Heater1.json, D_HVAC_ZoneThermostat1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType checkbox===&lt;br /&gt;
&lt;br /&gt;
:As of UI5 firmware 1.5.622, checkboxes are only half-baked and not usable in &amp;quot;flash&amp;quot; style tabs. If you need check-boxes, radio buttons, pop-up menus, etc. in your tab, you need to create a Javascript tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType image===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType image_player===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType js_button===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
=UI7 Updates=&lt;br /&gt;
&lt;br /&gt;
===ControlType line_break===&lt;br /&gt;
This is used to force the next control to appear below the previous one rather than alongside it. See [[Luup plugins: Static JSON file]].&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘line_break’&lt;br /&gt;
&lt;br /&gt;
===ControlType multi_state_button=== &lt;br /&gt;
This is a new type of control in UI7 and works as a “toggle” button. It is used for On/Off, Armed/Disarmed etc buttons.&lt;br /&gt;
&lt;br /&gt;
As an example, here is the definition from the .json file of the Door/Window sensor needed to create this kind of control:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
&amp;quot;ControlType&amp;quot;: &amp;quot;multi_state_button&amp;quot;,&lt;br /&gt;
&amp;quot;top&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
&amp;quot;left&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;states&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
                  &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                      &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_arm&amp;quot;,&lt;br /&gt;
                      &amp;quot;text&amp;quot;: &amp;quot;Arm&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                         &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                           &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                   {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                           ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;arm&amp;quot;&lt;br /&gt;
              },&lt;br /&gt;
              {&lt;br /&gt;
                     &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                           &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_bypass&amp;quot;,&lt;br /&gt;
                           &amp;quot;text&amp;quot;: &amp;quot;Bypass&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                            ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;bypass&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
        ]&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘multi_state_button’&lt;br /&gt;
* '''top''' - an integer specifying the row from the control group where the control will be placed&lt;br /&gt;
* '''left''' - an integer which specifies which column will the control be placed in&lt;br /&gt;
* '''states''' - an array of objects, each object representing a different state for the button; each object has the following properties:&lt;br /&gt;
** '''Lable''' - a JSON object with two keys:&lt;br /&gt;
*** '''lang_tag''' - a string used for localization&lt;br /&gt;
*** '''text''' - the string displayed if localization fails&lt;br /&gt;
** '''Display''' - a JSON object with the following keys:&lt;br /&gt;
*** '''Service''' - a string containing the service id&lt;br /&gt;
*** '''Variable''' - a string containing the name of the variable&lt;br /&gt;
*** '''Value''' - a number which represents the value of the state in which the control must be in order for this state to be displayed/marked as selected&lt;br /&gt;
** '''Command''' - a JSON object with the following properties:&lt;br /&gt;
*** '''Service''' - a string with the service id of the action which this state will perform&lt;br /&gt;
*** '''Action''' - a string representing the name of the action to be executed&lt;br /&gt;
*** '''Parameters''' - an array of JSON objects holding the parameters to pass to the action&lt;br /&gt;
* '''ControlCode''' - a string containing the control code of this control (it is recommended to be unique for each device type)&lt;br /&gt;
&lt;br /&gt;
As you can see, the multi_state_button is a merge resulted from two controls of type button.&lt;br /&gt;
&lt;br /&gt;
[[File:MotionSensor.jpg|image|left]]&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_tabs</id>
		<title>Luup plugin tabs</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_tabs"/>
				<updated>2015-03-28T23:02:28Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: /* ControlType line_break */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Root Keys=&lt;br /&gt;
Refer to [[Luup_plugins:_Static_JSON_file|Root Keys]].&lt;br /&gt;
&lt;br /&gt;
=Tabs Key=&lt;br /&gt;
The '''Tabs''' key in the [[Luup plugins: Static JSON file|static JSON file]] performs two duties in the UI4 HTML interface: &lt;br /&gt;
&lt;br /&gt;
*It controls how the device appears on the '''Dashboard''' and '''Devices''' web pages.&lt;br /&gt;
*It controls how each tab appears in the the device's detailed dialog.&lt;br /&gt;
&lt;br /&gt;
The value of the '''Tabs''' key is a JSON array. Each array element is a JSON object (associative array). One for each Tab. &lt;br /&gt;
&lt;br /&gt;
==Tab fields==&lt;br /&gt;
These fields must be present for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object containing two keys:&lt;br /&gt;
*'''lang_tag''' (a string) is not displayed in the HTML interface but may be used by localized UIs.&lt;br /&gt;
*'''text''' (a string) is displayed in the tab's handle at the top of the dialog. &lt;br /&gt;
;'''Position''' &lt;br /&gt;
:A string which contains a number matching the relative position of this tab in relation to the other tabs. The leftmost tab has value &amp;quot;0&amp;quot;, the next tab &amp;quot;1&amp;quot;, and so on. &lt;br /&gt;
;'''TabType''' &lt;br /&gt;
:A string which describes how the content of the tab is to be generated. The value may be one of '''javascript''' or '''flash'''. These are described in the following sections.&lt;br /&gt;
&lt;br /&gt;
These fields are optional for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''ControlGroup'''&lt;br /&gt;
:Required only if the first tab is also used to display elements on the dashboard. See [[Luup_plugin_tabs#Dashboard_appearance|''Dashboard appearance'']] below.&lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
:Added in UI5.&lt;br /&gt;
&lt;br /&gt;
;'''Control'''&lt;br /&gt;
:An array of controls for each tab. Each control consists of:&lt;br /&gt;
*'''ControlType''' - the Control Types are described further below. &lt;br /&gt;
*'''ControlGroup''' - a reference to the control's ControlGroup &lt;br /&gt;
*'''text_align''' - align left or right &lt;br /&gt;
*'''top''' - dashboard position in button units&lt;br /&gt;
*'''left''' - dashboard position in button units&lt;br /&gt;
&lt;br /&gt;
eg a slider sitting above a two by two array of buttons:&lt;br /&gt;
&lt;br /&gt;
slider: top = 0, left = 0&amp;lt;br\&amp;gt;&lt;br /&gt;
top left button: top = 1.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
top right button: top = 1.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
bot left button: top = 2.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
bot right button: top = 2.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TabType javascript===&lt;br /&gt;
The '''javascript''' tab type executes client-side JavaScript, producing an HTML fragment which forms the body of the tab. These additional keys are required in the tab JSON object: &lt;br /&gt;
&lt;br /&gt;
;'''ScriptName''' &lt;br /&gt;
:The name of the JavaScript file. Conventionally, plugin JavaScript files are named '''J_PluginName.js'''. &lt;br /&gt;
;'''Function''' &lt;br /&gt;
:The function inside the JavaScript file which will be called to produce the HTML.&lt;br /&gt;
&lt;br /&gt;
The JavaScript code has access to most Luup variables through the [[JavaScript API]]. &lt;br /&gt;
&lt;br /&gt;
===TabType flash===&lt;br /&gt;
The '''flash''' tab type creates a tab body with a simple page-description language. Each component of the page is placed into the body at the position specified. You can't place a '''Control''' on the dashboard unless you place it in a '''ControlGroup''', which in turn must be placed in a '''SceneGroup''': &lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
&lt;br /&gt;
''SceneGroup'' (added in UI5) is similar to a ControlGroup, but with some key differences: it is customizable (by setting its position and dimensions) and it defines the behavior of the buttons assigned to the same state variable. If two buttons in the same SceneGroup are assigned to the same variable, only one of them can be activated in the Scene Editor (radio buttons behavior). If two buttons assigned to the same variable are in different SceneGroups, both can be activated at the same time in the Scene Editor (checkboxes behavior). &lt;br /&gt;
&lt;br /&gt;
It has the following tags: &lt;br /&gt;
&lt;br /&gt;
*'''id''': A numeric value (positive integer numbers). &lt;br /&gt;
*'''top''': The offset from the device header. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''left''': The offset from the device icon. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''x''', '''y''': the dimensions of the SceneGroup. e.g If I have two horizontally aligned buttons in the SceneGroup, ''x'' will be ''2'' and ''y'' will be ''1''.&lt;br /&gt;
&lt;br /&gt;
A ''SceneGroup'' can contain one or more ''ControlGroups''. &lt;br /&gt;
&lt;br /&gt;
===Dashboard appearance===&lt;br /&gt;
The first array element in the '''Tabs''' field is special. If it is of type '''flash''' then a subset of the controls on the tab can be chosen to display on the UI4 dashboard.&lt;br /&gt;
&lt;br /&gt;
The dashboard box for a device has room for two rows of information and controls. These are called ''Control Groups''.&lt;br /&gt;
&lt;br /&gt;
The tab object must contain the key '''ControlGroup''', which specifies how the dashboard's two available rows of information are to be handled. '''ControlGroup''' is a JSON array containing JSON objects (associative arrays). Each object has the following keys:&lt;br /&gt;
&lt;br /&gt;
;'''id''' &lt;br /&gt;
:A string containing a number, &amp;quot;1&amp;quot; for the first object, &amp;quot;2&amp;quot; for the second, and so on. &lt;br /&gt;
;'''isSingle''' &lt;br /&gt;
:UI4: If this key is present and contains the value &amp;quot;1&amp;quot;, then this control group is combined with other control groups that have '''isSingle''' similarly set onto the same row. For an example of this, see the On/Off buttons of the Binary Switch and Dimmable Switch static JSON files. &lt;br /&gt;
:UI5: Since controls cannot be grouped in drop-down lists anymore, this tag has a different role in UI5. When isSingle is &amp;quot;1&amp;quot;, the buttons in the ControlGroup will have a background (e.g. the BinaryLight buttons). If isSingle is missing, the buttons won't have a background. That is the button shape is not shown; only the button text will show. &lt;br /&gt;
;'''type''' &lt;br /&gt;
:If this key is present and contains the value &amp;quot;info&amp;quot;, then the controls displayed on this row are not clickable. If this key is absent, buttons may be used in the control group.&lt;br /&gt;
&lt;br /&gt;
===Making a control appear on the dashboard===&lt;br /&gt;
All control types (label, variable, button, input, slider) can appear in the dashboard. However, by default, controls in the first tab's detail view do not appear in the dashboard. To make a control's text appear in the dashboard, add a '''ControlGroup''' to the control's JSON object. The value is a string containing a number, matching the '''id''' of one of the Tab's overall '''ControlGroup''' key. The dashboard will concatenate all of a control group's controls in the order they are presented in the '''Control''' array, and display them in a single row of text. &lt;br /&gt;
&lt;br /&gt;
===Keeping controls together===&lt;br /&gt;
Some controls may not make sense if presented individually (for instance, an input for an alarm PIN and a &amp;quot;disarm&amp;quot; button). To keep these controls together, add a '''ControlPair''' key to all of the controls that have to appear together. Use the same value (a number in a string) for all of them.&lt;br /&gt;
&lt;br /&gt;
==Control Types==&lt;br /&gt;
&lt;br /&gt;
The '''Control''' array contains JSON objects (associative arrays), one per item in the tab body. An item is either a static text label, a variable, an input field, or a button or slider. This is handled by setting the '''ControlType''' key on the object. &lt;br /&gt;
&lt;br /&gt;
===ControlType label===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''label''', a fixed label is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys:&lt;br /&gt;
*'''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. The key is optional.&lt;br /&gt;
*'''text''' is displayed as the content of the label. In UI5 on the &amp;quot;Dashboard&amp;quot;, the text is rendered by passing it in the &amp;quot;Title&amp;quot; attribute of a Div statement, so it can only be plain text, (as opposed to say HTML). However in the tabs, it is placed in a Div statement so HTML, such as a URL link, can be rendered. &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys, which are strings containing numbers (in pixels?). This controls where the label is placed on the tab. The label is left aligned.&lt;br /&gt;
*'''Top''': The offset from the top of tab writable area.&lt;br /&gt;
*'''Left''': The offset from the left side of the tab writable area.&lt;br /&gt;
*'''Width''': Window width of the label.&lt;br /&gt;
*'''Height''': Window height of the label.&lt;br /&gt;
&lt;br /&gt;
===ControlType variable===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''variable''', the contents of a Luup variable are displayed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with six keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the variable's value is placed in the tab), '''Service''' (a string containing the Service Id of the variable to be displayed), and '''Variable''' (a string containing the name of the variable to be displayed).&lt;br /&gt;
&lt;br /&gt;
===ControlType input===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''input''', a text box for the user to type a string is placed in the tab. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''ID''' &lt;br /&gt;
:A string which other controls (buttons) will use to refer to the string that the user has typed into this field. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers). This controls where the text box is placed in the tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType button===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''button''', a clickable button is placed at the specified coordinates. The button will appear selected (depressed) or unselected based on the value of a variable, permitting sets of buttons to act as radio buttons. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys: '''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. '''text''' is displayed as the text inside the button. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with up to seven keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), and (optionally) '''Value''' (a string, &amp;quot;1&amp;quot; if omitted). The specified variable's value controls whether the button appears selected (if the variable matches '''Value''') or not selected (if the variable doesn't match). &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching an input control on the page (for a user-entered parameter value).&lt;br /&gt;
&lt;br /&gt;
===ControlType slider===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''slider''', a draggable slider is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with nine keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), '''MinValue''' and '''MaxValue''' (both strings containing numbers, controlling what range of values the slider takes) and '''ID''' (a string, used in the '''Command''' to refer to the position the user has dragged the slider to). The specified variable's value is used to display the slider's current position. &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching the slider's ID (for a value matching what the user has dragged the slider to). &lt;br /&gt;
;'''ControlHeader''' &lt;br /&gt;
:Optional. If present, must have string value &amp;quot;1&amp;quot;. When set, the slider's label and value are displayed. Otherwise only the slider is shown. (TODO: This is from reverse-engineering the JavaScript code. Test this.)&lt;br /&gt;
&lt;br /&gt;
===ControlType slider_vertical===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_Heater1.json, D_HVAC_ZoneThermostat1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType checkbox===&lt;br /&gt;
&lt;br /&gt;
:As of UI5 firmware 1.5.622, checkboxes are only half-baked and not usable in &amp;quot;flash&amp;quot; style tabs. If you need check-boxes, radio buttons, pop-up menus, etc. in your tab, you need to create a Javascript tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType image===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType image_player===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType js_button===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
=UI7 Updates=&lt;br /&gt;
&lt;br /&gt;
===ControlType line_break===&lt;br /&gt;
This is used to force the next control to appear below the previous one rather than alongside it. See [[Luup plugins: Static JSON file]].&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘line_break’&lt;br /&gt;
&lt;br /&gt;
===ControlType multi_state_button=== &lt;br /&gt;
This is a new type of control in UI7 and works as a “toggle” button. It is used for On/Off, Armed/Disarmed etc buttons.&lt;br /&gt;
&lt;br /&gt;
As an example, here is the definition from the .json file of the Door/Window sensor needed to create this kind of control:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
&amp;quot;ControlType&amp;quot;: &amp;quot;multi_state_button&amp;quot;,&lt;br /&gt;
&amp;quot;top&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
&amp;quot;left&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;states&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
                  &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                      &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_arm&amp;quot;,&lt;br /&gt;
                      &amp;quot;text&amp;quot;: &amp;quot;Arm&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                         &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                           &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                   {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                           ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;arm&amp;quot;&lt;br /&gt;
              },&lt;br /&gt;
              {&lt;br /&gt;
                     &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                           &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_bypass&amp;quot;,&lt;br /&gt;
                           &amp;quot;text&amp;quot;: &amp;quot;Bypass&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                            ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;bypass&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
        ]&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘multi_state_button’&lt;br /&gt;
* '''top''' - an integer specifying the row from the control group where the control will be placed&lt;br /&gt;
* '''left''' - an integer which specifies which column will the control be placed in&lt;br /&gt;
* '''states''' - an array of objects, each object representing a different state for the button; each object has the following properties:&lt;br /&gt;
** '''Lable''' - a JSON object with two keys:&lt;br /&gt;
*** '''lang_tag''' - a string used for localization&lt;br /&gt;
*** '''text''' - the string displayed if localization fails&lt;br /&gt;
** '''Display''' - a JSON object with the following keys:&lt;br /&gt;
*** '''Service''' - a string containing the service id&lt;br /&gt;
*** '''Variable''' - a string containing the name of the variable&lt;br /&gt;
*** '''Value''' - a number which represents the value of the state in which the control must be in order for this state to be displayed/marked as selected&lt;br /&gt;
** '''Command''' - a JSON object with the following properties:&lt;br /&gt;
*** '''Service''' - a string with the service id of the action which this state will perform&lt;br /&gt;
*** '''Action''' - a string representing the name of the action to be executed&lt;br /&gt;
*** '''Parameters''' - an array of JSON objects holding the parameters to pass to the action&lt;br /&gt;
* '''ControlCode''' - a string containing the control code of this control (it is recommended to be unique for each device type)&lt;br /&gt;
&lt;br /&gt;
As you can see, the multi_state_button is a merge resulted from two controls of type button.&lt;br /&gt;
&lt;br /&gt;
[[File:MotionSensor.jpg|image|left]]&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_tabs</id>
		<title>Luup plugin tabs</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_tabs"/>
				<updated>2015-03-28T22:58:29Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: /* ControlType line_break */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Root Keys=&lt;br /&gt;
Refer to [[Luup_plugins:_Static_JSON_file|Root Keys]].&lt;br /&gt;
&lt;br /&gt;
=Tabs Key=&lt;br /&gt;
The '''Tabs''' key in the [[Luup plugins: Static JSON file|static JSON file]] performs two duties in the UI4 HTML interface: &lt;br /&gt;
&lt;br /&gt;
*It controls how the device appears on the '''Dashboard''' and '''Devices''' web pages.&lt;br /&gt;
*It controls how each tab appears in the the device's detailed dialog.&lt;br /&gt;
&lt;br /&gt;
The value of the '''Tabs''' key is a JSON array. Each array element is a JSON object (associative array). One for each Tab. &lt;br /&gt;
&lt;br /&gt;
==Tab fields==&lt;br /&gt;
These fields must be present for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object containing two keys:&lt;br /&gt;
*'''lang_tag''' (a string) is not displayed in the HTML interface but may be used by localized UIs.&lt;br /&gt;
*'''text''' (a string) is displayed in the tab's handle at the top of the dialog. &lt;br /&gt;
;'''Position''' &lt;br /&gt;
:A string which contains a number matching the relative position of this tab in relation to the other tabs. The leftmost tab has value &amp;quot;0&amp;quot;, the next tab &amp;quot;1&amp;quot;, and so on. &lt;br /&gt;
;'''TabType''' &lt;br /&gt;
:A string which describes how the content of the tab is to be generated. The value may be one of '''javascript''' or '''flash'''. These are described in the following sections.&lt;br /&gt;
&lt;br /&gt;
These fields are optional for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''ControlGroup'''&lt;br /&gt;
:Required only if the first tab is also used to display elements on the dashboard. See [[Luup_plugin_tabs#Dashboard_appearance|''Dashboard appearance'']] below.&lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
:Added in UI5.&lt;br /&gt;
&lt;br /&gt;
;'''Control'''&lt;br /&gt;
:An array of controls for each tab. Each control consists of:&lt;br /&gt;
*'''ControlType''' - the Control Types are described further below. &lt;br /&gt;
*'''ControlGroup''' - a reference to the control's ControlGroup &lt;br /&gt;
*'''text_align''' - align left or right &lt;br /&gt;
*'''top''' - dashboard position in button units&lt;br /&gt;
*'''left''' - dashboard position in button units&lt;br /&gt;
&lt;br /&gt;
eg a slider sitting above a two by two array of buttons:&lt;br /&gt;
&lt;br /&gt;
slider: top = 0, left = 0&amp;lt;br\&amp;gt;&lt;br /&gt;
top left button: top = 1.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
top right button: top = 1.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
bot left button: top = 2.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
bot right button: top = 2.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TabType javascript===&lt;br /&gt;
The '''javascript''' tab type executes client-side JavaScript, producing an HTML fragment which forms the body of the tab. These additional keys are required in the tab JSON object: &lt;br /&gt;
&lt;br /&gt;
;'''ScriptName''' &lt;br /&gt;
:The name of the JavaScript file. Conventionally, plugin JavaScript files are named '''J_PluginName.js'''. &lt;br /&gt;
;'''Function''' &lt;br /&gt;
:The function inside the JavaScript file which will be called to produce the HTML.&lt;br /&gt;
&lt;br /&gt;
The JavaScript code has access to most Luup variables through the [[JavaScript API]]. &lt;br /&gt;
&lt;br /&gt;
===TabType flash===&lt;br /&gt;
The '''flash''' tab type creates a tab body with a simple page-description language. Each component of the page is placed into the body at the position specified. You can't place a '''Control''' on the dashboard unless you place it in a '''ControlGroup''', which in turn must be placed in a '''SceneGroup''': &lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
&lt;br /&gt;
''SceneGroup'' (added in UI5) is similar to a ControlGroup, but with some key differences: it is customizable (by setting its position and dimensions) and it defines the behavior of the buttons assigned to the same state variable. If two buttons in the same SceneGroup are assigned to the same variable, only one of them can be activated in the Scene Editor (radio buttons behavior). If two buttons assigned to the same variable are in different SceneGroups, both can be activated at the same time in the Scene Editor (checkboxes behavior). &lt;br /&gt;
&lt;br /&gt;
It has the following tags: &lt;br /&gt;
&lt;br /&gt;
*'''id''': A numeric value (positive integer numbers). &lt;br /&gt;
*'''top''': The offset from the device header. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''left''': The offset from the device icon. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''x''', '''y''': the dimensions of the SceneGroup. e.g If I have two horizontally aligned buttons in the SceneGroup, ''x'' will be ''2'' and ''y'' will be ''1''.&lt;br /&gt;
&lt;br /&gt;
A ''SceneGroup'' can contain one or more ''ControlGroups''. &lt;br /&gt;
&lt;br /&gt;
===Dashboard appearance===&lt;br /&gt;
The first array element in the '''Tabs''' field is special. If it is of type '''flash''' then a subset of the controls on the tab can be chosen to display on the UI4 dashboard.&lt;br /&gt;
&lt;br /&gt;
The dashboard box for a device has room for two rows of information and controls. These are called ''Control Groups''.&lt;br /&gt;
&lt;br /&gt;
The tab object must contain the key '''ControlGroup''', which specifies how the dashboard's two available rows of information are to be handled. '''ControlGroup''' is a JSON array containing JSON objects (associative arrays). Each object has the following keys:&lt;br /&gt;
&lt;br /&gt;
;'''id''' &lt;br /&gt;
:A string containing a number, &amp;quot;1&amp;quot; for the first object, &amp;quot;2&amp;quot; for the second, and so on. &lt;br /&gt;
;'''isSingle''' &lt;br /&gt;
:UI4: If this key is present and contains the value &amp;quot;1&amp;quot;, then this control group is combined with other control groups that have '''isSingle''' similarly set onto the same row. For an example of this, see the On/Off buttons of the Binary Switch and Dimmable Switch static JSON files. &lt;br /&gt;
:UI5: Since controls cannot be grouped in drop-down lists anymore, this tag has a different role in UI5. When isSingle is &amp;quot;1&amp;quot;, the buttons in the ControlGroup will have a background (e.g. the BinaryLight buttons). If isSingle is missing, the buttons won't have a background. That is the button shape is not shown; only the button text will show. &lt;br /&gt;
;'''type''' &lt;br /&gt;
:If this key is present and contains the value &amp;quot;info&amp;quot;, then the controls displayed on this row are not clickable. If this key is absent, buttons may be used in the control group.&lt;br /&gt;
&lt;br /&gt;
===Making a control appear on the dashboard===&lt;br /&gt;
All control types (label, variable, button, input, slider) can appear in the dashboard. However, by default, controls in the first tab's detail view do not appear in the dashboard. To make a control's text appear in the dashboard, add a '''ControlGroup''' to the control's JSON object. The value is a string containing a number, matching the '''id''' of one of the Tab's overall '''ControlGroup''' key. The dashboard will concatenate all of a control group's controls in the order they are presented in the '''Control''' array, and display them in a single row of text. &lt;br /&gt;
&lt;br /&gt;
===Keeping controls together===&lt;br /&gt;
Some controls may not make sense if presented individually (for instance, an input for an alarm PIN and a &amp;quot;disarm&amp;quot; button). To keep these controls together, add a '''ControlPair''' key to all of the controls that have to appear together. Use the same value (a number in a string) for all of them.&lt;br /&gt;
&lt;br /&gt;
==Control Types==&lt;br /&gt;
&lt;br /&gt;
The '''Control''' array contains JSON objects (associative arrays), one per item in the tab body. An item is either a static text label, a variable, an input field, or a button or slider. This is handled by setting the '''ControlType''' key on the object. &lt;br /&gt;
&lt;br /&gt;
===ControlType label===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''label''', a fixed label is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys:&lt;br /&gt;
*'''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. The key is optional.&lt;br /&gt;
*'''text''' is displayed as the content of the label. In UI5 on the &amp;quot;Dashboard&amp;quot;, the text is rendered by passing it in the &amp;quot;Title&amp;quot; attribute of a Div statement, so it can only be plain text, (as opposed to say HTML). However in the tabs, it is placed in a Div statement so HTML, such as a URL link, can be rendered. &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys, which are strings containing numbers (in pixels?). This controls where the label is placed on the tab. The label is left aligned.&lt;br /&gt;
*'''Top''': The offset from the top of tab writable area.&lt;br /&gt;
*'''Left''': The offset from the left side of the tab writable area.&lt;br /&gt;
*'''Width''': Window width of the label.&lt;br /&gt;
*'''Height''': Window height of the label.&lt;br /&gt;
&lt;br /&gt;
===ControlType variable===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''variable''', the contents of a Luup variable are displayed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with six keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the variable's value is placed in the tab), '''Service''' (a string containing the Service Id of the variable to be displayed), and '''Variable''' (a string containing the name of the variable to be displayed).&lt;br /&gt;
&lt;br /&gt;
===ControlType input===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''input''', a text box for the user to type a string is placed in the tab. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''ID''' &lt;br /&gt;
:A string which other controls (buttons) will use to refer to the string that the user has typed into this field. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers). This controls where the text box is placed in the tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType button===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''button''', a clickable button is placed at the specified coordinates. The button will appear selected (depressed) or unselected based on the value of a variable, permitting sets of buttons to act as radio buttons. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys: '''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. '''text''' is displayed as the text inside the button. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with up to seven keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), and (optionally) '''Value''' (a string, &amp;quot;1&amp;quot; if omitted). The specified variable's value controls whether the button appears selected (if the variable matches '''Value''') or not selected (if the variable doesn't match). &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching an input control on the page (for a user-entered parameter value).&lt;br /&gt;
&lt;br /&gt;
===ControlType slider===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''slider''', a draggable slider is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with nine keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), '''MinValue''' and '''MaxValue''' (both strings containing numbers, controlling what range of values the slider takes) and '''ID''' (a string, used in the '''Command''' to refer to the position the user has dragged the slider to). The specified variable's value is used to display the slider's current position. &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching the slider's ID (for a value matching what the user has dragged the slider to). &lt;br /&gt;
;'''ControlHeader''' &lt;br /&gt;
:Optional. If present, must have string value &amp;quot;1&amp;quot;. When set, the slider's label and value are displayed. Otherwise only the slider is shown. (TODO: This is from reverse-engineering the JavaScript code. Test this.)&lt;br /&gt;
&lt;br /&gt;
===ControlType slider_vertical===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_Heater1.json, D_HVAC_ZoneThermostat1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType checkbox===&lt;br /&gt;
&lt;br /&gt;
:As of UI5 firmware 1.5.622, checkboxes are only half-baked and not usable in &amp;quot;flash&amp;quot; style tabs. If you need check-boxes, radio buttons, pop-up menus, etc. in your tab, you need to create a Javascript tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType image===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType image_player===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType js_button===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
=UI7 Updates=&lt;br /&gt;
&lt;br /&gt;
===ControlType line_break===&lt;br /&gt;
This is used to force the next control to appear below the previous one rather than alongside it. See [[Static JSON file]]&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘line_break’&lt;br /&gt;
&lt;br /&gt;
===ControlType multi_state_button=== &lt;br /&gt;
This is a new type of control in UI7 and works as a “toggle” button. It is used for On/Off, Armed/Disarmed etc buttons.&lt;br /&gt;
&lt;br /&gt;
As an example, here is the definition from the .json file of the Door/Window sensor needed to create this kind of control:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
&amp;quot;ControlType&amp;quot;: &amp;quot;multi_state_button&amp;quot;,&lt;br /&gt;
&amp;quot;top&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
&amp;quot;left&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;states&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
                  &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                      &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_arm&amp;quot;,&lt;br /&gt;
                      &amp;quot;text&amp;quot;: &amp;quot;Arm&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                         &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                           &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                   {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                           ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;arm&amp;quot;&lt;br /&gt;
              },&lt;br /&gt;
              {&lt;br /&gt;
                     &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                           &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_bypass&amp;quot;,&lt;br /&gt;
                           &amp;quot;text&amp;quot;: &amp;quot;Bypass&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                            ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;bypass&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
        ]&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘multi_state_button’&lt;br /&gt;
* '''top''' - an integer specifying the row from the control group where the control will be placed&lt;br /&gt;
* '''left''' - an integer which specifies which column will the control be placed in&lt;br /&gt;
* '''states''' - an array of objects, each object representing a different state for the button; each object has the following properties:&lt;br /&gt;
** '''Lable''' - a JSON object with two keys:&lt;br /&gt;
*** '''lang_tag''' - a string used for localization&lt;br /&gt;
*** '''text''' - the string displayed if localization fails&lt;br /&gt;
** '''Display''' - a JSON object with the following keys:&lt;br /&gt;
*** '''Service''' - a string containing the service id&lt;br /&gt;
*** '''Variable''' - a string containing the name of the variable&lt;br /&gt;
*** '''Value''' - a number which represents the value of the state in which the control must be in order for this state to be displayed/marked as selected&lt;br /&gt;
** '''Command''' - a JSON object with the following properties:&lt;br /&gt;
*** '''Service''' - a string with the service id of the action which this state will perform&lt;br /&gt;
*** '''Action''' - a string representing the name of the action to be executed&lt;br /&gt;
*** '''Parameters''' - an array of JSON objects holding the parameters to pass to the action&lt;br /&gt;
* '''ControlCode''' - a string containing the control code of this control (it is recommended to be unique for each device type)&lt;br /&gt;
&lt;br /&gt;
As you can see, the multi_state_button is a merge resulted from two controls of type button.&lt;br /&gt;
&lt;br /&gt;
[[File:MotionSensor.jpg|image|left]]&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_tabs</id>
		<title>Luup plugin tabs</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_tabs"/>
				<updated>2015-03-28T22:55:17Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: /* UI7 Updates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Root Keys=&lt;br /&gt;
Refer to [[Luup_plugins:_Static_JSON_file|Root Keys]].&lt;br /&gt;
&lt;br /&gt;
=Tabs Key=&lt;br /&gt;
The '''Tabs''' key in the [[Luup plugins: Static JSON file|static JSON file]] performs two duties in the UI4 HTML interface: &lt;br /&gt;
&lt;br /&gt;
*It controls how the device appears on the '''Dashboard''' and '''Devices''' web pages.&lt;br /&gt;
*It controls how each tab appears in the the device's detailed dialog.&lt;br /&gt;
&lt;br /&gt;
The value of the '''Tabs''' key is a JSON array. Each array element is a JSON object (associative array). One for each Tab. &lt;br /&gt;
&lt;br /&gt;
==Tab fields==&lt;br /&gt;
These fields must be present for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object containing two keys:&lt;br /&gt;
*'''lang_tag''' (a string) is not displayed in the HTML interface but may be used by localized UIs.&lt;br /&gt;
*'''text''' (a string) is displayed in the tab's handle at the top of the dialog. &lt;br /&gt;
;'''Position''' &lt;br /&gt;
:A string which contains a number matching the relative position of this tab in relation to the other tabs. The leftmost tab has value &amp;quot;0&amp;quot;, the next tab &amp;quot;1&amp;quot;, and so on. &lt;br /&gt;
;'''TabType''' &lt;br /&gt;
:A string which describes how the content of the tab is to be generated. The value may be one of '''javascript''' or '''flash'''. These are described in the following sections.&lt;br /&gt;
&lt;br /&gt;
These fields are optional for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''ControlGroup'''&lt;br /&gt;
:Required only if the first tab is also used to display elements on the dashboard. See [[Luup_plugin_tabs#Dashboard_appearance|''Dashboard appearance'']] below.&lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
:Added in UI5.&lt;br /&gt;
&lt;br /&gt;
;'''Control'''&lt;br /&gt;
:An array of controls for each tab. Each control consists of:&lt;br /&gt;
*'''ControlType''' - the Control Types are described further below. &lt;br /&gt;
*'''ControlGroup''' - a reference to the control's ControlGroup &lt;br /&gt;
*'''text_align''' - align left or right &lt;br /&gt;
*'''top''' - dashboard position in button units&lt;br /&gt;
*'''left''' - dashboard position in button units&lt;br /&gt;
&lt;br /&gt;
eg a slider sitting above a two by two array of buttons:&lt;br /&gt;
&lt;br /&gt;
slider: top = 0, left = 0&amp;lt;br\&amp;gt;&lt;br /&gt;
top left button: top = 1.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
top right button: top = 1.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
bot left button: top = 2.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
bot right button: top = 2.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TabType javascript===&lt;br /&gt;
The '''javascript''' tab type executes client-side JavaScript, producing an HTML fragment which forms the body of the tab. These additional keys are required in the tab JSON object: &lt;br /&gt;
&lt;br /&gt;
;'''ScriptName''' &lt;br /&gt;
:The name of the JavaScript file. Conventionally, plugin JavaScript files are named '''J_PluginName.js'''. &lt;br /&gt;
;'''Function''' &lt;br /&gt;
:The function inside the JavaScript file which will be called to produce the HTML.&lt;br /&gt;
&lt;br /&gt;
The JavaScript code has access to most Luup variables through the [[JavaScript API]]. &lt;br /&gt;
&lt;br /&gt;
===TabType flash===&lt;br /&gt;
The '''flash''' tab type creates a tab body with a simple page-description language. Each component of the page is placed into the body at the position specified. You can't place a '''Control''' on the dashboard unless you place it in a '''ControlGroup''', which in turn must be placed in a '''SceneGroup''': &lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
&lt;br /&gt;
''SceneGroup'' (added in UI5) is similar to a ControlGroup, but with some key differences: it is customizable (by setting its position and dimensions) and it defines the behavior of the buttons assigned to the same state variable. If two buttons in the same SceneGroup are assigned to the same variable, only one of them can be activated in the Scene Editor (radio buttons behavior). If two buttons assigned to the same variable are in different SceneGroups, both can be activated at the same time in the Scene Editor (checkboxes behavior). &lt;br /&gt;
&lt;br /&gt;
It has the following tags: &lt;br /&gt;
&lt;br /&gt;
*'''id''': A numeric value (positive integer numbers). &lt;br /&gt;
*'''top''': The offset from the device header. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''left''': The offset from the device icon. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''x''', '''y''': the dimensions of the SceneGroup. e.g If I have two horizontally aligned buttons in the SceneGroup, ''x'' will be ''2'' and ''y'' will be ''1''.&lt;br /&gt;
&lt;br /&gt;
A ''SceneGroup'' can contain one or more ''ControlGroups''. &lt;br /&gt;
&lt;br /&gt;
===Dashboard appearance===&lt;br /&gt;
The first array element in the '''Tabs''' field is special. If it is of type '''flash''' then a subset of the controls on the tab can be chosen to display on the UI4 dashboard.&lt;br /&gt;
&lt;br /&gt;
The dashboard box for a device has room for two rows of information and controls. These are called ''Control Groups''.&lt;br /&gt;
&lt;br /&gt;
The tab object must contain the key '''ControlGroup''', which specifies how the dashboard's two available rows of information are to be handled. '''ControlGroup''' is a JSON array containing JSON objects (associative arrays). Each object has the following keys:&lt;br /&gt;
&lt;br /&gt;
;'''id''' &lt;br /&gt;
:A string containing a number, &amp;quot;1&amp;quot; for the first object, &amp;quot;2&amp;quot; for the second, and so on. &lt;br /&gt;
;'''isSingle''' &lt;br /&gt;
:UI4: If this key is present and contains the value &amp;quot;1&amp;quot;, then this control group is combined with other control groups that have '''isSingle''' similarly set onto the same row. For an example of this, see the On/Off buttons of the Binary Switch and Dimmable Switch static JSON files. &lt;br /&gt;
:UI5: Since controls cannot be grouped in drop-down lists anymore, this tag has a different role in UI5. When isSingle is &amp;quot;1&amp;quot;, the buttons in the ControlGroup will have a background (e.g. the BinaryLight buttons). If isSingle is missing, the buttons won't have a background. That is the button shape is not shown; only the button text will show. &lt;br /&gt;
;'''type''' &lt;br /&gt;
:If this key is present and contains the value &amp;quot;info&amp;quot;, then the controls displayed on this row are not clickable. If this key is absent, buttons may be used in the control group.&lt;br /&gt;
&lt;br /&gt;
===Making a control appear on the dashboard===&lt;br /&gt;
All control types (label, variable, button, input, slider) can appear in the dashboard. However, by default, controls in the first tab's detail view do not appear in the dashboard. To make a control's text appear in the dashboard, add a '''ControlGroup''' to the control's JSON object. The value is a string containing a number, matching the '''id''' of one of the Tab's overall '''ControlGroup''' key. The dashboard will concatenate all of a control group's controls in the order they are presented in the '''Control''' array, and display them in a single row of text. &lt;br /&gt;
&lt;br /&gt;
===Keeping controls together===&lt;br /&gt;
Some controls may not make sense if presented individually (for instance, an input for an alarm PIN and a &amp;quot;disarm&amp;quot; button). To keep these controls together, add a '''ControlPair''' key to all of the controls that have to appear together. Use the same value (a number in a string) for all of them.&lt;br /&gt;
&lt;br /&gt;
==Control Types==&lt;br /&gt;
&lt;br /&gt;
The '''Control''' array contains JSON objects (associative arrays), one per item in the tab body. An item is either a static text label, a variable, an input field, or a button or slider. This is handled by setting the '''ControlType''' key on the object. &lt;br /&gt;
&lt;br /&gt;
===ControlType label===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''label''', a fixed label is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys:&lt;br /&gt;
*'''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. The key is optional.&lt;br /&gt;
*'''text''' is displayed as the content of the label. In UI5 on the &amp;quot;Dashboard&amp;quot;, the text is rendered by passing it in the &amp;quot;Title&amp;quot; attribute of a Div statement, so it can only be plain text, (as opposed to say HTML). However in the tabs, it is placed in a Div statement so HTML, such as a URL link, can be rendered. &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys, which are strings containing numbers (in pixels?). This controls where the label is placed on the tab. The label is left aligned.&lt;br /&gt;
*'''Top''': The offset from the top of tab writable area.&lt;br /&gt;
*'''Left''': The offset from the left side of the tab writable area.&lt;br /&gt;
*'''Width''': Window width of the label.&lt;br /&gt;
*'''Height''': Window height of the label.&lt;br /&gt;
&lt;br /&gt;
===ControlType variable===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''variable''', the contents of a Luup variable are displayed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with six keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the variable's value is placed in the tab), '''Service''' (a string containing the Service Id of the variable to be displayed), and '''Variable''' (a string containing the name of the variable to be displayed).&lt;br /&gt;
&lt;br /&gt;
===ControlType input===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''input''', a text box for the user to type a string is placed in the tab. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''ID''' &lt;br /&gt;
:A string which other controls (buttons) will use to refer to the string that the user has typed into this field. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers). This controls where the text box is placed in the tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType button===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''button''', a clickable button is placed at the specified coordinates. The button will appear selected (depressed) or unselected based on the value of a variable, permitting sets of buttons to act as radio buttons. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys: '''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. '''text''' is displayed as the text inside the button. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with up to seven keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), and (optionally) '''Value''' (a string, &amp;quot;1&amp;quot; if omitted). The specified variable's value controls whether the button appears selected (if the variable matches '''Value''') or not selected (if the variable doesn't match). &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching an input control on the page (for a user-entered parameter value).&lt;br /&gt;
&lt;br /&gt;
===ControlType slider===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''slider''', a draggable slider is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with nine keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), '''MinValue''' and '''MaxValue''' (both strings containing numbers, controlling what range of values the slider takes) and '''ID''' (a string, used in the '''Command''' to refer to the position the user has dragged the slider to). The specified variable's value is used to display the slider's current position. &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching the slider's ID (for a value matching what the user has dragged the slider to). &lt;br /&gt;
;'''ControlHeader''' &lt;br /&gt;
:Optional. If present, must have string value &amp;quot;1&amp;quot;. When set, the slider's label and value are displayed. Otherwise only the slider is shown. (TODO: This is from reverse-engineering the JavaScript code. Test this.)&lt;br /&gt;
&lt;br /&gt;
===ControlType slider_vertical===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_Heater1.json, D_HVAC_ZoneThermostat1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType checkbox===&lt;br /&gt;
&lt;br /&gt;
:As of UI5 firmware 1.5.622, checkboxes are only half-baked and not usable in &amp;quot;flash&amp;quot; style tabs. If you need check-boxes, radio buttons, pop-up menus, etc. in your tab, you need to create a Javascript tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType image===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType image_player===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType js_button===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
=UI7 Updates=&lt;br /&gt;
&lt;br /&gt;
===ControlType line_break===&lt;br /&gt;
This is used to force the next control to appear below the previous one rather than alongside it&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘line_break’&lt;br /&gt;
&lt;br /&gt;
===ControlType multi_state_button=== &lt;br /&gt;
This is a new type of control in UI7 and works as a “toggle” button. It is used for On/Off, Armed/Disarmed etc buttons.&lt;br /&gt;
&lt;br /&gt;
As an example, here is the definition from the .json file of the Door/Window sensor needed to create this kind of control:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
&amp;quot;ControlType&amp;quot;: &amp;quot;multi_state_button&amp;quot;,&lt;br /&gt;
&amp;quot;top&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
&amp;quot;left&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;states&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
                  &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                      &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_arm&amp;quot;,&lt;br /&gt;
                      &amp;quot;text&amp;quot;: &amp;quot;Arm&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                         &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                           &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                   {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                           ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;arm&amp;quot;&lt;br /&gt;
              },&lt;br /&gt;
              {&lt;br /&gt;
                     &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                           &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_bypass&amp;quot;,&lt;br /&gt;
                           &amp;quot;text&amp;quot;: &amp;quot;Bypass&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                            ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;bypass&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
        ]&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘multi_state_button’&lt;br /&gt;
* '''top''' - an integer specifying the row from the control group where the control will be placed&lt;br /&gt;
* '''left''' - an integer which specifies which column will the control be placed in&lt;br /&gt;
* '''states''' - an array of objects, each object representing a different state for the button; each object has the following properties:&lt;br /&gt;
** '''Lable''' - a JSON object with two keys:&lt;br /&gt;
*** '''lang_tag''' - a string used for localization&lt;br /&gt;
*** '''text''' - the string displayed if localization fails&lt;br /&gt;
** '''Display''' - a JSON object with the following keys:&lt;br /&gt;
*** '''Service''' - a string containing the service id&lt;br /&gt;
*** '''Variable''' - a string containing the name of the variable&lt;br /&gt;
*** '''Value''' - a number which represents the value of the state in which the control must be in order for this state to be displayed/marked as selected&lt;br /&gt;
** '''Command''' - a JSON object with the following properties:&lt;br /&gt;
*** '''Service''' - a string with the service id of the action which this state will perform&lt;br /&gt;
*** '''Action''' - a string representing the name of the action to be executed&lt;br /&gt;
*** '''Parameters''' - an array of JSON objects holding the parameters to pass to the action&lt;br /&gt;
* '''ControlCode''' - a string containing the control code of this control (it is recommended to be unique for each device type)&lt;br /&gt;
&lt;br /&gt;
As you can see, the multi_state_button is a merge resulted from two controls of type button.&lt;br /&gt;
&lt;br /&gt;
[[File:MotionSensor.jpg|image|left]]&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_tabs</id>
		<title>Luup plugin tabs</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_tabs"/>
				<updated>2015-03-28T22:49:32Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: /* ControlType multi_state_button */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Root Keys=&lt;br /&gt;
Refer to [[Luup_plugins:_Static_JSON_file|Root Keys]].&lt;br /&gt;
&lt;br /&gt;
=Tabs Key=&lt;br /&gt;
The '''Tabs''' key in the [[Luup plugins: Static JSON file|static JSON file]] performs two duties in the UI4 HTML interface: &lt;br /&gt;
&lt;br /&gt;
*It controls how the device appears on the '''Dashboard''' and '''Devices''' web pages.&lt;br /&gt;
*It controls how each tab appears in the the device's detailed dialog.&lt;br /&gt;
&lt;br /&gt;
The value of the '''Tabs''' key is a JSON array. Each array element is a JSON object (associative array). One for each Tab. &lt;br /&gt;
&lt;br /&gt;
==Tab fields==&lt;br /&gt;
These fields must be present for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object containing two keys:&lt;br /&gt;
*'''lang_tag''' (a string) is not displayed in the HTML interface but may be used by localized UIs.&lt;br /&gt;
*'''text''' (a string) is displayed in the tab's handle at the top of the dialog. &lt;br /&gt;
;'''Position''' &lt;br /&gt;
:A string which contains a number matching the relative position of this tab in relation to the other tabs. The leftmost tab has value &amp;quot;0&amp;quot;, the next tab &amp;quot;1&amp;quot;, and so on. &lt;br /&gt;
;'''TabType''' &lt;br /&gt;
:A string which describes how the content of the tab is to be generated. The value may be one of '''javascript''' or '''flash'''. These are described in the following sections.&lt;br /&gt;
&lt;br /&gt;
These fields are optional for each tab JSON object in the tab array: &lt;br /&gt;
&lt;br /&gt;
;'''ControlGroup'''&lt;br /&gt;
:Required only if the first tab is also used to display elements on the dashboard. See [[Luup_plugin_tabs#Dashboard_appearance|''Dashboard appearance'']] below.&lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
:Added in UI5.&lt;br /&gt;
&lt;br /&gt;
;'''Control'''&lt;br /&gt;
:An array of controls for each tab. Each control consists of:&lt;br /&gt;
*'''ControlType''' - the Control Types are described further below. &lt;br /&gt;
*'''ControlGroup''' - a reference to the control's ControlGroup &lt;br /&gt;
*'''text_align''' - align left or right &lt;br /&gt;
*'''top''' - dashboard position in button units&lt;br /&gt;
*'''left''' - dashboard position in button units&lt;br /&gt;
&lt;br /&gt;
eg a slider sitting above a two by two array of buttons:&lt;br /&gt;
&lt;br /&gt;
slider: top = 0, left = 0&amp;lt;br\&amp;gt;&lt;br /&gt;
top left button: top = 1.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
top right button: top = 1.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
bot left button: top = 2.5, left = 0&amp;lt;br/&amp;gt;&lt;br /&gt;
bot right button: top = 2.5, left = 1&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===TabType javascript===&lt;br /&gt;
The '''javascript''' tab type executes client-side JavaScript, producing an HTML fragment which forms the body of the tab. These additional keys are required in the tab JSON object: &lt;br /&gt;
&lt;br /&gt;
;'''ScriptName''' &lt;br /&gt;
:The name of the JavaScript file. Conventionally, plugin JavaScript files are named '''J_PluginName.js'''. &lt;br /&gt;
;'''Function''' &lt;br /&gt;
:The function inside the JavaScript file which will be called to produce the HTML.&lt;br /&gt;
&lt;br /&gt;
The JavaScript code has access to most Luup variables through the [[JavaScript API]]. &lt;br /&gt;
&lt;br /&gt;
===TabType flash===&lt;br /&gt;
The '''flash''' tab type creates a tab body with a simple page-description language. Each component of the page is placed into the body at the position specified. You can't place a '''Control''' on the dashboard unless you place it in a '''ControlGroup''', which in turn must be placed in a '''SceneGroup''': &lt;br /&gt;
&lt;br /&gt;
;'''SceneGroup'''&lt;br /&gt;
&lt;br /&gt;
''SceneGroup'' (added in UI5) is similar to a ControlGroup, but with some key differences: it is customizable (by setting its position and dimensions) and it defines the behavior of the buttons assigned to the same state variable. If two buttons in the same SceneGroup are assigned to the same variable, only one of them can be activated in the Scene Editor (radio buttons behavior). If two buttons assigned to the same variable are in different SceneGroups, both can be activated at the same time in the Scene Editor (checkboxes behavior). &lt;br /&gt;
&lt;br /&gt;
It has the following tags: &lt;br /&gt;
&lt;br /&gt;
*'''id''': A numeric value (positive integer numbers). &lt;br /&gt;
*'''top''': The offset from the device header. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''left''': The offset from the device icon. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''x''', '''y''': the dimensions of the SceneGroup. e.g If I have two horizontally aligned buttons in the SceneGroup, ''x'' will be ''2'' and ''y'' will be ''1''.&lt;br /&gt;
&lt;br /&gt;
A ''SceneGroup'' can contain one or more ''ControlGroups''. &lt;br /&gt;
&lt;br /&gt;
===Dashboard appearance===&lt;br /&gt;
The first array element in the '''Tabs''' field is special. If it is of type '''flash''' then a subset of the controls on the tab can be chosen to display on the UI4 dashboard.&lt;br /&gt;
&lt;br /&gt;
The dashboard box for a device has room for two rows of information and controls. These are called ''Control Groups''.&lt;br /&gt;
&lt;br /&gt;
The tab object must contain the key '''ControlGroup''', which specifies how the dashboard's two available rows of information are to be handled. '''ControlGroup''' is a JSON array containing JSON objects (associative arrays). Each object has the following keys:&lt;br /&gt;
&lt;br /&gt;
;'''id''' &lt;br /&gt;
:A string containing a number, &amp;quot;1&amp;quot; for the first object, &amp;quot;2&amp;quot; for the second, and so on. &lt;br /&gt;
;'''isSingle''' &lt;br /&gt;
:UI4: If this key is present and contains the value &amp;quot;1&amp;quot;, then this control group is combined with other control groups that have '''isSingle''' similarly set onto the same row. For an example of this, see the On/Off buttons of the Binary Switch and Dimmable Switch static JSON files. &lt;br /&gt;
:UI5: Since controls cannot be grouped in drop-down lists anymore, this tag has a different role in UI5. When isSingle is &amp;quot;1&amp;quot;, the buttons in the ControlGroup will have a background (e.g. the BinaryLight buttons). If isSingle is missing, the buttons won't have a background. That is the button shape is not shown; only the button text will show. &lt;br /&gt;
;'''type''' &lt;br /&gt;
:If this key is present and contains the value &amp;quot;info&amp;quot;, then the controls displayed on this row are not clickable. If this key is absent, buttons may be used in the control group.&lt;br /&gt;
&lt;br /&gt;
===Making a control appear on the dashboard===&lt;br /&gt;
All control types (label, variable, button, input, slider) can appear in the dashboard. However, by default, controls in the first tab's detail view do not appear in the dashboard. To make a control's text appear in the dashboard, add a '''ControlGroup''' to the control's JSON object. The value is a string containing a number, matching the '''id''' of one of the Tab's overall '''ControlGroup''' key. The dashboard will concatenate all of a control group's controls in the order they are presented in the '''Control''' array, and display them in a single row of text. &lt;br /&gt;
&lt;br /&gt;
===Keeping controls together===&lt;br /&gt;
Some controls may not make sense if presented individually (for instance, an input for an alarm PIN and a &amp;quot;disarm&amp;quot; button). To keep these controls together, add a '''ControlPair''' key to all of the controls that have to appear together. Use the same value (a number in a string) for all of them.&lt;br /&gt;
&lt;br /&gt;
==Control Types==&lt;br /&gt;
&lt;br /&gt;
The '''Control''' array contains JSON objects (associative arrays), one per item in the tab body. An item is either a static text label, a variable, an input field, or a button or slider. This is handled by setting the '''ControlType''' key on the object. &lt;br /&gt;
&lt;br /&gt;
===ControlType label===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''label''', a fixed label is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys:&lt;br /&gt;
*'''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. The key is optional.&lt;br /&gt;
*'''text''' is displayed as the content of the label. In UI5 on the &amp;quot;Dashboard&amp;quot;, the text is rendered by passing it in the &amp;quot;Title&amp;quot; attribute of a Div statement, so it can only be plain text, (as opposed to say HTML). However in the tabs, it is placed in a Div statement so HTML, such as a URL link, can be rendered. &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys, which are strings containing numbers (in pixels?). This controls where the label is placed on the tab. The label is left aligned.&lt;br /&gt;
*'''Top''': The offset from the top of tab writable area.&lt;br /&gt;
*'''Left''': The offset from the left side of the tab writable area.&lt;br /&gt;
*'''Width''': Window width of the label.&lt;br /&gt;
*'''Height''': Window height of the label.&lt;br /&gt;
&lt;br /&gt;
===ControlType variable===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''variable''', the contents of a Luup variable are displayed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with six keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the variable's value is placed in the tab), '''Service''' (a string containing the Service Id of the variable to be displayed), and '''Variable''' (a string containing the name of the variable to be displayed).&lt;br /&gt;
&lt;br /&gt;
===ControlType input===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''input''', a text box for the user to type a string is placed in the tab. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''ID''' &lt;br /&gt;
:A string which other controls (buttons) will use to refer to the string that the user has typed into this field. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers). This controls where the text box is placed in the tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType button===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''button''', a clickable button is placed at the specified coordinates. The button will appear selected (depressed) or unselected based on the value of a variable, permitting sets of buttons to act as radio buttons. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys: '''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. '''text''' is displayed as the text inside the button. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with up to seven keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), and (optionally) '''Value''' (a string, &amp;quot;1&amp;quot; if omitted). The specified variable's value controls whether the button appears selected (if the variable matches '''Value''') or not selected (if the variable doesn't match). &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching an input control on the page (for a user-entered parameter value).&lt;br /&gt;
&lt;br /&gt;
===ControlType slider===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''slider''', a draggable slider is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with nine keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), '''MinValue''' and '''MaxValue''' (both strings containing numbers, controlling what range of values the slider takes) and '''ID''' (a string, used in the '''Command''' to refer to the position the user has dragged the slider to). The specified variable's value is used to display the slider's current position. &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching the slider's ID (for a value matching what the user has dragged the slider to). &lt;br /&gt;
;'''ControlHeader''' &lt;br /&gt;
:Optional. If present, must have string value &amp;quot;1&amp;quot;. When set, the slider's label and value are displayed. Otherwise only the slider is shown. (TODO: This is from reverse-engineering the JavaScript code. Test this.)&lt;br /&gt;
&lt;br /&gt;
===ControlType slider_vertical===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_Heater1.json, D_HVAC_ZoneThermostat1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType checkbox===&lt;br /&gt;
&lt;br /&gt;
:As of UI5 firmware 1.5.622, checkboxes are only half-baked and not usable in &amp;quot;flash&amp;quot; style tabs. If you need check-boxes, radio buttons, pop-up menus, etc. in your tab, you need to create a Javascript tab.&lt;br /&gt;
&lt;br /&gt;
===ControlType image===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType image_player===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
===ControlType js_button===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
=UI7 Updates=&lt;br /&gt;
&lt;br /&gt;
===ControlType multi_state_button=== &lt;br /&gt;
This is a new type of control in UI7 and works as a “toggle” button. It is used for On/Off, Armed/Disarmed etc buttons.&lt;br /&gt;
&lt;br /&gt;
As an example, here is the definition from the .json file of the Door/Window sensor needed to create this kind of control:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
&amp;quot;ControlType&amp;quot;: &amp;quot;multi_state_button&amp;quot;,&lt;br /&gt;
&amp;quot;top&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
&amp;quot;left&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
       &amp;quot;states&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
                  &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                      &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_arm&amp;quot;,&lt;br /&gt;
                      &amp;quot;text&amp;quot;: &amp;quot;Arm&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                         &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                         &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                           &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                           &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                   {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                           ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;arm&amp;quot;&lt;br /&gt;
              },&lt;br /&gt;
              {&lt;br /&gt;
                     &amp;quot;Label&amp;quot;: {&lt;br /&gt;
                           &amp;quot;lang_tag&amp;quot;: &amp;quot;ui7_cmd_bypass&amp;quot;,&lt;br /&gt;
                           &amp;quot;text&amp;quot;: &amp;quot;Bypass&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlGroup&amp;quot;: &amp;quot;1&amp;quot;,&lt;br /&gt;
                     &amp;quot;Display&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Variable&amp;quot;: &amp;quot;Armed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;Command&amp;quot;: {&lt;br /&gt;
                           &amp;quot;Service&amp;quot;: &amp;quot;urn:micasaverde-com:serviceId:SecuritySensor1&amp;quot;,&lt;br /&gt;
                            &amp;quot;Action&amp;quot;: &amp;quot;SetArmed&amp;quot;,&lt;br /&gt;
                            &amp;quot;Parameters&amp;quot;: [&lt;br /&gt;
                                {&lt;br /&gt;
                                        &amp;quot;Name&amp;quot;: &amp;quot;newArmedValue&amp;quot;,&lt;br /&gt;
                                        &amp;quot;Value&amp;quot;: &amp;quot;0&amp;quot;&lt;br /&gt;
                                    }&lt;br /&gt;
                            ]&lt;br /&gt;
                     },&lt;br /&gt;
                     &amp;quot;ControlCode&amp;quot;: &amp;quot;bypass&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
        ]&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''ControlGroup''' - an integer - has the same behaviour as in previous UIs &lt;br /&gt;
* '''ControlType''' - a string which must be set to ‘multi_state_button’&lt;br /&gt;
* '''top''' - an integer specifying the row from the control group where the control will be placed&lt;br /&gt;
* '''left''' - an integer which specifies which column will the control be placed in&lt;br /&gt;
* '''states''' - an array of objects, each object representing a different state for the button; each object has the following properties:&lt;br /&gt;
** '''Lable''' - a JSON object with two keys:&lt;br /&gt;
*** '''lang_tag''' - a string used for localization&lt;br /&gt;
*** '''text''' - the string displayed if localization fails&lt;br /&gt;
** '''Display''' - a JSON object with the following keys:&lt;br /&gt;
*** '''Service''' - a string containing the service id&lt;br /&gt;
*** '''Variable''' - a string containing the name of the variable&lt;br /&gt;
*** '''Value''' - a number which represents the value of the state in which the control must be in order for this state to be displayed/marked as selected&lt;br /&gt;
** '''Command''' - a JSON object with the following properties:&lt;br /&gt;
*** '''Service''' - a string with the service id of the action which this state will perform&lt;br /&gt;
*** '''Action''' - a string representing the name of the action to be executed&lt;br /&gt;
*** '''Parameters''' - an array of JSON objects holding the parameters to pass to the action&lt;br /&gt;
* '''ControlCode''' - a string containing the control code of this control (it is recommended to be unique for each device type)&lt;br /&gt;
&lt;br /&gt;
As you can see, the multi_state_button is a merge resulted from two controls of type button.&lt;br /&gt;
&lt;br /&gt;
[[File:MotionSensor.jpg|image|left]]&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_icons</id>
		<title>Luup plugin icons</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_icons"/>
				<updated>2014-11-30T08:04:36Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
&lt;br /&gt;
The [[Luup plugins: Static JSON file|Luup plugin static JSON file]] describes the icon that appears in the dashboard of the UI4 web interface.&lt;br /&gt;
&lt;br /&gt;
=Icon format=&lt;br /&gt;
&lt;br /&gt;
All of Vera's icons in UI5 are 42x42 pixel PNGs with transparency. Icons of other sizes will be resized to 42x42. In UI7, icon size has grown to 50x50.&lt;br /&gt;
&lt;br /&gt;
=Configuration=&lt;br /&gt;
&lt;br /&gt;
In UI5, the top-level key '''flashicon''' contains a string.  This string is the name of an image file.  The filename is either a relative path or a URL starting with &amp;quot;http&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
==Relative paths==&lt;br /&gt;
&lt;br /&gt;
In UI5, If the filename is a relative path, it is relative to the directory '''/www/cmh/skins/default''' on the Vera.  If the filename ends in the string &amp;quot;.swf&amp;quot;, this extension is changed to &amp;quot;.png&amp;quot;.  (This is presumably a holdover from UI3, which was flash-based.  In UI4, there is no reason to maintain this ruse, so just use the &amp;quot;.png&amp;quot; extension.)&lt;br /&gt;
&lt;br /&gt;
Files installed in the &amp;quot;icons&amp;quot; directory are:&lt;br /&gt;
* Binary_Light.png&lt;br /&gt;
* Binary_Light_0.png&lt;br /&gt;
* Binary_Light_100.png&lt;br /&gt;
* Dimmable_Light.png&lt;br /&gt;
* Dimmable_Light_0.png&lt;br /&gt;
* Dimmable_Light_100.png&lt;br /&gt;
* Dimmable_Light_25.png&lt;br /&gt;
* Dimmable_Light_50.png&lt;br /&gt;
* Dimmable_Light_75.png&lt;br /&gt;
* Door_Lock.png&lt;br /&gt;
* Door_Lock_0.png&lt;br /&gt;
* Door_Lock_100.png&lt;br /&gt;
* Generic_IO.png&lt;br /&gt;
* Humidity_Sensor.png&lt;br /&gt;
* IR_Transmitter.png&lt;br /&gt;
* Insteon.png&lt;br /&gt;
* Ip_Camera.png&lt;br /&gt;
* Light_Sensor.png&lt;br /&gt;
* Motion_Sensor.png&lt;br /&gt;
* Motion_Sensor_0.png&lt;br /&gt;
* Motion_Sensor_100.png&lt;br /&gt;
* Power_Meter.png&lt;br /&gt;
* Scenes.png&lt;br /&gt;
* Temperature_Sensor.png&lt;br /&gt;
* Thermostat.png&lt;br /&gt;
* USB_UIRT.png&lt;br /&gt;
* Window_Covering.png&lt;br /&gt;
* Zwave.png&lt;br /&gt;
* advanced.png&lt;br /&gt;
* default.png&lt;br /&gt;
* default_device.png&lt;br /&gt;
* default_panel.png&lt;br /&gt;
* default_plugins.png&lt;br /&gt;
* device.png&lt;br /&gt;
* devices.png&lt;br /&gt;
* energy.png&lt;br /&gt;
* findvera.png&lt;br /&gt;
* generic_icon.png&lt;br /&gt;
* generic_sensor.png&lt;br /&gt;
* intro.png&lt;br /&gt;
* location.png&lt;br /&gt;
* music_audio.png&lt;br /&gt;
* plugins.png&lt;br /&gt;
* users.png&lt;br /&gt;
&lt;br /&gt;
== URLs ==&lt;br /&gt;
&lt;br /&gt;
If the filename is a URL, it refers to a remote file on a web server.&lt;br /&gt;
&lt;br /&gt;
=Constant icons=&lt;br /&gt;
&lt;br /&gt;
For an icon that remains the same all the time in UI5, the '''flashicon''' key is all you need to provide.  Ensure that the static JSON file does not contain a '''DisplayStatus''' key, and you are done.&lt;br /&gt;
&lt;br /&gt;
= Variable icons  =&lt;br /&gt;
&lt;br /&gt;
For an icon that varies based on the value of a variable, place a '''DisplayStatus''' key at the top level of the static JSON file. Its value is a JSON object (associative array) with four keys: &lt;br /&gt;
&lt;br /&gt;
;Service &lt;br /&gt;
:The Service ID of the variable that controls the icon. This can be found in the matching device XML file, under the '''serviceId''' element. &lt;br /&gt;
;Variable &lt;br /&gt;
:The variable name of the variable that controls the icon. &lt;br /&gt;
;MinValue &lt;br /&gt;
:The value that this variable has when it is considered &amp;quot;off&amp;quot; or at &amp;quot;0%&amp;quot;. This can even be a string, if the variable is a string that takes two values. &lt;br /&gt;
;MaxValue &lt;br /&gt;
:The value that this variable has when it is considered &amp;quot;on&amp;quot; or at &amp;quot;100%&amp;quot;. This can even be a string, if the variable is a string that takes two values.&lt;br /&gt;
&lt;br /&gt;
Example for a binary switch: &lt;br /&gt;
&amp;lt;pre&amp;gt;  &amp;quot;DisplayStatus&amp;quot;: {&lt;br /&gt;
        &amp;quot;Service&amp;quot;: &amp;quot;urn:upnp-org:serviceId:SwitchPower1&amp;quot;,&lt;br /&gt;
        &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
        &amp;quot;MinValue&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
        &amp;quot;MaxValue&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
Example for a variable which takes on a range of values. &lt;br /&gt;
&amp;lt;pre&amp;gt;  &amp;quot;DisplayStatus&amp;quot;: {&lt;br /&gt;
        &amp;quot;Service&amp;quot;: &amp;quot;urn:upnp-org:serviceId:Dimming1&amp;quot;,&lt;br /&gt;
        &amp;quot;Variable&amp;quot;: &amp;quot;LoadLevelStatus&amp;quot;,&lt;br /&gt;
        &amp;quot;MinValue&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
        &amp;quot;MaxValue&amp;quot;: &amp;quot;100&amp;quot; &lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
The filename in '''flashicon''' undergoes a special transformation for variable icons. The extension &amp;quot;.png&amp;quot; is changed to &amp;quot;_0.png&amp;quot;, &amp;quot;_25.png&amp;quot;, &amp;quot;_50.png&amp;quot;, &amp;quot;_75.png&amp;quot; or &amp;quot;_100.png&amp;quot; depending on the value of the service variable, linearly scaled from its range of 0:(MaxValue-MinValue) to 0:100. Values round up; 1-25 produces the &amp;quot;_25&amp;quot; image; 26-50 produces the &amp;quot;_50&amp;quot; image, and so on. For images which are not found (for instance, if the web server returns '''404 Not Found''') the default image is used. &lt;br /&gt;
&lt;br /&gt;
Consequently, at least three images, and as many as six, should be present for variable-icon plugins. For example, if '''flashicon''' contains '''&amp;lt;nowiki&amp;gt;http://example.com/image.png&amp;lt;/nowiki&amp;gt;''', the web server must be able to serve: &lt;br /&gt;
&lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;http://example.com/image.png&amp;lt;/nowiki&amp;gt;''' (for when the plugin is loading in the Luup engine) &lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;http://example.com/image_0.png&amp;lt;/nowiki&amp;gt;''' &lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;http://example.com/image_100.png&amp;lt;/nowiki&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
and it should be able to serve (if the variable is a sliding scale): &lt;br /&gt;
&lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;http://example.com/image_25.png&amp;lt;/nowiki&amp;gt;''' &lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;http://example.com/image_50.png&amp;lt;/nowiki&amp;gt;''' &lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;http://example.com/image_75.png&amp;lt;/nowiki&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
== state_icons  ==&lt;br /&gt;
&lt;br /&gt;
From firmware 1.5.401 onwards, you must also tell the Static JSON file which icon files exist, with the '''state_icons''' key at the top level. The corresponding value is a JSON array of strings, the file names which should be displayed in the UI. This key was added to prevent the plethora of 404 errors that would appear in the JavaScript console. &lt;br /&gt;
&lt;br /&gt;
Currently each device can have a maximum of 5 icons. The formula for choosing the icon is this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var status = Variable / (MaxValue - MinValue);&lt;br /&gt;
var val = ceil( status * 4 );&lt;br /&gt;
var icon = baseIconName + &amp;quot;_&amp;quot; + (isNaN( val * 25 ) ? 0 : (val * 25)) + &amp;quot;.png&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
e.g. For the dimmable light, the formula would look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var status = LoadLevelStatus / (100 - 0);&lt;br /&gt;
var val = ceil( status * 4 );&lt;br /&gt;
var icon = &amp;quot;Dimmable_Light&amp;quot; + &amp;quot;_&amp;quot; + (isNaN( val * 25 ) ? 0 : (val * 25)) + &amp;quot;.png&amp;quot;;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The resulting intervals and icons are:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! LoadLevelStatus&lt;br /&gt;
! Icon&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Dimmable_Light_0.png&lt;br /&gt;
|-&lt;br /&gt;
| 1 - 25&lt;br /&gt;
| Dimmable_Light_25.png&lt;br /&gt;
|-&lt;br /&gt;
| 26 - 50&lt;br /&gt;
| Dimmable_Light_50.png&lt;br /&gt;
|-&lt;br /&gt;
| 51 - 75&lt;br /&gt;
| Dimmable_Light_75.png&lt;br /&gt;
|-&lt;br /&gt;
| 76 - 100&lt;br /&gt;
| Dimmable_Light_100.png&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Cheating to get finer-grained variable icons  ==&lt;br /&gt;
&lt;br /&gt;
The JavaScript code that inserts the strings &amp;quot;_0&amp;quot; to &amp;quot;_100&amp;quot; into the image filename does not check that the raw variable value is in the range '''MinValue''' to '''MaxValue'''. By deliberately choosing a '''MaxValue''' less than the highest value that a variable can take, you can make the JavaScript load images with &amp;quot;_125&amp;quot;, &amp;quot;_150&amp;quot;, etc., effectively providing any number of images. '''MinValue''' is similarly handled, though the results are odd with negative variables or MinValue not equal to zero: the string inserted into the filename is &amp;quot;_-25&amp;quot;, &amp;quot;-50&amp;quot;, etc. &lt;br /&gt;
&lt;br /&gt;
That this trick works is probably accidental, and its behaviour probably should not be relied on.&lt;br /&gt;
= UI7 Icons=&lt;br /&gt;
&lt;br /&gt;
To avoid adding extra code to treat plugin icons, all plugin developers should take into consideration the following:&lt;br /&gt;
&lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;&amp;quot;default_icon&amp;quot;&amp;lt;/nowiki&amp;gt;'''&lt;br /&gt;
&amp;quot;'''flashicon'''&amp;quot; is not used any more, instead, UI7 uses &amp;quot;default_icon&amp;quot; as in the above example, which can be either the url from a image uploaded somewhere on the web or a image stored locally in /www/cmh/skins/default/img/devices/device_states/ . Please be sure if you choose the last method to not upload images with names identical to any of the existing device state icon files. Image resolution should be 50px x 50px.&lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;&amp;quot;state_icons&amp;quot;&amp;lt;/nowiki&amp;gt;'''&lt;br /&gt;
If the device created by the plugin has states and we need to display them we will use this object. &amp;quot;img&amp;quot; is the state image, same as above, it's a url to a image uploaded on the web or the name of a image stored locally in skins/default/img/devices/device_sates/. &amp;quot;conditions&amp;quot; is an array of objects from which we build logical expressions and evaluate them to decide what icon to be used for what state. In the example above if in lu_status for a given binary light with &amp;quot;subcategory_num&amp;quot; = 0 ( this means indoor binary light , because we also have devices for outdoor use like the Jasco 45604) we find    service : &amp;quot;urn:upnp-org:serviceId:SwitchPower1&amp;quot;, variable : &amp;quot;Status&amp;quot; and the value from this pair is equal (&amp;quot;operator&amp;quot;) to the &amp;quot;value&amp;quot; (0) in the conditions object then we show the &amp;quot;binary_light_off.png&amp;quot; in the UI (light is off). A good example of usage for &amp;quot;subcategory_num&amp;quot; can be seen in the json definition of the motion sensor D_MotionSensor1.json - which serves as template for a number of types of sensors but I am not sure if this will be the case for plugins(&amp;quot;subcategory_num&amp;quot; is not mandatory). As I said &amp;quot;conditions&amp;quot; is an array of objects,     this means that if we want to display a certain icon based on more than 1 condition we can do so by adding to it more objects defining different conditions.&lt;br /&gt;
&lt;br /&gt;
Example:     &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;default_icon&amp;quot;: &amp;quot;binary_light_default.png&amp;quot;,&lt;br /&gt;
    &amp;quot;state_icons&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;img&amp;quot;: &amp;quot;binary_light_off.png&amp;quot;,&lt;br /&gt;
            &amp;quot;conditions&amp;quot;: [&lt;br /&gt;
                {&lt;br /&gt;
                    &amp;quot;service&amp;quot;: &amp;quot;urn:upnp-org:serviceId:SwitchPower1&amp;quot;,&lt;br /&gt;
                    &amp;quot;variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
                    &amp;quot;operator&amp;quot;: &amp;quot;==&amp;quot;,&lt;br /&gt;
                    &amp;quot;value&amp;quot;: 0,&lt;br /&gt;
                    &amp;quot;subcategory_num&amp;quot;: 0&lt;br /&gt;
                }&lt;br /&gt;
            ]&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;img&amp;quot;: &amp;quot;binary_light_on.png&amp;quot;,&lt;br /&gt;
            &amp;quot;conditions&amp;quot;: [&lt;br /&gt;
                {&lt;br /&gt;
                    &amp;quot;service&amp;quot;: &amp;quot;urn:upnp-org:serviceId:SwitchPower1&amp;quot;,&lt;br /&gt;
                    &amp;quot;variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
                    &amp;quot;operator&amp;quot;: &amp;quot;==&amp;quot;,&lt;br /&gt;
                    &amp;quot;value&amp;quot;: 1,&lt;br /&gt;
                    &amp;quot;subcategory_num&amp;quot;: 0&lt;br /&gt;
                }&lt;br /&gt;
            ]&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_icons</id>
		<title>Luup plugin icons</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_icons"/>
				<updated>2014-11-30T08:00:44Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
&lt;br /&gt;
The [[Luup plugins: Static JSON file|Luup plugin static JSON file]] describes the icon that appears in the dashboard of the UI4 web interface.&lt;br /&gt;
&lt;br /&gt;
=Icon format=&lt;br /&gt;
&lt;br /&gt;
All of Vera's icons are 42x42 pixel PNGs with transparency.  Icons of other sizes will be resized to 42x42.&lt;br /&gt;
&lt;br /&gt;
=Configuration=&lt;br /&gt;
&lt;br /&gt;
The top-level key '''flashicon''' contains a string.  This string is the name of an image file.  The filename is either a relative path or a URL starting with &amp;quot;http&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
==Relative paths==&lt;br /&gt;
&lt;br /&gt;
If the filename is a relative path, it is relative to the directory '''/www/cmh/skins/default''' on the Vera.  If the filename ends in the string &amp;quot;.swf&amp;quot;, this extension is changed to &amp;quot;.png&amp;quot;.  (This is presumably a holdover from UI3, which was flash-based.  In UI4, there is no reason to maintain this ruse, so just use the &amp;quot;.png&amp;quot; extension.)&lt;br /&gt;
&lt;br /&gt;
Files installed in the &amp;quot;icons&amp;quot; directory are:&lt;br /&gt;
* Binary_Light.png&lt;br /&gt;
* Binary_Light_0.png&lt;br /&gt;
* Binary_Light_100.png&lt;br /&gt;
* Dimmable_Light.png&lt;br /&gt;
* Dimmable_Light_0.png&lt;br /&gt;
* Dimmable_Light_100.png&lt;br /&gt;
* Dimmable_Light_25.png&lt;br /&gt;
* Dimmable_Light_50.png&lt;br /&gt;
* Dimmable_Light_75.png&lt;br /&gt;
* Door_Lock.png&lt;br /&gt;
* Door_Lock_0.png&lt;br /&gt;
* Door_Lock_100.png&lt;br /&gt;
* Generic_IO.png&lt;br /&gt;
* Humidity_Sensor.png&lt;br /&gt;
* IR_Transmitter.png&lt;br /&gt;
* Insteon.png&lt;br /&gt;
* Ip_Camera.png&lt;br /&gt;
* Light_Sensor.png&lt;br /&gt;
* Motion_Sensor.png&lt;br /&gt;
* Motion_Sensor_0.png&lt;br /&gt;
* Motion_Sensor_100.png&lt;br /&gt;
* Power_Meter.png&lt;br /&gt;
* Scenes.png&lt;br /&gt;
* Temperature_Sensor.png&lt;br /&gt;
* Thermostat.png&lt;br /&gt;
* USB_UIRT.png&lt;br /&gt;
* Window_Covering.png&lt;br /&gt;
* Zwave.png&lt;br /&gt;
* advanced.png&lt;br /&gt;
* default.png&lt;br /&gt;
* default_device.png&lt;br /&gt;
* default_panel.png&lt;br /&gt;
* default_plugins.png&lt;br /&gt;
* device.png&lt;br /&gt;
* devices.png&lt;br /&gt;
* energy.png&lt;br /&gt;
* findvera.png&lt;br /&gt;
* generic_icon.png&lt;br /&gt;
* generic_sensor.png&lt;br /&gt;
* intro.png&lt;br /&gt;
* location.png&lt;br /&gt;
* music_audio.png&lt;br /&gt;
* plugins.png&lt;br /&gt;
* users.png&lt;br /&gt;
&lt;br /&gt;
== URLs ==&lt;br /&gt;
&lt;br /&gt;
If the filename is a URL, it refers to a remote file on a web server.&lt;br /&gt;
&lt;br /&gt;
=Constant icons=&lt;br /&gt;
&lt;br /&gt;
For an icon that remains the same all the time, the '''flashicon''' key is all you need to provide.  Ensure that the static JSON file does not contain a '''DisplayStatus''' key, and you are done.&lt;br /&gt;
&lt;br /&gt;
= Variable icons  =&lt;br /&gt;
&lt;br /&gt;
For an icon that varies based on the value of a variable, place a '''DisplayStatus''' key at the top level of the static JSON file. Its value is a JSON object (associative array) with four keys: &lt;br /&gt;
&lt;br /&gt;
;Service &lt;br /&gt;
:The Service ID of the variable that controls the icon. This can be found in the matching device XML file, under the '''serviceId''' element. &lt;br /&gt;
;Variable &lt;br /&gt;
:The variable name of the variable that controls the icon. &lt;br /&gt;
;MinValue &lt;br /&gt;
:The value that this variable has when it is considered &amp;quot;off&amp;quot; or at &amp;quot;0%&amp;quot;. This can even be a string, if the variable is a string that takes two values. &lt;br /&gt;
;MaxValue &lt;br /&gt;
:The value that this variable has when it is considered &amp;quot;on&amp;quot; or at &amp;quot;100%&amp;quot;. This can even be a string, if the variable is a string that takes two values.&lt;br /&gt;
&lt;br /&gt;
Example for a binary switch: &lt;br /&gt;
&amp;lt;pre&amp;gt;  &amp;quot;DisplayStatus&amp;quot;: {&lt;br /&gt;
        &amp;quot;Service&amp;quot;: &amp;quot;urn:upnp-org:serviceId:SwitchPower1&amp;quot;,&lt;br /&gt;
        &amp;quot;Variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
        &amp;quot;MinValue&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
        &amp;quot;MaxValue&amp;quot;: &amp;quot;1&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
Example for a variable which takes on a range of values. &lt;br /&gt;
&amp;lt;pre&amp;gt;  &amp;quot;DisplayStatus&amp;quot;: {&lt;br /&gt;
        &amp;quot;Service&amp;quot;: &amp;quot;urn:upnp-org:serviceId:Dimming1&amp;quot;,&lt;br /&gt;
        &amp;quot;Variable&amp;quot;: &amp;quot;LoadLevelStatus&amp;quot;,&lt;br /&gt;
        &amp;quot;MinValue&amp;quot;: &amp;quot;0&amp;quot;,&lt;br /&gt;
        &amp;quot;MaxValue&amp;quot;: &amp;quot;100&amp;quot; &lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
The filename in '''flashicon''' undergoes a special transformation for variable icons. The extension &amp;quot;.png&amp;quot; is changed to &amp;quot;_0.png&amp;quot;, &amp;quot;_25.png&amp;quot;, &amp;quot;_50.png&amp;quot;, &amp;quot;_75.png&amp;quot; or &amp;quot;_100.png&amp;quot; depending on the value of the service variable, linearly scaled from its range of 0:(MaxValue-MinValue) to 0:100. Values round up; 1-25 produces the &amp;quot;_25&amp;quot; image; 26-50 produces the &amp;quot;_50&amp;quot; image, and so on. For images which are not found (for instance, if the web server returns '''404 Not Found''') the default image is used. &lt;br /&gt;
&lt;br /&gt;
Consequently, at least three images, and as many as six, should be present for variable-icon plugins. For example, if '''flashicon''' contains '''&amp;lt;nowiki&amp;gt;http://example.com/image.png&amp;lt;/nowiki&amp;gt;''', the web server must be able to serve: &lt;br /&gt;
&lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;http://example.com/image.png&amp;lt;/nowiki&amp;gt;''' (for when the plugin is loading in the Luup engine) &lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;http://example.com/image_0.png&amp;lt;/nowiki&amp;gt;''' &lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;http://example.com/image_100.png&amp;lt;/nowiki&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
and it should be able to serve (if the variable is a sliding scale): &lt;br /&gt;
&lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;http://example.com/image_25.png&amp;lt;/nowiki&amp;gt;''' &lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;http://example.com/image_50.png&amp;lt;/nowiki&amp;gt;''' &lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;http://example.com/image_75.png&amp;lt;/nowiki&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
== state_icons  ==&lt;br /&gt;
&lt;br /&gt;
From firmware 1.5.401 onwards, you must also tell the Static JSON file which icon files exist, with the '''state_icons''' key at the top level. The corresponding value is a JSON array of strings, the file names which should be displayed in the UI. This key was added to prevent the plethora of 404 errors that would appear in the JavaScript console. &lt;br /&gt;
&lt;br /&gt;
Currently each device can have a maximum of 5 icons. The formula for choosing the icon is this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var status = Variable / (MaxValue - MinValue);&lt;br /&gt;
var val = ceil( status * 4 );&lt;br /&gt;
var icon = baseIconName + &amp;quot;_&amp;quot; + (isNaN( val * 25 ) ? 0 : (val * 25)) + &amp;quot;.png&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
e.g. For the dimmable light, the formula would look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var status = LoadLevelStatus / (100 - 0);&lt;br /&gt;
var val = ceil( status * 4 );&lt;br /&gt;
var icon = &amp;quot;Dimmable_Light&amp;quot; + &amp;quot;_&amp;quot; + (isNaN( val * 25 ) ? 0 : (val * 25)) + &amp;quot;.png&amp;quot;;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The resulting intervals and icons are:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! LoadLevelStatus&lt;br /&gt;
! Icon&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Dimmable_Light_0.png&lt;br /&gt;
|-&lt;br /&gt;
| 1 - 25&lt;br /&gt;
| Dimmable_Light_25.png&lt;br /&gt;
|-&lt;br /&gt;
| 26 - 50&lt;br /&gt;
| Dimmable_Light_50.png&lt;br /&gt;
|-&lt;br /&gt;
| 51 - 75&lt;br /&gt;
| Dimmable_Light_75.png&lt;br /&gt;
|-&lt;br /&gt;
| 76 - 100&lt;br /&gt;
| Dimmable_Light_100.png&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Cheating to get finer-grained variable icons  ==&lt;br /&gt;
&lt;br /&gt;
The JavaScript code that inserts the strings &amp;quot;_0&amp;quot; to &amp;quot;_100&amp;quot; into the image filename does not check that the raw variable value is in the range '''MinValue''' to '''MaxValue'''. By deliberately choosing a '''MaxValue''' less than the highest value that a variable can take, you can make the JavaScript load images with &amp;quot;_125&amp;quot;, &amp;quot;_150&amp;quot;, etc., effectively providing any number of images. '''MinValue''' is similarly handled, though the results are odd with negative variables or MinValue not equal to zero: the string inserted into the filename is &amp;quot;_-25&amp;quot;, &amp;quot;-50&amp;quot;, etc. &lt;br /&gt;
&lt;br /&gt;
That this trick works is probably accidental, and its behaviour probably should not be relied on.&lt;br /&gt;
= UI7 Icons=&lt;br /&gt;
&lt;br /&gt;
To avoid adding extra code to treat plugin icons, all plugin developers should take into consideration the following:&lt;br /&gt;
&lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;&amp;quot;default_icon&amp;quot;&amp;lt;/nowiki&amp;gt;'''&lt;br /&gt;
&amp;quot;'''flashicon'''&amp;quot; is not used any more, instead, UI7 uses &amp;quot;default_icon&amp;quot; as in the above example, which can be either the url from a image uploaded somewhere on the web or a image stored locally in /www/cmh/skins/default/img/devices/device_states/ . Please be sure if you choose the last method to not upload images with names identical to any of the existing device state icon files. Image resolution should be 50px x 50px.&lt;br /&gt;
*'''&amp;lt;nowiki&amp;gt;&amp;quot;state_icons&amp;quot;&amp;lt;/nowiki&amp;gt;'''&lt;br /&gt;
If the device created by the plugin has states and we need to display them we will use this object. &amp;quot;img&amp;quot; is the state image, same as above, it's a url to a image uploaded on the web or the name of a image stored locally in skins/default/img/devices/device_sates/. &amp;quot;conditions&amp;quot; is an array of objects from which we build logical expressions and evaluate them to decide what icon to be used for what state. In the example above if in lu_status for a given binary light with &amp;quot;subcategory_num&amp;quot; = 0 ( this means indoor binary light , because we also have devices for outdoor use like the Jasco 45604) we find    service : &amp;quot;urn:upnp-org:serviceId:SwitchPower1&amp;quot;, variable : &amp;quot;Status&amp;quot; and the value from this pair is equal (&amp;quot;operator&amp;quot;) to the &amp;quot;value&amp;quot; (0) in the conditions object then we show the &amp;quot;binary_light_off.png&amp;quot; in the UI (light is off). A good example of usage for &amp;quot;subcategory_num&amp;quot; can be seen in the json definition of the motion sensor D_MotionSensor1.json - which serves as template for a number of types of sensors but I am not sure if this will be the case for plugins(&amp;quot;subcategory_num&amp;quot; is not mandatory). As I said &amp;quot;conditions&amp;quot; is an array of objects,     this means that if we want to display a certain icon based on more than 1 condition we can do so by adding to it more objects defining different conditions.&lt;br /&gt;
&lt;br /&gt;
Example:     &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;default_icon&amp;quot;: &amp;quot;binary_light_default.png&amp;quot;,&lt;br /&gt;
    &amp;quot;state_icons&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;img&amp;quot;: &amp;quot;binary_light_off.png&amp;quot;,&lt;br /&gt;
            &amp;quot;conditions&amp;quot;: [&lt;br /&gt;
                {&lt;br /&gt;
                    &amp;quot;service&amp;quot;: &amp;quot;urn:upnp-org:serviceId:SwitchPower1&amp;quot;,&lt;br /&gt;
                    &amp;quot;variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
                    &amp;quot;operator&amp;quot;: &amp;quot;==&amp;quot;,&lt;br /&gt;
                    &amp;quot;value&amp;quot;: 0,&lt;br /&gt;
                    &amp;quot;subcategory_num&amp;quot;: 0&lt;br /&gt;
                }&lt;br /&gt;
            ]&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;img&amp;quot;: &amp;quot;binary_light_on.png&amp;quot;,&lt;br /&gt;
            &amp;quot;conditions&amp;quot;: [&lt;br /&gt;
                {&lt;br /&gt;
                    &amp;quot;service&amp;quot;: &amp;quot;urn:upnp-org:serviceId:SwitchPower1&amp;quot;,&lt;br /&gt;
                    &amp;quot;variable&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
                    &amp;quot;operator&amp;quot;: &amp;quot;==&amp;quot;,&lt;br /&gt;
                    &amp;quot;value&amp;quot;: 1,&lt;br /&gt;
                    &amp;quot;subcategory_num&amp;quot;: 0&lt;br /&gt;
                }&lt;br /&gt;
            ]&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_Lua_extensions</id>
		<title>Luup Lua extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_Lua_extensions"/>
				<updated>2013-07-20T14:07:05Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: /* function: register_handler */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In addition to the [[http://lua.org Lua]] commands described in the [[http://www.lua.org/manual/5.1/ Lua reference manual]], you can also reference in your Lua code variables and functions from modules which the Luup engine provides as follows: &lt;br /&gt;
&lt;br /&gt;
== Module: luup  ==&lt;br /&gt;
&lt;br /&gt;
These are general purpose functions and variables. Call them by using the luup. module, such as:&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;luup.log('Now running version: ' .. luup.version)&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== variable: device  ===&lt;br /&gt;
&lt;br /&gt;
The ID of this device instance, if it's running as part of a device &lt;br /&gt;
&lt;br /&gt;
=== variable: version, version_branch, version_major, version_minor  ===&lt;br /&gt;
&lt;br /&gt;
''version'' contains the version of the luup engine, such as &amp;quot;1.0.843&amp;quot;, as a string. The version consists of 3 numbers separated by a period: the branch, the major version, and the minor version. To make it easier to compare against a minimum acceptable version, ''version_branch, version_major and version_minor'' return each component as a number. You can put a comparison in the startup function in your Luup plugin and return false if the minimum version isn't met. The user will then see that the Luup device's startup check failed:&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;if( version_branch ~= 1 or version_major ~= 0 or version_minor &amp;lt; 843 ) then&lt;br /&gt;
    luup.log(&amp;quot;I need version 1.0.843 minimum to run&amp;quot;)&lt;br /&gt;
    return false&lt;br /&gt;
end&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== variable: longitude  ===&lt;br /&gt;
&lt;br /&gt;
Contains the longitude as a number, as found on the location tab in the setup UI. &lt;br /&gt;
&lt;br /&gt;
=== variable: latitude  ===&lt;br /&gt;
&lt;br /&gt;
Contains the latitude as a number, as found on the location tab in the setup UI. &lt;br /&gt;
&lt;br /&gt;
=== variable: timezone  ===&lt;br /&gt;
&lt;br /&gt;
Contains the timezone as a number of hours offset from UTC, as found on the location tab in the setup UI.  It accounts for DST, so, for example, Pacific Standard time will be -8 or -9 depending on DST.&lt;br /&gt;
&lt;br /&gt;
'''Note:''' Contains 0 for MiOS &amp;lt; 1.5.250 (Vera V2) / &amp;lt; 1.5.249 (Vera V3).&lt;br /&gt;
&lt;br /&gt;
=== variable: city  ===&lt;br /&gt;
&lt;br /&gt;
Contains the city as a string, as found on the location tab in the setup UI. &lt;br /&gt;
&lt;br /&gt;
=== variable: devices  ===&lt;br /&gt;
&lt;br /&gt;
Contains all the devices in the system as a table indexed by the device number.&lt;br /&gt;
&lt;br /&gt;
The members are:&lt;br /&gt;
* '''room_num''': (number) This is the number of the room the device is in. &lt;br /&gt;
* '''device_type''': (string) This is a string representing the type of the device.&lt;br /&gt;
* '''category_num''': (number) This is a category for the device.  See: [[Luup_Device_Categories]] for a list. &lt;br /&gt;
* '''subcategory_num''': (number) This is a sub category for the device.&lt;br /&gt;
* '''device_num_parent''': (number) This is the number of the parent device. See: [[Lua Device Structure]] for details. &lt;br /&gt;
* '''ip''': (string) If this device is IP based, this is the IP address. &lt;br /&gt;
* '''mac''': (string) If this device is IP based, this is the MAC address. &lt;br /&gt;
* '''user''': (string) If this device is IP based and requires http authentication, this is the username&lt;br /&gt;
* '''pass''': (string) If this device is IP based and requires http authentication, this is the password&lt;br /&gt;
* '''id''': (string) If this device has an internal ID that is specific to the device, it is contained here. For example, for Z-Wave devices this is the Node ID, and for Insteon device it is the Insteon ID. &lt;br /&gt;
* '''embedded''': (boolean) If this device is embedded, it means that it doesn't have its own room or exist as a separate device. It should be considered part of its parent. Like a 3-in-1 sensor is a device with 3 embedded child devices. &lt;br /&gt;
* '''hidden''': (boolean) If true the user checked the 'hidden' box and doesn't want to see the device on the dashboard. &lt;br /&gt;
* '''invisible''': (boolean) If true the device is 'for internal use only' and shouldn't be presented to the user. &lt;br /&gt;
* '''description''': (string) This is the text description for the device as supplied by the user in the web UI. &lt;br /&gt;
* '''udn''': (string) This is the UDN for the UPnP device.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Example to log device #5's IP address and its internal ID:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;luup.log('Device #5 ip: ' .. luup.devices[5].ip .. ' id: ' .. luup.devices[5].id)&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This code will log all the attributes from all the devices:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;for k, v in pairs(luup.devices) do&lt;br /&gt;
    for k2, v2 in pairs(v) do&lt;br /&gt;
        luup.log(&amp;quot;Device #&amp;quot; .. k .. &amp;quot;:&amp;quot; .. k2 .. &amp;quot;=&amp;quot; .. tostring(v2))&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return true&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== variable: rooms  ===&lt;br /&gt;
&lt;br /&gt;
Contains all the rooms as a table of strings indexed by the room number. Example:&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;luup.log('Room #1 is called: ' .. luup.rooms[1])&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== variable: scenes  ===&lt;br /&gt;
&lt;br /&gt;
Contains all the scenes in the system as a table indexed by the scene number. The members are: room_num (number), description(string), hidden(boolean)&lt;br /&gt;
&lt;br /&gt;
=== variable: remotes  ===&lt;br /&gt;
&lt;br /&gt;
Contains all the remotes in the system as a table indexed by the remote id. The members are: remote_file (string), room_num (number), description(string) &lt;br /&gt;
&lt;br /&gt;
=== variable: event_server ===&lt;br /&gt;
&lt;br /&gt;
type: string&lt;br /&gt;
&lt;br /&gt;
Contains the notification/event server. On UI5 it can be either ''cms1.mios.com'' or ''cms2.mios.com''.&lt;br /&gt;
&lt;br /&gt;
=== variable: ra_server ===&lt;br /&gt;
&lt;br /&gt;
type: string&lt;br /&gt;
&lt;br /&gt;
Contains the remote access server. Can be either ''fwd1.mios.com'' or ''fwd2.mios.com''.&lt;br /&gt;
&lt;br /&gt;
=== variable: pk_accesspoint ===&lt;br /&gt;
&lt;br /&gt;
type: number&lt;br /&gt;
&lt;br /&gt;
Contains the serial number of this Vera.&lt;br /&gt;
&lt;br /&gt;
=== variable: hw_key ===&lt;br /&gt;
&lt;br /&gt;
type: string&lt;br /&gt;
&lt;br /&gt;
Contains the Vera hardware key.&lt;br /&gt;
&lt;br /&gt;
=== function: log  ===&lt;br /&gt;
&lt;br /&gt;
parameters: what_to_log (string), log_level (optional, number) &lt;br /&gt;
&lt;br /&gt;
return: nothing &lt;br /&gt;
&lt;br /&gt;
Writes what_to_log to the log, /var/log/cmh/LuaUPnP.log, with the given log_level, which is 50 by default. See: [[Luup Loglevels]] &lt;br /&gt;
&lt;br /&gt;
=== function: task  ===&lt;br /&gt;
&lt;br /&gt;
parameters: message (string), status (number), description (string), handle (number) &lt;br /&gt;
&lt;br /&gt;
return: handle (number) &lt;br /&gt;
&lt;br /&gt;
When the Luup engine is starting status messages are displayed for the various modules as they're initialized. Normally each device, including Luup devices, automatically log their status and the user is shown an error if the device doesn't start, such as if the 'startup' function returns an error. &lt;br /&gt;
&lt;br /&gt;
If you have other startup sequences which you want the user to see to know that startup hasn't finished yet, call this function passing in a handle of -1 for the first call. The status should be: 1=Busy, 2=Error, 4=Successful. Message is the current state, such as 'downloading', and description describes the module, like 'Smartphone UI'. After the first call, store the handle and pass it on future calls to update the status rather than add a new one. &lt;br /&gt;
&lt;br /&gt;
=== function: call_delay  ===&lt;br /&gt;
&lt;br /&gt;
parameters: function_name (string), seconds (number), data (string), thread (bool) &lt;br /&gt;
&lt;br /&gt;
returns: result (number) &lt;br /&gt;
&lt;br /&gt;
The function ''function_name'' (the first parameter), which must be passed as a string, will be called in ''seconds'' seconds (the second parameter), and will be passed the string ''data''. The function returns 0 if successful. See: [[Luup Declarations#timed_function_callback]] for the names and syntax of the parameters that will be passed to function_name. function_name will only be called once and you must call call_delay again if you want it called again, or use call_timer instead. &lt;br /&gt;
&lt;br /&gt;
If thread is specified and is true or 1, the call back will be made in it's own thread and can block if needed. Normally it is called by a worker thread and is expected to return immediately.&lt;br /&gt;
&lt;br /&gt;
As of December 19, 2011, for all builds after 1.5.237, the 'thread' will be ignored.  Each Lua state has its own worker thread now, so all calls to call_delay and call_timer will occur in a separate thread.&lt;br /&gt;
&lt;br /&gt;
=== function: call_timer  ===&lt;br /&gt;
&lt;br /&gt;
parameters: function_name (string), type (number), time (string), days (string), data (string) &lt;br /&gt;
&lt;br /&gt;
returns: result (number) &lt;br /&gt;
&lt;br /&gt;
The function 'function_name', which must be passed as a string, will be called when the timer is triggered, and will be passed the string 'data'. The function returns 0 if successful. See: [[Luup Declarations#timed_function_callback]] for the names and syntax of the parameters that will be passed to function_name. &lt;br /&gt;
&lt;br /&gt;
Type is 1=Interval timer, 2=Day of week timer, 3=Day of month timer, 4=Absolute timer. For an interval timer, days is not used, and Time should be a number of seconds, minutes, or hours using an optional 'h' or 'm' suffix. Example: 30=call in 30 seconds, 5m=call in 5 minutes, 2h=call in 2 hours. For a day of week timer, Days is a comma separated list with the days of the week where 1=Monday and 7=Sunday. Time is the time of day in hh:mm:ss format. Time can also include an 'r' at the end for Sunrise or a 't' for Sunset and the time is relative to sunrise/sunset. For example: Days=&amp;quot;3,5&amp;quot; Time=&amp;quot;20:30:00&amp;quot; means your function will be called on the next Wed or Fri at 8:30pm. Days=&amp;quot;1,7&amp;quot; Time=&amp;quot;-3:00:00r&amp;quot; means your function will be called on the next Monday or Sunday 3 hours before sunrise. Day of month works the same way except Days is a comma separated list of days of the month, such as &amp;quot;15,20,30&amp;quot;. For an absolute timer, Days is not used, and Time should be in the format: &amp;quot;yyyy-mm-dd hh:mm:ss&amp;quot; &lt;br /&gt;
&lt;br /&gt;
Data can be a string passed back to the function. The function should be declared so it takes a single argument, which is this data.&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;function refreshCache(stuff)&lt;br /&gt;
    ....&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function startup()&lt;br /&gt;
    --&lt;br /&gt;
    -- Setup an interval-based timer to call refreshCache after 30 minutes.&lt;br /&gt;
    -- Note that if you want it to &amp;quot;recur&amp;quot; then you need to call this function again&lt;br /&gt;
    -- at the end of the refreshCache() implementation.&lt;br /&gt;
    --&lt;br /&gt;
    luup.call_timer(&amp;quot;refreshCache&amp;quot;, 1, &amp;quot;30m&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;SomeStuff&amp;quot;)&lt;br /&gt;
end&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== function: is_ready  ===&lt;br /&gt;
&lt;br /&gt;
parameters: device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: ready (boolean) &lt;br /&gt;
&lt;br /&gt;
version: UI5 and above&lt;br /&gt;
&lt;br /&gt;
Checks whether a device has successfully completed it's startup sequence. If so, &amp;lt;tt&amp;gt;is_ready&amp;lt;/tt&amp;gt; returns &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;. If your device shouldn't process incoming data until the startup sequence is finished, you may want to add a condition to the &amp;lt;tt&amp;gt;&amp;lt;incoming&amp;gt; block&amp;lt;/tt&amp;gt; that only processes data if &amp;lt;tt&amp;gt;is_ready(lul_device)&amp;lt;/tt&amp;gt; is &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;device&amp;lt;/tt&amp;gt; parameter, if it's a string, is interpreted as a udn.  If it's a number, it's interpreted as a device number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;&amp;lt;incoming&amp;gt;&lt;br /&gt;
    if (luup.is_ready(lul_device) == false) then&lt;br /&gt;
        return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    doSomething(lul_device)&lt;br /&gt;
&amp;lt;/incoming&amp;gt;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: call_action  ===&lt;br /&gt;
&lt;br /&gt;
parameters: service (string), action (string), arguments (table), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: error (number), error_msg (string), job (number), arguments (table) &lt;br /&gt;
&lt;br /&gt;
Invokes the UPnP service + action, passing in the arguments (table of string-&amp;amp;gt;string pairs) to the device, which, if it's a string, is interpreted as a udn, and if it's a number, is the device number. If the invocation could not be made, only error will be returned with a value of -1. Otherwise, all 4 values are returned. error is 0 if the UPnP device reported the action was successful. arguments is a table of string-&amp;amp;gt;string pairs with the return arguments from the action. If the action is handled asynchronously by a Luup job, then the job number will be returned as a positive integer. &lt;br /&gt;
&lt;br /&gt;
Example to dim device #5 to 50%:&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;local lul_arguments = {}&lt;br /&gt;
lul_arguments[&amp;quot;newLoadlevelTarget&amp;quot;] = 50&lt;br /&gt;
lul_resultcode, lul_resultstring, lul_job, lul_returnarguments = luup.call_action(&amp;quot;urn:upnp-org:serviceId:Dimming1&amp;quot;,&lt;br /&gt;
                                                                                  &amp;quot;SetLoadLevelTarget&amp;quot;, lul_arguments,&lt;br /&gt;
                                                                                  5)&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: variable_set  ===&lt;br /&gt;
&lt;br /&gt;
parameters: service (string), variable (string), value (string), device (string or number), [startup (bool)] &lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
The UPnP service+variable will be set to value for device, which if it's a string, is interpreted as a udn, and if it's a number, as a device id. If there are events or notifications tied to the variable they will be fired. &lt;br /&gt;
&lt;br /&gt;
Optionally, you can add an argument 'startup'. If startup is true, this change will be considered a startup value, and if the variable is set to it's existing value, events and notifications will ''not'' be fired. &lt;br /&gt;
&lt;br /&gt;
=== function: variable_get  ===&lt;br /&gt;
&lt;br /&gt;
parameters: service (string), variable (string), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: value (string) and time (number) or none (note: none means nothing at all. It does not mean 'nil')&lt;br /&gt;
&lt;br /&gt;
Returns the value of the UPnP service+variable and the time when the variable was last modified, as a unix time stamp (number of seconds since 1/1/1970). If the service+variable or device does not exist, it returns nothing. You can assign just the value to a variable, as follows:&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;local value = luup.variable_get(&amp;quot;urn:upnp-org:serviceId:Dimming1&amp;quot;, &amp;quot;LoadLevelTarget&amp;quot;, 5)&lt;br /&gt;
luup.log(&amp;quot;Dim level for device #5 is: &amp;quot; .. value)&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: attr_set  ===&lt;br /&gt;
&lt;br /&gt;
parameters: attribute (string), value(string), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: none &lt;br /&gt;
&lt;br /&gt;
Sets the top level attribute for the device to value. Examples of attributes are 'mac', 'name', 'id', etc.&lt;br /&gt;
&lt;br /&gt;
=== function: attr_get  ===&lt;br /&gt;
&lt;br /&gt;
parameters: attribute (string), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: string or none  (note: none means nothing at all. It does not mean 'nil')&lt;br /&gt;
&lt;br /&gt;
Gets the top level attribute for the device.  Examples of attributes are 'mac', 'name', 'id', etc.  If the attribute doesn't exist, it returns nothing.  If nothing is passed in for device, it gets the top level attribute from the master userdata, like firmware_version.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;-- this code logs nil if theDeviceNumber is invalid&lt;br /&gt;
local theName = luup.attr_get ('name', theDeviceNumber)&lt;br /&gt;
luup.log(theName)&lt;br /&gt;
return true&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;-- this code fails if theDeviceNumber is invalid&lt;br /&gt;
luup.log(luup.attr_get ('name', theDeviceNumber))&lt;br /&gt;
return true&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: register_handler  ===&lt;br /&gt;
&lt;br /&gt;
parameters: function_name (string), request_name (string) &lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
When a certain URL is requested from a web browser or other HTTP get, function_name will be called and whatever string and content_type it returns will be returned. &lt;br /&gt;
&lt;br /&gt;
See the Smartphone Web Interface plugin as an example:&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;luup.register_handler(&amp;quot;lug_WapRequest&amp;quot;,&amp;quot;wap&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function lug_WapRequest (lul_request, lul_parameters, lul_outputformat)&lt;br /&gt;
    local lul_html = &amp;quot;&amp;lt;head&amp;gt;\n&amp;quot; ..&lt;br /&gt;
                     &amp;quot;&amp;lt;title&amp;gt;Main&amp;lt;/title&amp;gt;\n&amp;quot; ..&lt;br /&gt;
                     &amp;quot;&amp;lt;/head&amp;gt;\n&amp;quot; ..&lt;br /&gt;
                     &amp;quot;&amp;lt;body&amp;gt;\n&amp;quot; ..&lt;br /&gt;
                     &amp;quot;Choose a room:&amp;lt;br/&amp;gt;\n&amp;quot;&lt;br /&gt;
    local lul_content_type = &amp;quot;text/html&amp;quot;&lt;br /&gt;
    return lul_html, lul_content_type&lt;br /&gt;
end&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The request is made with the URL: data_request?id=lr_[the registered name] on port 49451. So: http://192.168.1.1:49451/data_request?id=lr_wap will return the web page defined in the function lug_WapRequest in the example above.&lt;br /&gt;
&lt;br /&gt;
=== function: variable_watch  ===&lt;br /&gt;
&lt;br /&gt;
parameters: function_name (string), service (string), variable (string or nil), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
Whenever the UPnP variable is changed for the specified device, which if a string is interpreted as a UDN and if a number as a device ID, ''function_name'' will be called. See [[Luup Declarations#watch_callback]] for the values that will be passed to ''function_name''. If variable is nil, ''function_name'' will be called whenever any variable in the service is changed. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: devices_by_service  ===&lt;br /&gt;
&lt;br /&gt;
parameters: &lt;br /&gt;
&lt;br /&gt;
returns: &lt;br /&gt;
&lt;br /&gt;
=== function: device_supports_service  ===&lt;br /&gt;
&lt;br /&gt;
parameters: service ID (string), device (string or number)&lt;br /&gt;
&lt;br /&gt;
returns: ''true'' if the device supports the service, ''false'' otherwise&lt;br /&gt;
&lt;br /&gt;
A device supports a service if there is at least a command or state variable defined for that device using that service. Setting UPnP variables is unrestricted and free form, and the engine doesn't really know if a device actually uses it or does anything with it. So this function isn't really definitive.&lt;br /&gt;
&lt;br /&gt;
=== function: set_failure  ===&lt;br /&gt;
&lt;br /&gt;
parameters: value (boolean), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: &lt;br /&gt;
&lt;br /&gt;
Luup maintains a 'failure' flag for every device to indicate if it is not functioning. You can set the flag to true if the device is failing. If device is a string it is interpreted as a udn, if it's a number, as a device id. The lu_status URL will show for the device: &amp;lt;tooltip display=&amp;quot;1&amp;quot; tag2=&amp;quot;Lua Failure&amp;quot;/&amp;gt; and Lua Failure is shown in red in UI5 for the device.&lt;br /&gt;
&lt;br /&gt;
=== function: is_night  ===&lt;br /&gt;
&lt;br /&gt;
parameters: none &lt;br /&gt;
&lt;br /&gt;
returns: ''true'' if it's past sunset and before sunrise, ''false'' otherwise.&lt;br /&gt;
&lt;br /&gt;
=== function: sleep  ===&lt;br /&gt;
&lt;br /&gt;
parameters: number of milliseconds &lt;br /&gt;
&lt;br /&gt;
returns: none &lt;br /&gt;
&lt;br /&gt;
Sleeps a certain number of milliseconds&lt;br /&gt;
&lt;br /&gt;
=== function: sunset / sunrise ===&lt;br /&gt;
&lt;br /&gt;
parameters: none&lt;br /&gt;
&lt;br /&gt;
returns: The next sunset / sunrise in a Unix timestamp (i.e. the number of seconds since 1/1/1970 in UTC time). You can do a diff with &amp;lt;tt&amp;gt;os.time&amp;lt;/tt&amp;gt; to see how long it will be for the next event.  &amp;lt;tt&amp;gt;luup.sunset-os.time&amp;lt;/tt&amp;gt; is the number of seconds before the next sunset. Be sure the location and timezone are properly set or the sunset/sunrise will be wrong.&lt;br /&gt;
&lt;br /&gt;
required firmware: 1.5.353&lt;br /&gt;
&lt;br /&gt;
== Module: luup.inet  ==&lt;br /&gt;
&lt;br /&gt;
=== function: wget  ===&lt;br /&gt;
&lt;br /&gt;
parameters: URL (String), Timeout (Number), Username (String), Password (String) &lt;br /&gt;
&lt;br /&gt;
returns httpStatusCode (Number), content (String) &lt;br /&gt;
&lt;br /&gt;
This reads the URL and returns 2 variables: the first is a numeric error code which is 0 if successful, and the second is a string containing the contents of the page. If '''Timeout''' is specified, the function will timeout after that many seconds. The default value for '''Timeout''' is 5 seconds. If '''Username''' and '''Password''' are specified, they will be used for HTTP Basic Authentication. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Module: luup.chdev  ==&lt;br /&gt;
&lt;br /&gt;
Contains functions for a parent to synchronize its child devices. Whenever a device has multiple end-points, the devices are represented in a parent/child fashion where the parent device is responsible for reporting what child devices it has and giving each one a unique id. For example in the sample [[Luup Somfy Walkthrough]] there is a parent device, which is the interface module that controls up to 16 blinds, and up to 16 child devices, one for each blind. As shown in that sample, the parent calls start, then enumerates each child device with append, and finally calls sync. You will need to pass the same value for device to append and sync that you passed to start. &lt;br /&gt;
&lt;br /&gt;
=== function: start  ===&lt;br /&gt;
&lt;br /&gt;
parameters: device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: ptr (binary object) &lt;br /&gt;
&lt;br /&gt;
Tells Luup you will start enumerating the children of device. If device is a string it is interpreted as a udn, if it's a number, as a device id. The return value is a binary object which you cannot do anything with in Lua, but you do pass it to the append and sync functions. &lt;br /&gt;
&lt;br /&gt;
=== function: append  ===&lt;br /&gt;
&lt;br /&gt;
parameters: device (string or number), ptr (binary object), id (string), description (string), device_type (string), device_filename (string), implementation_filename (string), parameters (string), embedded (boolean) [, invisible (boolean)]&lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
Adds one child to &amp;lt;tt&amp;gt;device&amp;lt;/tt&amp;gt;. If &amp;lt;tt&amp;gt;device&amp;lt;/tt&amp;gt; is a string it is interpreted as a udn, if it's a number, as a device id. &lt;br /&gt;
&lt;br /&gt;
Pass in the &amp;lt;tt&amp;gt;ptr&amp;lt;/tt&amp;gt; which you received from the &amp;lt;tt&amp;gt;luup.chdev.start&amp;lt;/tt&amp;gt; call. Give each child a unique id so you can keep track of which is which. You can optionally provide a &amp;lt;tt&amp;gt;description&amp;lt;/tt&amp;gt; which the user sees in the user interface.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;device_type&amp;lt;/tt&amp;gt; is the UPnP device type, such as &amp;lt;tt&amp;gt;urn:schemas-upnp-org:device:BinaryLight:1&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;tt&amp;gt;device_filename&amp;lt;/tt&amp;gt; is specified, that is the name of the&amp;amp;nbsp;XML file with the UPnP device specification. The deviceType from the filename will override any &amp;lt;tt&amp;gt;device_type&amp;lt;/tt&amp;gt; you set manually. If the device_file contains the implementation file for this child device you do not need to specify it in &amp;lt;tt&amp;gt;implementation_filename&amp;lt;/tt&amp;gt;. Otherwise, if there is a Luup implementation for this child device and it's not being handled by the parent device, you can specify it in &amp;lt;tt&amp;gt;implementation_filename&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;tt&amp;gt;embedded&amp;lt;/tt&amp;gt; is true, the 'embedded' flag is set for the device which generally means that the parent and all the children will be displayed as one compound device, or group, rather than as separate devices which you can put in their own rooms. &lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;parameters&amp;lt;/tt&amp;gt; are UPnP service,variables you want set when the device is created. You can specify multiple variables by separating them with a line feed (\n) and use a &amp;lt;tt&amp;gt;,&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;=&amp;lt;/tt&amp;gt; to separate service, variable and value, like this: &amp;lt;tt&amp;gt;service,variable=value\nservice&amp;lt;/tt&amp;gt;...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
luup.chdev.append(device, children,&lt;br /&gt;
    string.format(&amp;quot;Input-%d&amp;quot;, i), string.format(&amp;quot;Input %d&amp;quot;, i),&lt;br /&gt;
    &amp;quot;urn:schemas-micasaverde-com:device:TemperatureSensor:1&amp;quot;, &amp;quot;D_TemperatureSensor1.xml&amp;quot;,&lt;br /&gt;
    &amp;quot;&amp;quot;, &amp;quot;urn:upnp-org:serviceId:TemperatureSensor1,CurrentTemperature=50&amp;quot;, true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: sync  ===&lt;br /&gt;
&lt;br /&gt;
parameters: device (string or number), ptr (binary object), &lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
If device is a string it is interpreted as a udn, if it's a number, as a device id. Pass in the ptr which you received from the start function. Tells the Luup engine you have finished enumerating the child devices. If the child devices have changed in any way, the new device tree will be written to the configuration file and the Luup engine is reset. &lt;br /&gt;
&lt;br /&gt;
== Module: io  ==&lt;br /&gt;
io.open&amp;lt;br/&amp;gt;&lt;br /&gt;
io.write&amp;lt;br/&amp;gt;&lt;br /&gt;
io.intercept&amp;lt;br/&amp;gt;&lt;br /&gt;
io.read&amp;lt;br/&amp;gt;&lt;br /&gt;
io.is_connected&lt;br /&gt;
&lt;br /&gt;
=== function: open  ===&lt;br /&gt;
&lt;br /&gt;
parameters: device (string or number), ip (string), port (number), &lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
If 'device' is a string it is interpreted as a UDN; if it's a number, as a device ID. This opens a socket on 'port' to 'ip' and stores the handle to the socket in 'device'. The opening of a socket can take time depending on the network, and a Luup function should return quickly whenever possible because each top-level device's Lua implementation runs in a single thread. So the actual opening of the socket occurs asynchronously and this function returns nothing. You will know that the socket opening failed if your subsequent call to write fails. &lt;br /&gt;
&lt;br /&gt;
Generally you do not need to call the open function because the socket is usually started automatically when the Luup engine starts. This is because the user typically either (a) associates a device with the destination io device, such as selecting an RS232 port for an alarm panel, where the RS232 is proxied by a socket, or (b) because the configuration settings for the device already include an IP address and port.&lt;br /&gt;
&lt;br /&gt;
=== function: write  ===&lt;br /&gt;
&lt;br /&gt;
parameters: data (string), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: result (boolean) &lt;br /&gt;
&lt;br /&gt;
In Lua a string can contain binary data, so data may be a binary block. If 'device' is a string it is interpreted as a UDN; if it's a number, as a device ID. This sends data on the socket that was opened automatically or with the open function above, and associated to 'device'. If the socket is not already open, write will wait up to 5 seconds for the socket before it returns an error. Result is 'true' if the data was sent successfully, and is 'false' or nill if an error occurred. &lt;br /&gt;
&lt;br /&gt;
=== function: intercept  ===&lt;br /&gt;
&lt;br /&gt;
parameters: device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
Normally when data comes in on a socket (I/O Port), the block of data is first passed to any pending jobs that are running for the device and are marked as 'waiting for data'. If there are none, or if none of the jobs' incoming data handlers report that they consumed (i.e. processed) the data, then the block of data is passed to the general 'incoming' function handler for the device. If you want to bypass this normal mechanism and read data directly from the socket, call intercept first to tell Luup you want to read incoming data with the read function. This is generally used during the initialization or startup sequences for devices. For example, you may need to send some data (a), receive some response (b), send some more data (c), receive another response (d), etc. In this case you would call 'intercept' first, then send a, then call read and confirm you got b, then call intercept again, then send c, then read d, and so on. &lt;br /&gt;
&lt;br /&gt;
You can call the read function without calling intercept and any incoming data will be returned by that function after it's called. The reason why you generally must call intercept is because normally you want to send some data and get a response. If you write the code like this ''send(data) data=read()'' then it's possible the response will arrive in the brief moment between the execution of send() and read(), and therefore get sent to the incoming data handler for the device. Intercept tells Luup to buffer any incoming data until the next read, bypassing the normal incoming data handler. So ''intercept() send(data) data=read()'' ensures that read will always get the response. If the device you're communicating with sends unsolicited data then there's the risk that the data you read is not the response you're looking for. If so, you can manually pass the response packet to the incoming data handler. &lt;br /&gt;
&lt;br /&gt;
**TBD: Add a function to do this**&lt;br /&gt;
&lt;br /&gt;
=== function: read  ===&lt;br /&gt;
&lt;br /&gt;
parameters: timeout (number), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: data (string) &lt;br /&gt;
&lt;br /&gt;
This reads a block of data from the socket. You must have called ''intercept'' previously so the data is passed. The time unit for ''timeout'' is seconds.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: is_connected  ===&lt;br /&gt;
&lt;br /&gt;
parameters: device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: connected (boolean) &lt;br /&gt;
&lt;br /&gt;
This function returns true if there is a valid IO port connected, otherwise returns false&lt;br /&gt;
&lt;br /&gt;
== Module: luup.job  ==&lt;br /&gt;
&lt;br /&gt;
=== function: status  ===&lt;br /&gt;
&lt;br /&gt;
parameters: job_number (number), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: job_status (number), notes (string) &lt;br /&gt;
&lt;br /&gt;
If '''job_number''' is invalid the function returns ''-1''. If '''device''' is a string it is interpreted as an UDN, if it's a number, as a device ID.&lt;br /&gt;
&lt;br /&gt;
This is the list with all job statuses and their meaning:&lt;br /&gt;
* '''-1''': No job, i.e. job doesn't exist.&lt;br /&gt;
*  '''0''': Job waiting to start.&lt;br /&gt;
*  '''1''': Job in progress.&lt;br /&gt;
*  '''2''': Job error.&lt;br /&gt;
*  '''3''': Job aborted.&lt;br /&gt;
*  '''4''': Job done.&lt;br /&gt;
*  '''5''': Job waiting for callback. Used in special cases.&lt;br /&gt;
*  '''6''': Job requeue. If the job was aborted and needs to be started, use this special value.&lt;br /&gt;
*  '''7''': Job in progress with pending data. This means the job is waiting for data, but can't take it now.&lt;br /&gt;
&lt;br /&gt;
=== function: set  ===&lt;br /&gt;
&lt;br /&gt;
parameters: job (userdata), setting (string), value (string) &lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
This stores a setting for a job. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;&amp;lt;job&amp;gt;&lt;br /&gt;
    luup.job.set(lul_job, &amp;quot;comments&amp;quot;, &amp;quot;In progress...&amp;quot;)&lt;br /&gt;
    local comments = luup.job.setting(lul_job, &amp;quot;comments&amp;quot;)&lt;br /&gt;
    luup.log(&amp;quot;job comments = &amp;quot; .. comments)&lt;br /&gt;
&amp;lt;/job&amp;gt;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: setting  ===&lt;br /&gt;
&lt;br /&gt;
parameters: job (userdata), setting (string) &lt;br /&gt;
&lt;br /&gt;
returns: value (string) &lt;br /&gt;
&lt;br /&gt;
This returns a setting for a job. &lt;br /&gt;
&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_Lua_extensions</id>
		<title>Luup Lua extensions</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_Lua_extensions"/>
				<updated>2013-07-20T14:05:05Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: /* function: register_handler */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In addition to the [[http://lua.org Lua]] commands described in the [[http://www.lua.org/manual/5.1/ Lua reference manual]], you can also reference in your Lua code variables and functions from modules which the Luup engine provides as follows: &lt;br /&gt;
&lt;br /&gt;
== Module: luup  ==&lt;br /&gt;
&lt;br /&gt;
These are general purpose functions and variables. Call them by using the luup. module, such as:&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;luup.log('Now running version: ' .. luup.version)&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== variable: device  ===&lt;br /&gt;
&lt;br /&gt;
The ID of this device instance, if it's running as part of a device &lt;br /&gt;
&lt;br /&gt;
=== variable: version, version_branch, version_major, version_minor  ===&lt;br /&gt;
&lt;br /&gt;
''version'' contains the version of the luup engine, such as &amp;quot;1.0.843&amp;quot;, as a string. The version consists of 3 numbers separated by a period: the branch, the major version, and the minor version. To make it easier to compare against a minimum acceptable version, ''version_branch, version_major and version_minor'' return each component as a number. You can put a comparison in the startup function in your Luup plugin and return false if the minimum version isn't met. The user will then see that the Luup device's startup check failed:&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;if( version_branch ~= 1 or version_major ~= 0 or version_minor &amp;lt; 843 ) then&lt;br /&gt;
    luup.log(&amp;quot;I need version 1.0.843 minimum to run&amp;quot;)&lt;br /&gt;
    return false&lt;br /&gt;
end&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== variable: longitude  ===&lt;br /&gt;
&lt;br /&gt;
Contains the longitude as a number, as found on the location tab in the setup UI. &lt;br /&gt;
&lt;br /&gt;
=== variable: latitude  ===&lt;br /&gt;
&lt;br /&gt;
Contains the latitude as a number, as found on the location tab in the setup UI. &lt;br /&gt;
&lt;br /&gt;
=== variable: timezone  ===&lt;br /&gt;
&lt;br /&gt;
Contains the timezone as a number of hours offset from UTC, as found on the location tab in the setup UI.  It accounts for DST, so, for example, Pacific Standard time will be -8 or -9 depending on DST.&lt;br /&gt;
&lt;br /&gt;
'''Note:''' Contains 0 for MiOS &amp;lt; 1.5.250 (Vera V2) / &amp;lt; 1.5.249 (Vera V3).&lt;br /&gt;
&lt;br /&gt;
=== variable: city  ===&lt;br /&gt;
&lt;br /&gt;
Contains the city as a string, as found on the location tab in the setup UI. &lt;br /&gt;
&lt;br /&gt;
=== variable: devices  ===&lt;br /&gt;
&lt;br /&gt;
Contains all the devices in the system as a table indexed by the device number.&lt;br /&gt;
&lt;br /&gt;
The members are:&lt;br /&gt;
* '''room_num''': (number) This is the number of the room the device is in. &lt;br /&gt;
* '''device_type''': (string) This is a string representing the type of the device.&lt;br /&gt;
* '''category_num''': (number) This is a category for the device.  See: [[Luup_Device_Categories]] for a list. &lt;br /&gt;
* '''subcategory_num''': (number) This is a sub category for the device.&lt;br /&gt;
* '''device_num_parent''': (number) This is the number of the parent device. See: [[Lua Device Structure]] for details. &lt;br /&gt;
* '''ip''': (string) If this device is IP based, this is the IP address. &lt;br /&gt;
* '''mac''': (string) If this device is IP based, this is the MAC address. &lt;br /&gt;
* '''user''': (string) If this device is IP based and requires http authentication, this is the username&lt;br /&gt;
* '''pass''': (string) If this device is IP based and requires http authentication, this is the password&lt;br /&gt;
* '''id''': (string) If this device has an internal ID that is specific to the device, it is contained here. For example, for Z-Wave devices this is the Node ID, and for Insteon device it is the Insteon ID. &lt;br /&gt;
* '''embedded''': (boolean) If this device is embedded, it means that it doesn't have its own room or exist as a separate device. It should be considered part of its parent. Like a 3-in-1 sensor is a device with 3 embedded child devices. &lt;br /&gt;
* '''hidden''': (boolean) If true the user checked the 'hidden' box and doesn't want to see the device on the dashboard. &lt;br /&gt;
* '''invisible''': (boolean) If true the device is 'for internal use only' and shouldn't be presented to the user. &lt;br /&gt;
* '''description''': (string) This is the text description for the device as supplied by the user in the web UI. &lt;br /&gt;
* '''udn''': (string) This is the UDN for the UPnP device.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Example to log device #5's IP address and its internal ID:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;luup.log('Device #5 ip: ' .. luup.devices[5].ip .. ' id: ' .. luup.devices[5].id)&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This code will log all the attributes from all the devices:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;for k, v in pairs(luup.devices) do&lt;br /&gt;
    for k2, v2 in pairs(v) do&lt;br /&gt;
        luup.log(&amp;quot;Device #&amp;quot; .. k .. &amp;quot;:&amp;quot; .. k2 .. &amp;quot;=&amp;quot; .. tostring(v2))&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return true&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== variable: rooms  ===&lt;br /&gt;
&lt;br /&gt;
Contains all the rooms as a table of strings indexed by the room number. Example:&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;luup.log('Room #1 is called: ' .. luup.rooms[1])&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== variable: scenes  ===&lt;br /&gt;
&lt;br /&gt;
Contains all the scenes in the system as a table indexed by the scene number. The members are: room_num (number), description(string), hidden(boolean)&lt;br /&gt;
&lt;br /&gt;
=== variable: remotes  ===&lt;br /&gt;
&lt;br /&gt;
Contains all the remotes in the system as a table indexed by the remote id. The members are: remote_file (string), room_num (number), description(string) &lt;br /&gt;
&lt;br /&gt;
=== variable: event_server ===&lt;br /&gt;
&lt;br /&gt;
type: string&lt;br /&gt;
&lt;br /&gt;
Contains the notification/event server. On UI5 it can be either ''cms1.mios.com'' or ''cms2.mios.com''.&lt;br /&gt;
&lt;br /&gt;
=== variable: ra_server ===&lt;br /&gt;
&lt;br /&gt;
type: string&lt;br /&gt;
&lt;br /&gt;
Contains the remote access server. Can be either ''fwd1.mios.com'' or ''fwd2.mios.com''.&lt;br /&gt;
&lt;br /&gt;
=== variable: pk_accesspoint ===&lt;br /&gt;
&lt;br /&gt;
type: number&lt;br /&gt;
&lt;br /&gt;
Contains the serial number of this Vera.&lt;br /&gt;
&lt;br /&gt;
=== variable: hw_key ===&lt;br /&gt;
&lt;br /&gt;
type: string&lt;br /&gt;
&lt;br /&gt;
Contains the Vera hardware key.&lt;br /&gt;
&lt;br /&gt;
=== function: log  ===&lt;br /&gt;
&lt;br /&gt;
parameters: what_to_log (string), log_level (optional, number) &lt;br /&gt;
&lt;br /&gt;
return: nothing &lt;br /&gt;
&lt;br /&gt;
Writes what_to_log to the log, /var/log/cmh/LuaUPnP.log, with the given log_level, which is 50 by default. See: [[Luup Loglevels]] &lt;br /&gt;
&lt;br /&gt;
=== function: task  ===&lt;br /&gt;
&lt;br /&gt;
parameters: message (string), status (number), description (string), handle (number) &lt;br /&gt;
&lt;br /&gt;
return: handle (number) &lt;br /&gt;
&lt;br /&gt;
When the Luup engine is starting status messages are displayed for the various modules as they're initialized. Normally each device, including Luup devices, automatically log their status and the user is shown an error if the device doesn't start, such as if the 'startup' function returns an error. &lt;br /&gt;
&lt;br /&gt;
If you have other startup sequences which you want the user to see to know that startup hasn't finished yet, call this function passing in a handle of -1 for the first call. The status should be: 1=Busy, 2=Error, 4=Successful. Message is the current state, such as 'downloading', and description describes the module, like 'Smartphone UI'. After the first call, store the handle and pass it on future calls to update the status rather than add a new one. &lt;br /&gt;
&lt;br /&gt;
=== function: call_delay  ===&lt;br /&gt;
&lt;br /&gt;
parameters: function_name (string), seconds (number), data (string), thread (bool) &lt;br /&gt;
&lt;br /&gt;
returns: result (number) &lt;br /&gt;
&lt;br /&gt;
The function ''function_name'' (the first parameter), which must be passed as a string, will be called in ''seconds'' seconds (the second parameter), and will be passed the string ''data''. The function returns 0 if successful. See: [[Luup Declarations#timed_function_callback]] for the names and syntax of the parameters that will be passed to function_name. function_name will only be called once and you must call call_delay again if you want it called again, or use call_timer instead. &lt;br /&gt;
&lt;br /&gt;
If thread is specified and is true or 1, the call back will be made in it's own thread and can block if needed. Normally it is called by a worker thread and is expected to return immediately.&lt;br /&gt;
&lt;br /&gt;
As of December 19, 2011, for all builds after 1.5.237, the 'thread' will be ignored.  Each Lua state has its own worker thread now, so all calls to call_delay and call_timer will occur in a separate thread.&lt;br /&gt;
&lt;br /&gt;
=== function: call_timer  ===&lt;br /&gt;
&lt;br /&gt;
parameters: function_name (string), type (number), time (string), days (string), data (string) &lt;br /&gt;
&lt;br /&gt;
returns: result (number) &lt;br /&gt;
&lt;br /&gt;
The function 'function_name', which must be passed as a string, will be called when the timer is triggered, and will be passed the string 'data'. The function returns 0 if successful. See: [[Luup Declarations#timed_function_callback]] for the names and syntax of the parameters that will be passed to function_name. &lt;br /&gt;
&lt;br /&gt;
Type is 1=Interval timer, 2=Day of week timer, 3=Day of month timer, 4=Absolute timer. For an interval timer, days is not used, and Time should be a number of seconds, minutes, or hours using an optional 'h' or 'm' suffix. Example: 30=call in 30 seconds, 5m=call in 5 minutes, 2h=call in 2 hours. For a day of week timer, Days is a comma separated list with the days of the week where 1=Monday and 7=Sunday. Time is the time of day in hh:mm:ss format. Time can also include an 'r' at the end for Sunrise or a 't' for Sunset and the time is relative to sunrise/sunset. For example: Days=&amp;quot;3,5&amp;quot; Time=&amp;quot;20:30:00&amp;quot; means your function will be called on the next Wed or Fri at 8:30pm. Days=&amp;quot;1,7&amp;quot; Time=&amp;quot;-3:00:00r&amp;quot; means your function will be called on the next Monday or Sunday 3 hours before sunrise. Day of month works the same way except Days is a comma separated list of days of the month, such as &amp;quot;15,20,30&amp;quot;. For an absolute timer, Days is not used, and Time should be in the format: &amp;quot;yyyy-mm-dd hh:mm:ss&amp;quot; &lt;br /&gt;
&lt;br /&gt;
Data can be a string passed back to the function. The function should be declared so it takes a single argument, which is this data.&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;function refreshCache(stuff)&lt;br /&gt;
    ....&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function startup()&lt;br /&gt;
    --&lt;br /&gt;
    -- Setup an interval-based timer to call refreshCache after 30 minutes.&lt;br /&gt;
    -- Note that if you want it to &amp;quot;recur&amp;quot; then you need to call this function again&lt;br /&gt;
    -- at the end of the refreshCache() implementation.&lt;br /&gt;
    --&lt;br /&gt;
    luup.call_timer(&amp;quot;refreshCache&amp;quot;, 1, &amp;quot;30m&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;SomeStuff&amp;quot;)&lt;br /&gt;
end&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== function: is_ready  ===&lt;br /&gt;
&lt;br /&gt;
parameters: device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: ready (boolean) &lt;br /&gt;
&lt;br /&gt;
version: UI5 and above&lt;br /&gt;
&lt;br /&gt;
Checks whether a device has successfully completed it's startup sequence. If so, &amp;lt;tt&amp;gt;is_ready&amp;lt;/tt&amp;gt; returns &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;. If your device shouldn't process incoming data until the startup sequence is finished, you may want to add a condition to the &amp;lt;tt&amp;gt;&amp;lt;incoming&amp;gt; block&amp;lt;/tt&amp;gt; that only processes data if &amp;lt;tt&amp;gt;is_ready(lul_device)&amp;lt;/tt&amp;gt; is &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;device&amp;lt;/tt&amp;gt; parameter, if it's a string, is interpreted as a udn.  If it's a number, it's interpreted as a device number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;&amp;lt;incoming&amp;gt;&lt;br /&gt;
    if (luup.is_ready(lul_device) == false) then&lt;br /&gt;
        return&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    doSomething(lul_device)&lt;br /&gt;
&amp;lt;/incoming&amp;gt;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: call_action  ===&lt;br /&gt;
&lt;br /&gt;
parameters: service (string), action (string), arguments (table), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: error (number), error_msg (string), job (number), arguments (table) &lt;br /&gt;
&lt;br /&gt;
Invokes the UPnP service + action, passing in the arguments (table of string-&amp;amp;gt;string pairs) to the device, which, if it's a string, is interpreted as a udn, and if it's a number, is the device number. If the invocation could not be made, only error will be returned with a value of -1. Otherwise, all 4 values are returned. error is 0 if the UPnP device reported the action was successful. arguments is a table of string-&amp;amp;gt;string pairs with the return arguments from the action. If the action is handled asynchronously by a Luup job, then the job number will be returned as a positive integer. &lt;br /&gt;
&lt;br /&gt;
Example to dim device #5 to 50%:&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;local lul_arguments = {}&lt;br /&gt;
lul_arguments[&amp;quot;newLoadlevelTarget&amp;quot;] = 50&lt;br /&gt;
lul_resultcode, lul_resultstring, lul_job, lul_returnarguments = luup.call_action(&amp;quot;urn:upnp-org:serviceId:Dimming1&amp;quot;,&lt;br /&gt;
                                                                                  &amp;quot;SetLoadLevelTarget&amp;quot;, lul_arguments,&lt;br /&gt;
                                                                                  5)&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: variable_set  ===&lt;br /&gt;
&lt;br /&gt;
parameters: service (string), variable (string), value (string), device (string or number), [startup (bool)] &lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
The UPnP service+variable will be set to value for device, which if it's a string, is interpreted as a udn, and if it's a number, as a device id. If there are events or notifications tied to the variable they will be fired. &lt;br /&gt;
&lt;br /&gt;
Optionally, you can add an argument 'startup'. If startup is true, this change will be considered a startup value, and if the variable is set to it's existing value, events and notifications will ''not'' be fired. &lt;br /&gt;
&lt;br /&gt;
=== function: variable_get  ===&lt;br /&gt;
&lt;br /&gt;
parameters: service (string), variable (string), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: value (string) and time (number) or none (note: none means nothing at all. It does not mean 'nil')&lt;br /&gt;
&lt;br /&gt;
Returns the value of the UPnP service+variable and the time when the variable was last modified, as a unix time stamp (number of seconds since 1/1/1970). If the service+variable or device does not exist, it returns nothing. You can assign just the value to a variable, as follows:&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;local value = luup.variable_get(&amp;quot;urn:upnp-org:serviceId:Dimming1&amp;quot;, &amp;quot;LoadLevelTarget&amp;quot;, 5)&lt;br /&gt;
luup.log(&amp;quot;Dim level for device #5 is: &amp;quot; .. value)&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: attr_set  ===&lt;br /&gt;
&lt;br /&gt;
parameters: attribute (string), value(string), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: none &lt;br /&gt;
&lt;br /&gt;
Sets the top level attribute for the device to value. Examples of attributes are 'mac', 'name', 'id', etc.&lt;br /&gt;
&lt;br /&gt;
=== function: attr_get  ===&lt;br /&gt;
&lt;br /&gt;
parameters: attribute (string), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: string or none  (note: none means nothing at all. It does not mean 'nil')&lt;br /&gt;
&lt;br /&gt;
Gets the top level attribute for the device.  Examples of attributes are 'mac', 'name', 'id', etc.  If the attribute doesn't exist, it returns nothing.  If nothing is passed in for device, it gets the top level attribute from the master userdata, like firmware_version.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;-- this code logs nil if theDeviceNumber is invalid&lt;br /&gt;
local theName = luup.attr_get ('name', theDeviceNumber)&lt;br /&gt;
luup.log(theName)&lt;br /&gt;
return true&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;-- this code fails if theDeviceNumber is invalid&lt;br /&gt;
luup.log(luup.attr_get ('name', theDeviceNumber))&lt;br /&gt;
return true&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: register_handler  ===&lt;br /&gt;
&lt;br /&gt;
parameters: function_name (string), request_name (string) &lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
When a certain URL is requested from a web browser or other HTTP get, function_name will be called and whatever string and content_type it returns will be returned. &lt;br /&gt;
&lt;br /&gt;
See the Smartphone Web Interface plugin as an example:&amp;lt;br&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;luup.register_handler(&amp;quot;lug_WapRequest&amp;quot;,&amp;quot;wap&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function lug_WapRequest (lul_request, lul_parameters, lul_outputformat)&lt;br /&gt;
    local lul_html = &amp;quot;&amp;lt;head&amp;gt;\n&amp;quot; ..&lt;br /&gt;
                     &amp;quot;&amp;lt;title&amp;gt;Main&amp;lt;/title&amp;gt;\n&amp;quot; ..&lt;br /&gt;
                     &amp;quot;&amp;lt;/head&amp;gt;\n&amp;quot; ..&lt;br /&gt;
                     &amp;quot;&amp;lt;body&amp;gt;\n&amp;quot; ..&lt;br /&gt;
                     &amp;quot;Choose a room:&amp;lt;br/&amp;gt;\n&amp;quot;&lt;br /&gt;
    local lua_content_type = &amp;quot;text/html&amp;quot;&lt;br /&gt;
    return lul_html, lua_content_type&lt;br /&gt;
end&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The request is made with the URL: data_request?id=lr_[the registered name] on port 49451. So: http://192.168.1.1:49451/data_request?id=lr_wap will return the web page defined in the function lug_WapRequest in the example above.&lt;br /&gt;
&lt;br /&gt;
=== function: variable_watch  ===&lt;br /&gt;
&lt;br /&gt;
parameters: function_name (string), service (string), variable (string or nil), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
Whenever the UPnP variable is changed for the specified device, which if a string is interpreted as a UDN and if a number as a device ID, ''function_name'' will be called. See [[Luup Declarations#watch_callback]] for the values that will be passed to ''function_name''. If variable is nil, ''function_name'' will be called whenever any variable in the service is changed. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: devices_by_service  ===&lt;br /&gt;
&lt;br /&gt;
parameters: &lt;br /&gt;
&lt;br /&gt;
returns: &lt;br /&gt;
&lt;br /&gt;
=== function: device_supports_service  ===&lt;br /&gt;
&lt;br /&gt;
parameters: service ID (string), device (string or number)&lt;br /&gt;
&lt;br /&gt;
returns: ''true'' if the device supports the service, ''false'' otherwise&lt;br /&gt;
&lt;br /&gt;
A device supports a service if there is at least a command or state variable defined for that device using that service. Setting UPnP variables is unrestricted and free form, and the engine doesn't really know if a device actually uses it or does anything with it. So this function isn't really definitive.&lt;br /&gt;
&lt;br /&gt;
=== function: set_failure  ===&lt;br /&gt;
&lt;br /&gt;
parameters: value (boolean), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: &lt;br /&gt;
&lt;br /&gt;
Luup maintains a 'failure' flag for every device to indicate if it is not functioning. You can set the flag to true if the device is failing. If device is a string it is interpreted as a udn, if it's a number, as a device id. The lu_status URL will show for the device: &amp;lt;tooltip display=&amp;quot;1&amp;quot; tag2=&amp;quot;Lua Failure&amp;quot;/&amp;gt; and Lua Failure is shown in red in UI5 for the device.&lt;br /&gt;
&lt;br /&gt;
=== function: is_night  ===&lt;br /&gt;
&lt;br /&gt;
parameters: none &lt;br /&gt;
&lt;br /&gt;
returns: ''true'' if it's past sunset and before sunrise, ''false'' otherwise.&lt;br /&gt;
&lt;br /&gt;
=== function: sleep  ===&lt;br /&gt;
&lt;br /&gt;
parameters: number of milliseconds &lt;br /&gt;
&lt;br /&gt;
returns: none &lt;br /&gt;
&lt;br /&gt;
Sleeps a certain number of milliseconds&lt;br /&gt;
&lt;br /&gt;
=== function: sunset / sunrise ===&lt;br /&gt;
&lt;br /&gt;
parameters: none&lt;br /&gt;
&lt;br /&gt;
returns: The next sunset / sunrise in a Unix timestamp (i.e. the number of seconds since 1/1/1970 in UTC time). You can do a diff with &amp;lt;tt&amp;gt;os.time&amp;lt;/tt&amp;gt; to see how long it will be for the next event.  &amp;lt;tt&amp;gt;luup.sunset-os.time&amp;lt;/tt&amp;gt; is the number of seconds before the next sunset. Be sure the location and timezone are properly set or the sunset/sunrise will be wrong.&lt;br /&gt;
&lt;br /&gt;
required firmware: 1.5.353&lt;br /&gt;
&lt;br /&gt;
== Module: luup.inet  ==&lt;br /&gt;
&lt;br /&gt;
=== function: wget  ===&lt;br /&gt;
&lt;br /&gt;
parameters: URL (String), Timeout (Number), Username (String), Password (String) &lt;br /&gt;
&lt;br /&gt;
returns httpStatusCode (Number), content (String) &lt;br /&gt;
&lt;br /&gt;
This reads the URL and returns 2 variables: the first is a numeric error code which is 0 if successful, and the second is a string containing the contents of the page. If '''Timeout''' is specified, the function will timeout after that many seconds. The default value for '''Timeout''' is 5 seconds. If '''Username''' and '''Password''' are specified, they will be used for HTTP Basic Authentication. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Module: luup.chdev  ==&lt;br /&gt;
&lt;br /&gt;
Contains functions for a parent to synchronize its child devices. Whenever a device has multiple end-points, the devices are represented in a parent/child fashion where the parent device is responsible for reporting what child devices it has and giving each one a unique id. For example in the sample [[Luup Somfy Walkthrough]] there is a parent device, which is the interface module that controls up to 16 blinds, and up to 16 child devices, one for each blind. As shown in that sample, the parent calls start, then enumerates each child device with append, and finally calls sync. You will need to pass the same value for device to append and sync that you passed to start. &lt;br /&gt;
&lt;br /&gt;
=== function: start  ===&lt;br /&gt;
&lt;br /&gt;
parameters: device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: ptr (binary object) &lt;br /&gt;
&lt;br /&gt;
Tells Luup you will start enumerating the children of device. If device is a string it is interpreted as a udn, if it's a number, as a device id. The return value is a binary object which you cannot do anything with in Lua, but you do pass it to the append and sync functions. &lt;br /&gt;
&lt;br /&gt;
=== function: append  ===&lt;br /&gt;
&lt;br /&gt;
parameters: device (string or number), ptr (binary object), id (string), description (string), device_type (string), device_filename (string), implementation_filename (string), parameters (string), embedded (boolean) [, invisible (boolean)]&lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
Adds one child to &amp;lt;tt&amp;gt;device&amp;lt;/tt&amp;gt;. If &amp;lt;tt&amp;gt;device&amp;lt;/tt&amp;gt; is a string it is interpreted as a udn, if it's a number, as a device id. &lt;br /&gt;
&lt;br /&gt;
Pass in the &amp;lt;tt&amp;gt;ptr&amp;lt;/tt&amp;gt; which you received from the &amp;lt;tt&amp;gt;luup.chdev.start&amp;lt;/tt&amp;gt; call. Give each child a unique id so you can keep track of which is which. You can optionally provide a &amp;lt;tt&amp;gt;description&amp;lt;/tt&amp;gt; which the user sees in the user interface.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;device_type&amp;lt;/tt&amp;gt; is the UPnP device type, such as &amp;lt;tt&amp;gt;urn:schemas-upnp-org:device:BinaryLight:1&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;tt&amp;gt;device_filename&amp;lt;/tt&amp;gt; is specified, that is the name of the&amp;amp;nbsp;XML file with the UPnP device specification. The deviceType from the filename will override any &amp;lt;tt&amp;gt;device_type&amp;lt;/tt&amp;gt; you set manually. If the device_file contains the implementation file for this child device you do not need to specify it in &amp;lt;tt&amp;gt;implementation_filename&amp;lt;/tt&amp;gt;. Otherwise, if there is a Luup implementation for this child device and it's not being handled by the parent device, you can specify it in &amp;lt;tt&amp;gt;implementation_filename&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;tt&amp;gt;embedded&amp;lt;/tt&amp;gt; is true, the 'embedded' flag is set for the device which generally means that the parent and all the children will be displayed as one compound device, or group, rather than as separate devices which you can put in their own rooms. &lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;parameters&amp;lt;/tt&amp;gt; are UPnP service,variables you want set when the device is created. You can specify multiple variables by separating them with a line feed (\n) and use a &amp;lt;tt&amp;gt;,&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;=&amp;lt;/tt&amp;gt; to separate service, variable and value, like this: &amp;lt;tt&amp;gt;service,variable=value\nservice&amp;lt;/tt&amp;gt;...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
luup.chdev.append(device, children,&lt;br /&gt;
    string.format(&amp;quot;Input-%d&amp;quot;, i), string.format(&amp;quot;Input %d&amp;quot;, i),&lt;br /&gt;
    &amp;quot;urn:schemas-micasaverde-com:device:TemperatureSensor:1&amp;quot;, &amp;quot;D_TemperatureSensor1.xml&amp;quot;,&lt;br /&gt;
    &amp;quot;&amp;quot;, &amp;quot;urn:upnp-org:serviceId:TemperatureSensor1,CurrentTemperature=50&amp;quot;, true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: sync  ===&lt;br /&gt;
&lt;br /&gt;
parameters: device (string or number), ptr (binary object), &lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
If device is a string it is interpreted as a udn, if it's a number, as a device id. Pass in the ptr which you received from the start function. Tells the Luup engine you have finished enumerating the child devices. If the child devices have changed in any way, the new device tree will be written to the configuration file and the Luup engine is reset. &lt;br /&gt;
&lt;br /&gt;
== Module: io  ==&lt;br /&gt;
io.open&amp;lt;br/&amp;gt;&lt;br /&gt;
io.write&amp;lt;br/&amp;gt;&lt;br /&gt;
io.intercept&amp;lt;br/&amp;gt;&lt;br /&gt;
io.read&amp;lt;br/&amp;gt;&lt;br /&gt;
io.is_connected&lt;br /&gt;
&lt;br /&gt;
=== function: open  ===&lt;br /&gt;
&lt;br /&gt;
parameters: device (string or number), ip (string), port (number), &lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
If 'device' is a string it is interpreted as a UDN; if it's a number, as a device ID. This opens a socket on 'port' to 'ip' and stores the handle to the socket in 'device'. The opening of a socket can take time depending on the network, and a Luup function should return quickly whenever possible because each top-level device's Lua implementation runs in a single thread. So the actual opening of the socket occurs asynchronously and this function returns nothing. You will know that the socket opening failed if your subsequent call to write fails. &lt;br /&gt;
&lt;br /&gt;
Generally you do not need to call the open function because the socket is usually started automatically when the Luup engine starts. This is because the user typically either (a) associates a device with the destination io device, such as selecting an RS232 port for an alarm panel, where the RS232 is proxied by a socket, or (b) because the configuration settings for the device already include an IP address and port.&lt;br /&gt;
&lt;br /&gt;
=== function: write  ===&lt;br /&gt;
&lt;br /&gt;
parameters: data (string), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: result (boolean) &lt;br /&gt;
&lt;br /&gt;
In Lua a string can contain binary data, so data may be a binary block. If 'device' is a string it is interpreted as a UDN; if it's a number, as a device ID. This sends data on the socket that was opened automatically or with the open function above, and associated to 'device'. If the socket is not already open, write will wait up to 5 seconds for the socket before it returns an error. Result is 'true' if the data was sent successfully, and is 'false' or nill if an error occurred. &lt;br /&gt;
&lt;br /&gt;
=== function: intercept  ===&lt;br /&gt;
&lt;br /&gt;
parameters: device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
Normally when data comes in on a socket (I/O Port), the block of data is first passed to any pending jobs that are running for the device and are marked as 'waiting for data'. If there are none, or if none of the jobs' incoming data handlers report that they consumed (i.e. processed) the data, then the block of data is passed to the general 'incoming' function handler for the device. If you want to bypass this normal mechanism and read data directly from the socket, call intercept first to tell Luup you want to read incoming data with the read function. This is generally used during the initialization or startup sequences for devices. For example, you may need to send some data (a), receive some response (b), send some more data (c), receive another response (d), etc. In this case you would call 'intercept' first, then send a, then call read and confirm you got b, then call intercept again, then send c, then read d, and so on. &lt;br /&gt;
&lt;br /&gt;
You can call the read function without calling intercept and any incoming data will be returned by that function after it's called. The reason why you generally must call intercept is because normally you want to send some data and get a response. If you write the code like this ''send(data) data=read()'' then it's possible the response will arrive in the brief moment between the execution of send() and read(), and therefore get sent to the incoming data handler for the device. Intercept tells Luup to buffer any incoming data until the next read, bypassing the normal incoming data handler. So ''intercept() send(data) data=read()'' ensures that read will always get the response. If the device you're communicating with sends unsolicited data then there's the risk that the data you read is not the response you're looking for. If so, you can manually pass the response packet to the incoming data handler. &lt;br /&gt;
&lt;br /&gt;
**TBD: Add a function to do this**&lt;br /&gt;
&lt;br /&gt;
=== function: read  ===&lt;br /&gt;
&lt;br /&gt;
parameters: timeout (number), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: data (string) &lt;br /&gt;
&lt;br /&gt;
This reads a block of data from the socket. You must have called ''intercept'' previously so the data is passed. The time unit for ''timeout'' is seconds.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: is_connected  ===&lt;br /&gt;
&lt;br /&gt;
parameters: device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: connected (boolean) &lt;br /&gt;
&lt;br /&gt;
This function returns true if there is a valid IO port connected, otherwise returns false&lt;br /&gt;
&lt;br /&gt;
== Module: luup.job  ==&lt;br /&gt;
&lt;br /&gt;
=== function: status  ===&lt;br /&gt;
&lt;br /&gt;
parameters: job_number (number), device (string or number) &lt;br /&gt;
&lt;br /&gt;
returns: job_status (number), notes (string) &lt;br /&gt;
&lt;br /&gt;
If '''job_number''' is invalid the function returns ''-1''. If '''device''' is a string it is interpreted as an UDN, if it's a number, as a device ID.&lt;br /&gt;
&lt;br /&gt;
This is the list with all job statuses and their meaning:&lt;br /&gt;
* '''-1''': No job, i.e. job doesn't exist.&lt;br /&gt;
*  '''0''': Job waiting to start.&lt;br /&gt;
*  '''1''': Job in progress.&lt;br /&gt;
*  '''2''': Job error.&lt;br /&gt;
*  '''3''': Job aborted.&lt;br /&gt;
*  '''4''': Job done.&lt;br /&gt;
*  '''5''': Job waiting for callback. Used in special cases.&lt;br /&gt;
*  '''6''': Job requeue. If the job was aborted and needs to be started, use this special value.&lt;br /&gt;
*  '''7''': Job in progress with pending data. This means the job is waiting for data, but can't take it now.&lt;br /&gt;
&lt;br /&gt;
=== function: set  ===&lt;br /&gt;
&lt;br /&gt;
parameters: job (userdata), setting (string), value (string) &lt;br /&gt;
&lt;br /&gt;
returns: nothing &lt;br /&gt;
&lt;br /&gt;
This stores a setting for a job. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;lua&amp;quot;&amp;gt;&amp;lt;job&amp;gt;&lt;br /&gt;
    luup.job.set(lul_job, &amp;quot;comments&amp;quot;, &amp;quot;In progress...&amp;quot;)&lt;br /&gt;
    local comments = luup.job.setting(lul_job, &amp;quot;comments&amp;quot;)&lt;br /&gt;
    luup.log(&amp;quot;job comments = &amp;quot; .. comments)&lt;br /&gt;
&amp;lt;/job&amp;gt;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== function: setting  ===&lt;br /&gt;
&lt;br /&gt;
parameters: job (userdata), setting (string) &lt;br /&gt;
&lt;br /&gt;
returns: value (string) &lt;br /&gt;
&lt;br /&gt;
This returns a setting for a job. &lt;br /&gt;
&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_Plugins_ByHand</id>
		<title>Luup Plugins ByHand</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_Plugins_ByHand"/>
				<updated>2013-07-07T20:01:06Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: Add description of &amp;lt;files&amp;gt; element.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
==Description of the XML files==&lt;br /&gt;
&lt;br /&gt;
Because the web generator is not yet operational (as of June 2013) you need to create the XML files by hand.  The easiest way to do this is to start with an existing device as a template that you modify to suit your needs.  In Vera's Setup Web UI, choose Devices, Luup plug-ins (in UI5, choose Apps, Develop Apps, Luup Files).  There is a list of all the XML files which came with Vera by default.  Files that start with D_ are UPnP device specifications.  Files that start with S_ are UPnP service specifications.  And files that start with I_ are Luup implementation files.  Click view to see the XML file.  We recommend using the Firefox web browser because it has built-in support for nicely displaying XML files in a graphical tree.  XML can be hard to read in other web browser.&lt;br /&gt;
&lt;br /&gt;
When you open a device specification file the xml tag deviceType defines what kind of device it is.  This is how a UPnP Control Point knows what this device is.  If the device type starts with urn:schemas-upnp-org, that means it's a UPnP defined standard, and the list of services the device must support are defined by the UPnP forum.  If you're creating your own device type you can substitute the schemas-upnp-org with your own web domain name and change the other parts of the name.  But stick to the same convention and use only a-z, 0-9 and hypens (-) and colons (:).  Remember though that if you use your own device type chances are UPnP Control Points won't know what to do with it unless the author of the control point makes a custom addition for you.&lt;br /&gt;
&lt;br /&gt;
The device file references the service files (S_) and gives each service a serviceType and a serviceId.  The serviceType what defines the standard UPnP service.  But since it's possible to have multiple instances of a given service, each needs a unique serviceId.  For example, there is a standard UPnP service to set the setpoint on a thermostat called: urn:schemas-upnp-org:service:TemperatureSetpoint:1.  But many thermostats have multiple setpoints, such as heat and cool.  So in the standard for a UPnP thermostat device (D_HVAC_ZoneThermostat1.xml), there are 2 instances of the serviceType &amp;quot;urn:schemas-upnp-org:service:TemperatureSetpoint:1&amp;quot;, one has the id &amp;quot;urn:upnp-org:serviceId:TemperatureSetpoint1_Cool&amp;quot; and the other &amp;quot;urn:upnp-org:serviceId:TemperatureSetpoint1_Heat&amp;quot;.  They both use the same service specification, S_TemperatureSetpoint1.xml, which is in the XML tag SCPDURL.&lt;br /&gt;
&lt;br /&gt;
The controlURL and eventSubURL are set by the Luup engine and the values in the device specification file are ignored.&lt;br /&gt;
&lt;br /&gt;
The UPnP specification allows that we can add our own custom xml tags.  So we add the xml tags &amp;quot;implemenationList&amp;quot; to the device specification which references the implementation files that device will use.&lt;br /&gt;
&lt;br /&gt;
== The Luup XML implementation file ==&lt;br /&gt;
&lt;br /&gt;
I_GC100.xml is a full featured, functional implementation for the Global Cache GC100, which is an ethernet device with relay switches, input sensors, infrared transmitters and serial ports. I_GC100.xml shows a complete Luup plugin with parent/child devices. The corresponding UPnP Device specification file is D_GC100.xml. In all the implementation files the top-level XML tag (ie root node) is called &amp;quot;implementation&amp;quot;. It contains the following elements: &lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;settings&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
The settings node contains various settings for the implementation. &lt;br /&gt;
&lt;br /&gt;
protocol: Is the protocol to use to talk to the device if you'll be sending data over the network or a serial port. The protocol tag tells Luup what's considered a single ''chunk'' of data. By using a format, from the supported list below, you avoid byte-by-byte processing on input streams as the Luup engine will ''chunk'' the data to you and pass it to your Lua code handling &amp;lt;tt&amp;gt;&amp;lt;incoming&amp;gt;&amp;lt;/tt&amp;gt; requests.&lt;br /&gt;
Lua code is much cleaner when it handles data in chunks.  If you have a protocol that's not natively supported, and is likely to be used by other devices, let us know and we'll add it to the Luup engine so you don't need to mess with it.&lt;br /&gt;
&lt;br /&gt;
Valid values for this tag are:&lt;br /&gt;
*&amp;lt;tt&amp;gt;crlf&amp;lt;/tt&amp;gt; - all incoming commands are terminated with a carriage return+line feed character, and all outgoing data should have a cr+lf appended. &lt;br /&gt;
*&amp;lt;tt&amp;gt;stxetx&amp;lt;/tt&amp;gt; - all incoming commands are surrounded by STX and ETX characters.  If you send the string &amp;quot;test&amp;quot; the framework will add the STX before and the ETX at the end, and if the string &amp;quot;&amp;lt;tt&amp;gt;''&amp;lt;stx&amp;gt;''test''&amp;lt;etx&amp;gt;''&amp;lt;/tt&amp;gt;&amp;quot; is received, the framework will strip the STX and ETX and pass the string &amp;quot;test&amp;quot; to your incoming data handler.&lt;br /&gt;
*&amp;lt;tt&amp;gt;raw&amp;lt;/tt&amp;gt; - makes no modifications to outgoing data, and calls your incoming data callback for each byte received.  This adds more overhead since the engine needs to call your Luup function for every character, and makes your code complex.  So, generally avoid using '&amp;lt;tt&amp;gt;raw&amp;lt;/tt&amp;gt;' and let us add support for your protocol if you have a new one we don't yet support.&lt;br /&gt;
&lt;br /&gt;
ioPort: One way to have the device talk to another device on the internet. If you put a TCP port number here, an outgoing connection attempt is made automatically when the device initializes. (The remote IP address should be placed in the 'ip' box in the device's advanced configuration tab.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
handleChildren: If this flag is 1, then any actions that are sent to one of this device's children will be handled within this devices implementation file. Look at the I_GC100.xml file to see how this is done. The GC100 is a parent device which has no services or actions, but has child devices like IR transmitters and relays. This implementation file handles the SwitchPower/SetTarget action for the relays, as well as the SendProntoCode action for the ir transmitters, even though those actions will be sent to the GC100's child device--not the device GC100 itself.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;functions&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Put here the Lua code for functions you want to be able to use in other places in your Lua code. You can also declare local variables here before the functions are described. Watch out for XML syntax. In particular, the &amp;gt; and &amp;gt;= comparison operators should be escaped.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;files&amp;gt; ===&lt;br /&gt;
As an alternative to the &amp;lt;functions&amp;gt; element, you can put your Lua implementation in a separate file containing pure Lua code and not worry about XML escapes. By convention, the file name should begin with L_ and end with .lua. as in &amp;lt;files&amp;gt;L_MyDevice.lua&amp;lt;/files&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;startup&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
This tag is a comma separated list of functions you want the Luup engine to call when it's starting up. Generally these are functions in the 'functions' tag. &lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;actionList&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
This is where you specify what to do when an action comes in. Create an &amp;quot;action&amp;quot; node for each action, which contains &amp;quot;serviceId&amp;quot; and &amp;quot;name&amp;quot; tags to indicate what action is being implemented. The &amp;quot;name&amp;quot; tag refers to the name of the Action as defined in the UPnP Service Specification file. &lt;br /&gt;
&lt;br /&gt;
==== function declarations ====&lt;br /&gt;
&lt;br /&gt;
Whatever Lua code you create in the tags will be put inside a function automatically by the Luup engine, and your code will be passed variables that are relevant to whatever the code needs to do. For example, the code inside the 'run' tag is passed lul_device,lul_settings where lul_device is the id of the device the action was sent to, and lul_settings has the arguments to the UPnP action. See: [[Luup Declarations]] for details.&lt;br /&gt;
&lt;br /&gt;
There are several different nodes you can put within the &amp;quot;action&amp;quot; node. &lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;run/job/incoming/timeout&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Put Lua code in the 'run' node that is run immediately when the action is received. This code should be short and quick and return right away because the UPnP Control Point will probably be blocked while it waits for the reply. Also, your Luup device will not do anything else while the 'run' code is executing. If it will take some time to handle the action, use the 'job' tag instead. Job's will run asynchronously, meaning they happen in the background. If you implement this action in a Job, you won't be able to give the result code (ie success/failure) to the UPnP Control Point because the Luup engine gives the Control Point an &amp;quot;action successful&amp;quot; as soon as it creates the job. You can put code in both the 'run' and 'job' tags. In this case, the code in the 'run' tag is run immediately and can return an error condition which is sent back to the UPnP Control Point, or, if it returns 'OK', then Luup will send the 'ok' to the UPnP Control Point and run the job later. The job can take as long as you want. &lt;br /&gt;
&lt;br /&gt;
The UPnP forum did not create an action &amp;quot;on&amp;quot; or &amp;quot;off&amp;quot; for a light switch because they know that it can take some time to actually turn the light on or off, and you don't want to block the UPnP control point waiting for the light to go on or off. So, the UPnP action to turn a light on is called &amp;quot;SetTarget&amp;quot;. The UPnP action tells the control point it executed the action ok simply when it receives the action. If the control point wants to know for sure if the light actually turned on, the control point should watch the 'Status' variable and see if it changes to &amp;quot;1&amp;quot; when the light is actually on. &lt;br /&gt;
&lt;br /&gt;
In the case of the GC100, (see: I_GC100.xml), on/off of the relays happens immediately. So the implementation is inside a 'run' tag, which sets the &amp;quot;Status&amp;quot; variables. In the case of SendProntoCode, the implementation is inside a 'job' tag since the pronto commands may take time. &lt;br /&gt;
&lt;br /&gt;
When you have Lua code inside a 'job' tag, Luup will return 'OK' to the Control Point in response to the action, and then queues up the job and runs the Lua in your 'job' code. The job code returns 2 values:&lt;br /&gt;
&lt;br /&gt;
1) the status of the job&lt;br /&gt;
&lt;br /&gt;
2) how long to wait before the job times out in seconds.&lt;br /&gt;
&lt;br /&gt;
The status can be:&lt;br /&gt;
*0=job_WaitingToStart: In vera's UI a job in this state is displayed as a gray icon. It means it's waiting to start. If you return this value your 'job' code will be run again in the 'timeout' seconds &lt;br /&gt;
*2=job_Error, or 3=job_Aborted: In vera's UI a job in this state is displayed as a red icon. This means the job failed. Your code won't be run again. &lt;br /&gt;
*4=job_Done: In vera's UI a job in this state is displayed as a green icon. This means the job finished ok. Your code won't be run again. &lt;br /&gt;
*5=job_WaitingForCallback: In vera's UI a job in this state is displayed as a moving blue icon. This means the job is running and you're waiting for return data. Any data that comes in while the job is in this state will go to the lua code in the job's 'incoming' tag. If no data comes in before the number of seconds you return in the timeout, then the code in the job's 'timeout' tag is run. &lt;br /&gt;
&lt;br /&gt;
So in the SendProntoCode job code for the I_GC100, we return 5,10 which means we're waiting for data and should wait up to 10 seconds. &lt;br /&gt;
&lt;br /&gt;
While a job is active, a UPnP control point will see the status of the job when it uses the GetStatus action for Vera. In Vera's web ui an active job is shown as an icon next to the device. While Luup is in the middle of executing the Lua code for your job, the status is reported to the control point as 1=job_InProgress. &lt;br /&gt;
&lt;br /&gt;
Whenever your job is in status 5=job_WaitingForCallback, whatever data comes in on the I/O port will be first given to the job's 'incoming' Lua code *before* it's given to the general purpose 'incoming' Lua code (see below). If the incoming data is not for this job, you should not return a 'true' as the 3rd return parameter from 'incoming', and the Luup engine will forward the incoming data to the general purpose 'incoming' Lua code. If you do return 'true', the Luup engine assumes you handled the incoming data and there's nothing more to do with it. See the incoming tag for the SendProntoCode job code for the I_GC100 as an example. The 'incoming' code returns 3 values. The first 2 are the same as for the 'job', and the 3rd is true or false depending on if the incoming data was for this job or not. &lt;br /&gt;
&lt;br /&gt;
The Luup engine will only run one chunk of Lua code at a time. This is necessary to prevent problems with the shared variables. However, there can be multiple jobs in the 5=job_WaitingForCallback state. When one job is run, and it returns the status 5=job_WaitingForCallback, the Luup engine will start another job while the first one is waiting. So when incoming data is received, the Luup engine will pass the data to the 'incoming' Lua code for all the jobs in the 5=job_WaitingForCallback state, in the order the jobs were created. As soon as a job returns 'true' for the 3rd parameter, Luup discards the incoming data. As long as the jobs return 'false' the data is passed on to the next job, and eventually to the general purpose 'incoming' Lua code. If, when you start a job, you do not want other jobs to run until your job is done, call the [[Luup Lua extensions]] lu_block_jobs. Other Luup plugins will continue to run asynchronously, but no other jobs will run for this particular Luup plugin until this job becomes 'done' or 'error/abort'. &lt;br /&gt;
&lt;br /&gt;
[[Luup Declarations]] lists what variables this Lua code receives and what it should return. &lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;incoming&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
This block defines what to do when data is received by the device, if this device is talking on a serial port, ethernet port, or some other generic I/O port. The Luup engine handles low-level I/O for these types of ports so you only need to call one of the [[Luup Lua extensions]] to send data on the port. &lt;br /&gt;
&lt;br /&gt;
This is the general purpose incoming data handler that the Luup engine will call whenever data comes in on an I/O Port. It has a node 'lua' which contains the Lua code that is called. &lt;br /&gt;
&lt;br /&gt;
[[Luup Declarations]] lists what variables this Lua code receives and what it should return. &lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;ir&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Put an ir code in pronto format in this tag. Luup will send a SendProntoCode action to whatever device is specified as the [[Luup IO Device]] in the [[Luup Configuration File]]. &lt;br /&gt;
&lt;br /&gt;
==Walkthrough to create a device==&lt;br /&gt;
&lt;br /&gt;
We have documented step-by-step the detailed process of creating a Luup interface, including everything that was done to debug, for Somfy blind control device (see: [http://www.blindshademotors.com/documents/accessories-special-applications/rs232-to-rts-compatability.pdf]), which is a simple 1-way serial device (ie send data, but don't get any response) here: [[Luup_Somfy_Walkthrough]]&lt;br /&gt;
&lt;br /&gt;
No matter type of Luup plugin you'll be creating the procedure to create it is the same.  This is true for Luup plugins which do not talk to any external hardware, such as a 'Weather' plugin to provide weather services, as well as plugins that talk to infrared devices like a TV, and serial/network devices.&lt;br /&gt;
&lt;br /&gt;
The first step is to create the UPnP device specification and service specification files.  Whenever possible you will want to re-use existing service specification files, as explained in &amp;quot;Introduction to UPnP&amp;quot; [[Luup_Plugins#Introduction_to_UPnP here]].  And if your device is functionally the same as an existing UPnP Device type, like a light switch, you should re-use an existing UPnP Device Specification file also so that a UPnP Control Point will know how to control your device with modification.  A list of all currently known UPnP Device and Service specification files is here: [[Luup_UPNP_Files]].  Find the existing UPnP device file that is most similar to the Luup plugin you are creating, and list all the services that device will implement.  Download these files on your computer either from the [[Luup_UPNP_Files]] page, or by going to Vera's setup page and choosing Devices, Luup plugins.&lt;br /&gt;
&lt;br /&gt;
Modify the UPnP device specification file as needed, such as changing the manufacturer and model.  Do not worry about the UDN tag as Luup will create a UDN for you automatically.  If your device is functionally the same as the template you started from, leave the deviceType the same.  If it's different, modify the deviceType replacing the &amp;quot;schemas-upnp-org&amp;quot; or &amp;quot;micasaverde-com&amp;quot; with some domain that you have, or if you don't have one, just use your name.  Leave the :device: but change the word after it to describe your device.  The deviceType must contain only a-z, 0-9, : and -.&lt;br /&gt;
&lt;br /&gt;
In the '''&amp;lt;serviceList&amp;gt;''' tag, add all the services you will implement.  You can also create new services at this time.  Save your new device file and service files as D_[some name] and S_[some name].  This is not mandated by UPnP, but is a convention we use so it's easy to recognize the file type by the name.&lt;br /&gt;
&lt;br /&gt;
Now refer to &amp;quot;The Luup XML implementation file&amp;quot; section above to learn how to create an implementation file.&lt;br /&gt;
&lt;br /&gt;
To add the device to Vera's configuration file, so Vera will load and use the device, go to the Devices tab and at the bottom fill in the UPnP Device filename in the 'Add Device' box and pick a room.  When you save your changes, which causes the Luup engine to reload the new configuration, it will look for the device, service and implementation files to start the device.  If the files don't already exist on Vera, Vera will log an error and will not start the device.  So you need to upload any new files you created by going to the Devices, Luup plugins page in Vera's setup web page.  You can upload several files at once.  The files will not be used until the Loop engine is reset so you probably want to check the &amp;quot;Restart Luup after upload&amp;quot; box before you click 'go'.  If you upload the files without checking the box and want to restart the Luup engine, just click 'save', even if the button is grayed out.  You will likely need to make several changes to the files before they're right so you can leave one web browser open to the 'Luup plugin' page and just modify the files in your text editor then click 'go' again to re-upload them after saving your changes.  You can open another browser window or tab to access other pages in Vera's web ui and control the device while leaving the list of files to upload on the Luup plugin page intact so you can re-upload by clicking 'go'.&lt;br /&gt;
&lt;br /&gt;
Next you'll want to know how to debug your Luup plugins and Lua code.  See [[Luup_Debugging]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugin_tabs</id>
		<title>Luup plugin tabs</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugin_tabs"/>
				<updated>2013-07-07T01:11:01Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: Updated documentation for check-boxes as currently unusable in UI5&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;br&amp;gt; The '''Tabs''' key in the [[Luup plugins: Static JSON file|static JSON file]] performs two duties in the UI4 HTML interface: &lt;br /&gt;
&lt;br /&gt;
*It controls how the device appears in the dashboard. &lt;br /&gt;
*It controls how each tab appears in the the device's detailed dialog.&lt;br /&gt;
&lt;br /&gt;
The value of the '''Tabs''' key is a JSON array. Each array element is a JSON object (associative array). &lt;br /&gt;
&lt;br /&gt;
= Required tab fields  =&lt;br /&gt;
&lt;br /&gt;
These fields must be present in each JSON object in the tab list: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object containing two keys: '''lang_tag''' (a string) is not displayed in the HTML interface but may be used by localized UIs; '''text''' (a string) is displayed in the tab's handle at the top of the dialog. &lt;br /&gt;
;'''Position''' &lt;br /&gt;
:A string which contains a number matching the relative position of this tab in relation to the other tabs. The leftmost tab has value &amp;quot;0&amp;quot;, the next tab &amp;quot;1&amp;quot;, and so on. &lt;br /&gt;
;'''TabType''' &lt;br /&gt;
:A string which describes how the content of the tab is to be generated. The value may be one of '''javascript''' or '''flash'''. These are described in the following sections.&lt;br /&gt;
&lt;br /&gt;
== TabType javascript  ==&lt;br /&gt;
&lt;br /&gt;
The '''javascript''' tab type executes client-side JavaScript, producing an HTML fragment which forms the body of the tab. These additional keys are required in the tab JSON object: &lt;br /&gt;
&lt;br /&gt;
;'''ScriptName''' &lt;br /&gt;
:The name of the JavaScript file. Conventionally, plugin JavaScript files are named '''J_PluginName.js'''. &lt;br /&gt;
;'''Function''' &lt;br /&gt;
:The function inside the JavaScript file which will be called to produce the HTML.&lt;br /&gt;
&lt;br /&gt;
The JavaScript code has access to most Luup variables through the [[JavaScript API]]. &lt;br /&gt;
&lt;br /&gt;
== TabType flash  ==&lt;br /&gt;
&lt;br /&gt;
The '''flash''' tab type creates a tab body with a simple page-description language. Each component of the page is placed into the body at the position specified. You can't place a '''Control''' on the dashboard unless you place it in a '''ControlGroup''', which in turn must be placed in a '''SceneGroup''': &lt;br /&gt;
&lt;br /&gt;
;'''UI5 SceneGroup'''&lt;br /&gt;
&lt;br /&gt;
''SceneGroup'' is a new type of container, similar to a ControlGroup, but with some key differences: it is customizable (by setting its position and dimensions) and it defines the behavior of the buttons assigned to the same state variable. If two buttons in the same SceneGroup are assigned to the same variable, only one of them can be activated in the scene editor (radio buttons behavior). If two buttons assigned to the same variable are in different SceneGroups, both can be activated at the same time in the scene editor (checkboxes behavior). &lt;br /&gt;
&lt;br /&gt;
It has the following tags: &lt;br /&gt;
&lt;br /&gt;
*'''id''': A numeric value (positive integer numbers). &lt;br /&gt;
*'''top''': The offset from the device header. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''left''': The offset from the device icon. It uses the same units as ''x'' and ''y''. &lt;br /&gt;
*'''x''', '''y''': the dimensions of the SceneGroup. e.g If I have two horizontally aligned buttons in the SceneGroup, ''x'' will be ''2'' and ''y'' will be ''1''.&lt;br /&gt;
&lt;br /&gt;
A ''SceneGroup'' can contain one or more ''ControlGroups''. &lt;br /&gt;
&lt;br /&gt;
;'''ControlGroup''' &lt;br /&gt;
:Required only if the first tab is also used to display elements on the dashboard. See ''Dashboard appearance'' below. &lt;br /&gt;
;'''Control''' &lt;br /&gt;
:An array of JSON objects, each representing a control on the tab. The Control Types are described further below. Each control object in the array can also contain: &lt;br /&gt;
:'''ControlGroup''' - a reference to the control's control group &lt;br /&gt;
:'''top''' - does what? &lt;br /&gt;
:'''left''' - does what?&lt;br /&gt;
&lt;br /&gt;
== Control Types  ==&lt;br /&gt;
&lt;br /&gt;
The '''Control''' array contains JSON objects (associative arrays), one per item in the tab body. An item is either a static text label, a variable, an input field, or a button or slider. This is handled by setting the '''ControlType''' key on the object. &lt;br /&gt;
&lt;br /&gt;
=== ControlType label  ===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''label''', a fixed label is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys: '''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. '''text''' is displayed as the content of the label. In UI5 on the &amp;quot;Dashboard&amp;quot;, the text is rendered by passing it in the &amp;quot;Title&amp;quot; attribute of a Div statement, so it can only be plain text, (as opposed to say HTML). However in the tabs, it is placed in a Div statement so HTML, such as a URL link, can be rendered. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers). This controls where the label is placed in the tab.&lt;br /&gt;
&lt;br /&gt;
=== ControlType variable  ===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''variable''', the contents of a Luup variable are displayed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with six keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the variable's value is placed in the tab), '''Service''' (a string containing the Service Id of the variable to be displayed), and '''Variable''' (a string containing the name of the variable to be displayed).&lt;br /&gt;
&lt;br /&gt;
=== ControlType input  ===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''input''', a text box for the user to type a string is placed in the tab. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''ID''' &lt;br /&gt;
:A string which other controls (buttons) will use to refer to the string that the user has typed into this field. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with four keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers). This controls where the text box is placed in the tab.&lt;br /&gt;
&lt;br /&gt;
=== ControlType button  ===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''button''', a clickable button is placed at the specified coordinates. The button will appear selected (depressed) or unselected based on the value of a variable, permitting sets of buttons to act as radio buttons. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Label''' &lt;br /&gt;
:A JSON object with two keys: '''lang_tag''' is not displayed but can be used by localizing interfaces to translate the label text. '''text''' is displayed as the text inside the button. &lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with up to seven keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), and (optionally) '''Value''' (a string, &amp;quot;1&amp;quot; if omitted). The specified variable's value controls whether the button appears selected (if the variable matches '''Value''') or not selected (if the variable doesn't match). &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching an input control on the page (for a user-entered parameter value).&lt;br /&gt;
&lt;br /&gt;
=== ControlType slider  ===&lt;br /&gt;
&lt;br /&gt;
With '''ControlType''' set to '''slider''', a draggable slider is placed at the specified coordinates. The following additional keys must also be provided: &lt;br /&gt;
&lt;br /&gt;
;'''Display''' &lt;br /&gt;
:A JSON object with nine keys: '''Top''', '''Left''', '''Width''', '''Height''' (all strings containing numbers, controlling where the button is placed in the tab), '''Service''' (a string containing the Service Id of a variable), and '''Variable''' (a string containing the name of a variable), '''MinValue''' and '''MaxValue''' (both strings containing numbers, controlling what range of values the slider takes) and '''ID''' (a string, used in the '''Command''' to refer to the position the user has dragged the slider to). The specified variable's value is used to display the slider's current position. &lt;br /&gt;
;'''Command''' &lt;br /&gt;
:A JSON object with three keys: '''Service''' (a string containing the service Id of an action that this device can perform), '''Action''' (a string containing the name of the action), and '''Parameters''' (a JSON array of JSON objects, forming the parameters to pass to the action). Each parameter object has a key '''Name''' for the parameter name, and either a key '''Value''' (for a fixed parameter value) or a key '''ID''' matching the slider's ID (for a value matching what the user has dragged the slider to). &lt;br /&gt;
;'''ControlHeader''' &lt;br /&gt;
:Optional. If present, must have string value &amp;quot;1&amp;quot;. When set, the slider's label and value are displayed. Otherwise only the slider is shown. (TODO: This is from reverse-engineering the JavaScript code. Test this.)&lt;br /&gt;
&lt;br /&gt;
=== ControlType slider_vertical  ===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_Heater1.json, D_HVAC_ZoneThermostat1.json&lt;br /&gt;
&lt;br /&gt;
=== ControlType checkbox  ===&lt;br /&gt;
&lt;br /&gt;
:As of UI5 firmware 1.5.622, checkboxes are only half-baked and not usable in &amp;quot;flash&amp;quot; style tabs. If you need check-boxes, radio buttons, pop-up menus, etc. in your tab, you need to create a Javascript tab.&lt;br /&gt;
&lt;br /&gt;
=== ControlType image  ===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
=== ControlType image_player  ===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
=== ControlType js_button  ===&lt;br /&gt;
&lt;br /&gt;
:Used in: D_DigitalSecurityCamera1.json&lt;br /&gt;
&lt;br /&gt;
= Dashboard appearance  =&lt;br /&gt;
&lt;br /&gt;
The first array element in the '''Tabs''' field is special. If it is of type '''flash''' then a subset of the controls on the tab can be chosen to display on the UI4 dashboard.&lt;br /&gt;
&lt;br /&gt;
== Control Groups  ==&lt;br /&gt;
&lt;br /&gt;
The dashboard box for a device has room for two rows of information and controls. These are called ''Control Groups''.&lt;br /&gt;
&lt;br /&gt;
The tab object must contain the key '''ControlGroup''', which specifies how the dashboard's two available rows of information are to be handled. '''ControlGroup''' is a JSON array containing JSON objects (associative arrays). Each object has the following keys:&lt;br /&gt;
&lt;br /&gt;
;'''id''' &lt;br /&gt;
:A string containing a number, &amp;quot;1&amp;quot; for the first object, &amp;quot;2&amp;quot; for the second, and so on. &lt;br /&gt;
;'''isSingle''' &lt;br /&gt;
:UI4: If this key is present and contains the value &amp;quot;1&amp;quot;, then this control group is combined with other control groups that have '''isSingle''' similarly set onto the same row. For an example of this, see the On/Off buttons of the Binary Switch and Dimmable Switch static JSON files. &lt;br /&gt;
:UI5: Since controls cannot be grouped in drop-down lists anymore, this tag has a different role in UI5. When isSingle is &amp;quot;1&amp;quot;, the buttons in the ControlGroup will have a background (e.g. the BinaryLight buttons). If isSingle is missing, the buttons won't have a background. That is the button shape is not shown; only the button text will show. &lt;br /&gt;
;'''type''' &lt;br /&gt;
:If this key is present and contains the value &amp;quot;info&amp;quot;, then the controls displayed on this row are not clickable. If this key is absent, buttons may be used in the control group.&lt;br /&gt;
&lt;br /&gt;
== Making a control appear on the dashboard  ==&lt;br /&gt;
&lt;br /&gt;
All control types (label, variable, button, input, slider) can appear in the dashboard. However, by default, controls in the first tab's detail view do not appear in the dashboard. To make a control's text appear in the dashboard, add a '''ControlGroup''' to the control's JSON object. The value is a string containing a number, matching the '''id''' of one of the Tab's overall '''ControlGroup''' key. The dashboard will concatenate all of a control group's controls in the order they are presented in the '''Control''' array, and display them in a single row of text. &lt;br /&gt;
&lt;br /&gt;
== Keeping controls together  ==&lt;br /&gt;
&lt;br /&gt;
Some controls may not make sense if presented individually (for instance, an input for an alarm PIN and a &amp;quot;disarm&amp;quot; button). To keep these controls together, add a '''ControlPair''' key to all of the controls that have to appear together. Use the same value (a number in a string) for all of them. &lt;br /&gt;
&lt;br /&gt;
[[Category:Development]]&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_Debugging</id>
		<title>Luup Debugging</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_Debugging"/>
				<updated>2013-06-18T07:20:54Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
This document assumes you have already read [[Luup_Intro]] for a general introduction to Luup, [[Luup_Plugins]] to learn what goes into a plugin, and that you are creating the Luup plugin using either the web-based Luup plugin generator (coming soon) or are creating Luup's XML files by hand as described here: [[Luup_Plugins_ByHand]].  This document will help you debug the Lua code that you write and see what's going on with your plugins.&lt;br /&gt;
&lt;br /&gt;
To learn about Lua, see [http://www.lua.org/ lua.org] and the [http://www.lua.org/manual/5.1/ Lua reference manual] which lists all the functions and variables that are built into Lua.  &lt;br /&gt;
&lt;br /&gt;
The [http://lua-users.org/wiki/TutorialDirectory Lua Tutorial], on [http://Lua-Users.org lua-users.org], can also be of great help here.&lt;br /&gt;
&lt;br /&gt;
Additionally, the Luup engine provides a set of [[Luup_Lua_extensions|Extensions]] (variables and functions) you can call &lt;br /&gt;
within your Lua code.&lt;br /&gt;
&lt;br /&gt;
== Third party tools ==&lt;br /&gt;
&lt;br /&gt;
There are tools that will aid you in debugging your plugin:&lt;br /&gt;
&lt;br /&gt;
*'''putty''' If you are using Microsoft Windows you can download a utility, putty, to login to Vera's console here: [http://the.earth.li/~sgtatham/putty/latest/x86/putty.exe]  You don't need to install putty.  Just put the .exe file on your desktop, run it, and in the host name type in the IP address of Vera and click 'open'.  At the ''&amp;lt;tt&amp;gt;login as:&amp;lt;/tt&amp;gt;'' prompt enter the username: ''&amp;lt;tt&amp;gt;root&amp;lt;/tt&amp;gt;'' and then when prompted enter the root password you setup.  If you don't see the root password or need help logging in see [[Logon_Vera_SSH#Can.27t_find_the_root_password.3F|Logon # Can't find the root password?]].&lt;br /&gt;
&lt;br /&gt;
*'''DeviceSpy / Intel Tools for UPnP''' This is a set of tools for Microsoft Windows you can download here: [http://www.intel.com/cd/ids/developer/asmo-na/eng/downloads/upnp/tools/218896.htm?desturl=http://softwarecommunity.intel.com/isn/downloads/zips/IntelToolsForUPnPTechnology.zip]  We use the utility called ''DeviceSpy''&lt;br /&gt;
&lt;br /&gt;
== Luup logs ==&lt;br /&gt;
Vera is running Linux, and you can login to Vera's console to monitor the logs.  A special user, called ''root'', is used to login to Vera.  The password setup mechanism is different between Vera1 or a Vera2 models.&lt;br /&gt;
&lt;br /&gt;
=== Logging in on Vera1 ===&lt;br /&gt;
The ''root'' password must first be set in Vera's configuration menu's.  Do this by either going to Vera's Advanced, Net &amp;amp; Wi-fi tab and clicking 'Advanced configuration'&lt;br /&gt;
&lt;br /&gt;
Alternatively, at a Windows prompt, you can type ''telnet vera_ip'' (where vera_ip is the IP address of Vera).  If you login with telnet, type: ''passwd'' and press enter to set a root password.&lt;br /&gt;
&lt;br /&gt;
=== Logging in on Vera2 and Vera3 ===&lt;br /&gt;
The ''root'' password for a Vera2 or Vera3 box is printed on a label on the underside of the box.&lt;br /&gt;
&lt;br /&gt;
=== Getting around  ===&lt;br /&gt;
&lt;br /&gt;
If you have a Mac or Linux PC you can now login directly to Vera using ''ssh''. From the console type: ''ssh root@vera_ip'' (where vera_ip is the IP address of Vera). If you are using Windows, use putty as described above. &lt;br /&gt;
&lt;br /&gt;
When you are at the ''root@HomeControl:~#'' prompt type in: &lt;br /&gt;
&lt;br /&gt;
  cd /var/log/cmh''&lt;br /&gt;
&lt;br /&gt;
to go to the log directory. The command: &lt;br /&gt;
&lt;br /&gt;
  ls -lh&lt;br /&gt;
&lt;br /&gt;
lists all the log files. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;LuaUPnP.log&amp;lt;/tt&amp;gt; is the main log file. To see everything that is being logged by Luup in real time is called &amp;quot;tailing the log&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
Do this by typing: &lt;br /&gt;
&lt;br /&gt;
  tail -f LuaUPnP.log&lt;br /&gt;
&lt;br /&gt;
To stop following the log press '''Ctrl+C'''. Press the up arrow to scroll through the recent commands to run them again by hitting enter, without retyping them. &lt;br /&gt;
&lt;br /&gt;
Note that Vera doesn't have a lot of memory for the logs. So regularly you will see the logs say 'Going to rotate logs' and then stop. This means the memory filled up so the logs are being removed. If the check box in Vera's Advanced, Logs page &amp;quot;Archive old logs on findvera (recommended)&amp;quot; is checked, the logs will be sent to Mi Casa Verde's server and archived for 7 days before purging. &lt;br /&gt;
&lt;br /&gt;
TODO: We'll add instructions for logging to a USB memory stick so you have more room. &lt;br /&gt;
&lt;br /&gt;
Each line starts with the log level and the date time, such as: &lt;br /&gt;
&lt;br /&gt;
  02      06/12/09 18:50:15.254  &lt;br /&gt;
&lt;br /&gt;
The first number, log level, has this meaning: &lt;br /&gt;
&lt;br /&gt;
*01 - Critical error. Something is wrong that shouldn't happen. &lt;br /&gt;
*02 - Warning. This is something to make note of, though it's not always a problem. &lt;br /&gt;
*03 - StartStop. These log messages indicate Luup engine is starting/stopping. This happens every time you save configuration changes. &lt;br /&gt;
*04 - Job. This relates to 'jobs'. &lt;br /&gt;
*05 - Home Automation. These logs are status messages from Home Automation devices. &lt;br /&gt;
*06 - Variable. A UPnP Variable has changed. &lt;br /&gt;
*07 - Event. An event is triggered. This is what you attach to a scene. &lt;br /&gt;
*08 - Action. A UPnP Action was received. &lt;br /&gt;
*09 - Enumeration. When the Luup engine starts this lists all the devices in the system. &lt;br /&gt;
*10 - General Status. There are lots of these messages to indicate something happening in the system. &lt;br /&gt;
*41 - Outgoing data. This is all data going to the external devices, such as the Z-Wave dongle, in their raw form. This is also true for your Luup plugins talking to the serial/network devices too. &lt;br /&gt;
*42 - Incoming data. (as above) &lt;br /&gt;
*50 - Lua code. When you log something in Lua code using the &amp;lt;tt&amp;gt;luup.log&amp;lt;/tt&amp;gt; function it has this log level. &lt;br /&gt;
*51 - Raw outgoing Serial data, from any Serial port attached to the Plugin. &lt;br /&gt;
*52 - Raw incoming Serial data, from any Serial port attached to the Plugin. &lt;br /&gt;
*61 - Outgoing Insteon data&lt;br /&gt;
*62 - Incoming Insteon data&lt;br /&gt;
*200 - UPnP message. There are a huge amount of logs generated from the UPnP engine showing all activity.&lt;br /&gt;
&lt;br /&gt;
By default the Luup engine will skip logs with status 10 and 200 because they generate such a large amount of activity. Vera can actually be less reliable when 10 and 200 are activated because the logs grow so quickly sometimes that it is hard to archive them as fast as they come in. &lt;br /&gt;
&lt;br /&gt;
You can see what logs will shown by typing: &lt;br /&gt;
&lt;br /&gt;
  cat /etc/cmh/cmh.conf&lt;br /&gt;
&lt;br /&gt;
By default you will see: &lt;br /&gt;
&lt;br /&gt;
  LogLevels = 1,2,3,4,5,6,7,8,9,50&lt;br /&gt;
&lt;br /&gt;
meaning the Luup engine will log those log levels and ignore the rest. When you click 'Verbose logging' in Vera's web UI under setup, logs, you will see: &lt;br /&gt;
&lt;br /&gt;
  #LogLevels = 1,2,3,4,5,6,7,8,9,50&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;#&amp;lt;/tt&amp;gt; in front means the log levels are ignored and all logs are stored, except UPnP. If you really want to see the UPnP messages, which will fill up the memory very fast, you can type &lt;br /&gt;
&lt;br /&gt;
  vi /etc/cmh/cmh.conf&lt;br /&gt;
&lt;br /&gt;
and move the cursor on top of the 0 in LogUPnP = 0. Then press ''r1'' to replace the 0 with a 1, and then type '':wq'' to write the changes and quit. These somewhat arcane commands are part of Linux's default text editor, vi. After you make a change, you will want to restart the Luup engine. Do this by clicking the 'save' button. Because you normally just want full logging temporarily, Luup will automatically revert to the default log levels after 4 hours unless you check 'Lock log levels' in Vera's Advanced, Log page. &lt;br /&gt;
&lt;br /&gt;
To make sense of what's in the logs it's best to use grep and 'regular expressions'. If you're a true uber geek this is the greatest joy. If you're a mere mortal, it's best to just stick to some examples. In regular expressions the symbol ^ means 'show me lines that start with' and the symbol \| means 'or'. So here are some samples: &lt;br /&gt;
&lt;br /&gt;
Show me everything in the log that is a 'critical error': &lt;br /&gt;
&lt;br /&gt;
  grep '^01' LuaUPnP.log&lt;br /&gt;
&lt;br /&gt;
I enabled Verbose logging, so incoming/outgoing data is logged, show me that plus any normal log messages (0-9) plus any log messages in my Lua code: &lt;br /&gt;
&lt;br /&gt;
  grep '^0\|^4\|^5' LuaUPnP.log&lt;br /&gt;
&lt;br /&gt;
(meaning any line that starts with 0, which 0-9, or 4, which is 40 and 41, or 5, which is 50. &lt;br /&gt;
&lt;br /&gt;
I want the same data, but I want to follow the logs in real time with the same filter: &lt;br /&gt;
&lt;br /&gt;
  tail -f LuaUPnP.log | grep '^0\|^4\|^5'&lt;br /&gt;
&lt;br /&gt;
You can also put in the 'grep' strings that you want to match. Let's say you see in the logs that every time a job status changes it logs using log level 10 (status) some line that includes the string: 'Job::m_eJobStatus'. You want to follow the logs and show just Lua logs, critical errors, and job status changes: &lt;br /&gt;
&lt;br /&gt;
  tail -f LuaUPnP.log | grep '^5\|^01\|Job::m_eJobStatus'&lt;br /&gt;
&lt;br /&gt;
When you are writing your Lua code, include copious logging with &amp;lt;tt&amp;gt;luup.log&amp;lt;/tt&amp;gt; (see [[Luup Lua extensions#function:_log|Luup_Lua_extensions log function]]) to see what's happening in your Lua code.&lt;br /&gt;
&lt;br /&gt;
==Debug in small chunks without restarting each time==&lt;br /&gt;
&lt;br /&gt;
If you are adding code for an action, it takes time to upload your files and initiate the action.  So it's best to debug the Lua code in tiny bite sized pieces first to be sure the syntax and basic operation is ok.&lt;br /&gt;
&lt;br /&gt;
In Vera's web ui choose Devices, Luup plugins and click 'Test Lua UPnP code'.  For the device number put in the number of the device you are debugging, which is shown when you click the + button next to the device.  Or, if your Lua code isn't going to be calling any of the functions and variables you created in that device, you can just put in a device number of 0, which means this snippet of code runs by itself.  If you put in a valid device number for a Luup plugin, it runs in the context of that device meaning it can call the other functions and variables in the plugin.&lt;br /&gt;
&lt;br /&gt;
Put your small chunk of code in the input box and click 'go' to run it.  Be sure the code outputs something to the log with &amp;lt;tt&amp;gt;luup.log&amp;lt;/tt&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
In another console or putty window you can be following the logs with:&lt;br /&gt;
&lt;br /&gt;
  tail -f LuaUPnP.log | grep '^01\|^5'&lt;br /&gt;
&lt;br /&gt;
Always log critical errors, &amp;lt;tt&amp;gt;01&amp;lt;/tt&amp;gt;, because that way if your Lua code won't run due to a syntax error you will see the log entry.&lt;br /&gt;
&lt;br /&gt;
In the real implementation your Lua code will probably be passed variables.  For example, if the Lua code being run is in response to an action, it will be passed as a variable the arguments to the UPnP action.  To test your small blocks of code with this you can simply add the same variable names and hardcode some test variables.  The list of variables that are passed to your Lua code are documents here: [[Luup_Declarations]].  You can also see the complete Lua code that the Luup engine has generated including the function declarations with the &amp;lt;tt&amp;gt;lu_lua&amp;lt;/tt&amp;gt; (See [[Luup_Requests#lu_lua|Luup_Requests lu_lua]]) request on the URL.&lt;br /&gt;
&lt;br /&gt;
For example, I will view the complete Lua code for device #8: &lt;br /&gt;
&lt;br /&gt;
  http://vera_ip:49451/data_request?id=lu_lua&amp;amp;DeviceNum=8&lt;br /&gt;
&lt;br /&gt;
and I can see that the Lua code I put in my 'run' XML tag in the implementation file for the &amp;lt;tt&amp;gt;SetTarget&amp;lt;/tt&amp;gt; action looks like this is within this function declaration:&lt;br /&gt;
&lt;br /&gt;
  function SetTarget_run(lul_device,lul_settings)&lt;br /&gt;
&lt;br /&gt;
As explained in [[Luup_Declarations]] the &amp;lt;tt&amp;gt;lul_device&amp;lt;/tt&amp;gt; will the device number that is receiving the command.  So if I want to test some Lua code that I will put inside a 'run' tag for the &amp;lt;tt&amp;gt;SetTarget&amp;lt;/tt&amp;gt; action, which logs the device that received the UPnP Action (we'll use 10) and the argument &amp;lt;tt&amp;gt;newTargetValue&amp;lt;/tt&amp;gt; which indicates if the device is being turned on or off, then I would put in the test code:&lt;br /&gt;
&lt;br /&gt;
  lul_device=10&lt;br /&gt;
  lul_settings['newTargetValue']=1&lt;br /&gt;
  luup.log(&amp;quot;Got a SetTarget action for device &amp;quot; .. lul_device .. &amp;quot; to turn on/off: &amp;quot; .. lul_settings['newTargetValue'])&lt;br /&gt;
&lt;br /&gt;
now when you click 'go' see what goes in the Log.  If your log entry is ok, copy the &amp;lt;tt&amp;gt;luup.log&amp;lt;/tt&amp;gt; line from this test code, without the &amp;lt;tt&amp;gt;lul_device=&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;lul_settings=&amp;lt;/tt&amp;gt; lines, into the actual implementation and you know that it will work when the real UPnP action is called and the Luup engine passes the actual &amp;lt;tt&amp;gt;lul_device&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;lul_settings&amp;lt;/tt&amp;gt; to your Lua code.&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_Debugging</id>
		<title>Luup Debugging</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_Debugging"/>
				<updated>2013-06-18T07:18:29Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
This document assumes you have already read [[Luup_Intro]] for a general introduction to Luup, [[Luup_Plugins]] to learn what goes into a plugin, and that you are creating the Luup plugin using either the web-based Luup plugin generator (coming soon) or are creating Luup's XML files by hand as described here: [[Luup_Plugins_ByHand]].  This document will help you debug the Lua code that you write and see what's going on with your plugins.&lt;br /&gt;
&lt;br /&gt;
To learn about Lua, see [http://www.lua.org/ lua.org] and the [http://www.lua.org/manual/5.1/ Lua reference manual] which lists all the functions and variables that are built into Lua.  &lt;br /&gt;
&lt;br /&gt;
The [http://lua-users.org/wiki/TutorialDirectory Lua Tutorial], on [http://Lua-Users.org lua-users.org], can also be of great help here.&lt;br /&gt;
&lt;br /&gt;
Additionally, the Luup engine provides a set of [[Luup_Lua_extensions|Extensions]] (variables and functions) you can call &lt;br /&gt;
within your Lua code.&lt;br /&gt;
&lt;br /&gt;
== Third party tools ==&lt;br /&gt;
&lt;br /&gt;
There are tools that will aid you in debugging your plugin:&lt;br /&gt;
&lt;br /&gt;
*'''putty''' If you are using Microsoft Windows you can download a utility, putty, to login to Vera's console here: [http://the.earth.li/~sgtatham/putty/latest/x86/putty.exe]  You don't need to install putty.  Just put the .exe file on your desktop, run it, and in the host name type in the IP address of Vera and click 'open'.  At the ''&amp;lt;tt&amp;gt;login as:&amp;lt;/tt&amp;gt;'' prompt enter the username: ''&amp;lt;tt&amp;gt;root&amp;lt;/tt&amp;gt;'' and then when prompted enter the root password you setup.  If you don't see the root password or need help logging in see [[Logon_Vera_SSH#Can.27t_find_the_root_password.3F|Logon # Can't find the root password?]].&lt;br /&gt;
&lt;br /&gt;
*'''DeviceSpy / Intel Tools for UPnP''' This is a set of tools for Microsoft Windows you can download here: [http://www.intel.com/cd/ids/developer/asmo-na/eng/downloads/upnp/tools/218896.htm?desturl=http://softwarecommunity.intel.com/isn/downloads/zips/IntelToolsForUPnPTechnology.zip]  We use the utility called ''DeviceSpy''&lt;br /&gt;
&lt;br /&gt;
== Luup logs ==&lt;br /&gt;
Vera is running Linux, and you can login to Vera's console to monitor the logs.  A special user, called ''root'', is used to login to Vera.  The password setup mechanism is different between Vera1 or a Vera2 models.&lt;br /&gt;
&lt;br /&gt;
=== Logging in on Vera1 ===&lt;br /&gt;
The ''root'' password must first be set in Vera's configuration menu's.  Do this by either going to Vera's Advanced, Net &amp;amp; Wi-fi tab and clicking 'Advanced configuration'&lt;br /&gt;
&lt;br /&gt;
Alternatively, at a Windows prompt, you can type ''telnet vera_ip'' (where vera_ip is the IP address of Vera).  If you login with telnet, type: ''passwd'' and press enter to set a root password.&lt;br /&gt;
&lt;br /&gt;
=== Logging in on Vera2 and Vera3 ===&lt;br /&gt;
The ''root'' password for a Vera2 or Vera3 box is printed on a label on the underside of the box.&lt;br /&gt;
&lt;br /&gt;
=== Getting around  ===&lt;br /&gt;
&lt;br /&gt;
If you have a Mac or Linux PC you can now login directly to Vera using ''ssh''. From the console type: ''ssh root@vera_ip'' (where vera_ip is the IP address of Vera). If you are using Windows, use putty as described above. &lt;br /&gt;
&lt;br /&gt;
When you are at the ''root@HomeControl:~#'' prompt type in: &lt;br /&gt;
&lt;br /&gt;
  cd /var/log/cmh''&lt;br /&gt;
&lt;br /&gt;
to go to the log directory. The command: &lt;br /&gt;
&lt;br /&gt;
  ls -lh&lt;br /&gt;
&lt;br /&gt;
lists all the log files. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;LuaUPnP.log&amp;lt;/tt&amp;gt; is the main log file. To see everything that is being logged by Luup in real time is called &amp;quot;tailing the log&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
Do this by typing: &lt;br /&gt;
&lt;br /&gt;
  tail -f LuaUPnP.log&lt;br /&gt;
&lt;br /&gt;
To stop following the log press '''Ctrl+C'''. Press the up arrow to scroll through the recent commands to run them again by hitting enter, without retyping them. &lt;br /&gt;
&lt;br /&gt;
Note that Vera doesn't have a lot of memory for the logs. So regularly you will see the logs say 'Going to rotate logs' and then stop. This means the memory filled up so the logs are being removed. If the check box in Vera's Advanced, Logs page &amp;quot;Archive old logs on findvera (recommended)&amp;quot; is checked, the logs will be sent to Mi Casa Verde's server and archived for 7 days before purging. &lt;br /&gt;
&lt;br /&gt;
TODO: We'll add instructions for logging to a USB memory stick so you have more room. &lt;br /&gt;
&lt;br /&gt;
Each line starts with the log level and the date time, such as: &lt;br /&gt;
&lt;br /&gt;
  02      06/12/09 18:50:15.254  &lt;br /&gt;
&lt;br /&gt;
The first number, log level, has this meaning: &lt;br /&gt;
&lt;br /&gt;
*01 - Critical error. Something is wrong that shouldn't happen. &lt;br /&gt;
*02 - Warning. This is something to make note of, though it's not always a problem. &lt;br /&gt;
*03 - StartStop. These log messages indicate Luup engine is starting/stopping. This happens every time you save configuration changes. &lt;br /&gt;
*04 - Job. This relates to 'jobs'. &lt;br /&gt;
*05 - Home Automation. These logs are status messages from Home Automation devices. &lt;br /&gt;
*06 - Variable. A UPnP Variable has changed. &lt;br /&gt;
*07 - Event. An event is triggered. This is what you attach to a scene. &lt;br /&gt;
*08 - Action. A UPnP Action was received. &lt;br /&gt;
*09 - Enumeration. When the Luup engine starts this lists all the devices in the system. &lt;br /&gt;
*10 - General Status. There are lots of these messages to indicate something happening in the system. &lt;br /&gt;
*41 - Outgoing data. This is all data going to the external devices, such as the Z-Wave dongle, in their raw form. This is also true for your Luup plugins talking to the serial/network devices too. &lt;br /&gt;
*42 - Incoming data. (as above) &lt;br /&gt;
*50 - Lua code. When you log something in Lua code using the &amp;lt;tt&amp;gt;luup.log&amp;lt;/tt&amp;gt; function it has this log level. &lt;br /&gt;
*51 - Raw outgoing Serial data, from any Serial port attached to the Plugin. &lt;br /&gt;
*52 - Raw incoming Serial data, from any Serial port attached to the Plugin. &lt;br /&gt;
*200 - UPnP message. There are a huge amount of logs generated from the UPnP engine showing all activity.&lt;br /&gt;
&lt;br /&gt;
By default the Luup engine will skip logs with status 10 and 200 because they generate such a large amount of activity. Vera can actually be less reliable when 10 and 200 are activated because the logs grow so quickly sometimes that it is hard to archive them as fast as they come in. &lt;br /&gt;
&lt;br /&gt;
You can see what logs will shown by typing: &lt;br /&gt;
&lt;br /&gt;
  cat /etc/cmh/cmh.conf&lt;br /&gt;
&lt;br /&gt;
By default you will see: &lt;br /&gt;
&lt;br /&gt;
  LogLevels = 1,2,3,4,5,6,7,8,9,50&lt;br /&gt;
&lt;br /&gt;
meaning the Luup engine will log those log levels and ignore the rest. When you click 'Verbose logging' in Vera's web UI under setup, logs, you will see: &lt;br /&gt;
&lt;br /&gt;
  #LogLevels = 1,2,3,4,5,6,7,8,9,50&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;#&amp;lt;/tt&amp;gt; in front means the log levels are ignored and all logs are stored, except UPnP. If you really want to see the UPnP messages, which will fill up the memory very fast, you can type &lt;br /&gt;
&lt;br /&gt;
  vi /etc/cmh/cmh.conf&lt;br /&gt;
&lt;br /&gt;
and move the cursor on top of the 0 in LogUPnP = 0. Then press ''r1'' to replace the 0 with a 1, and then type '':wq'' to write the changes and quit. These somewhat arcane commands are part of Linux's default text editor, vi. After you make a change, you will want to restart the Luup engine. Do this by clicking the 'save' button. Because you normally just want full logging temporarily, Luup will automatically revert to the default log levels after 4 hours unless you check 'Lock log levels' in Vera's Advanced, Log page. &lt;br /&gt;
&lt;br /&gt;
To make sense of what's in the logs it's best to use grep and 'regular expressions'. If you're a true uber geek this is the greatest joy. If you're a mere mortal, it's best to just stick to some examples. In regular expressions the symbol ^ means 'show me lines that start with' and the symbol \| means 'or'. So here are some samples: &lt;br /&gt;
&lt;br /&gt;
Show me everything in the log that is a 'critical error': &lt;br /&gt;
&lt;br /&gt;
  grep '^01' LuaUPnP.log&lt;br /&gt;
&lt;br /&gt;
I enabled Verbose logging, so incoming/outgoing data is logged, show me that plus any normal log messages (0-9) plus any log messages in my Lua code: &lt;br /&gt;
&lt;br /&gt;
  grep '^0\|^4\|^5' LuaUPnP.log&lt;br /&gt;
&lt;br /&gt;
(meaning any line that starts with 0, which 0-9, or 4, which is 40 and 41, or 5, which is 50. &lt;br /&gt;
&lt;br /&gt;
I want the same data, but I want to follow the logs in real time with the same filter: &lt;br /&gt;
&lt;br /&gt;
  tail -f LuaUPnP.log | grep '^0\|^4\|^5'&lt;br /&gt;
&lt;br /&gt;
You can also put in the 'grep' strings that you want to match. Let's say you see in the logs that every time a job status changes it logs using log level 10 (status) some line that includes the string: 'Job::m_eJobStatus'. You want to follow the logs and show just Lua logs, critical errors, and job status changes: &lt;br /&gt;
&lt;br /&gt;
  tail -f LuaUPnP.log | grep '^5\|^01\|Job::m_eJobStatus'&lt;br /&gt;
&lt;br /&gt;
When you are writing your Lua code, include copious logging with &amp;lt;tt&amp;gt;luup.log&amp;lt;/tt&amp;gt; (see [[Luup Lua extensions#function:_log|Luup_Lua_extensions log function]]) to see what's happening in your Lua code.&lt;br /&gt;
&lt;br /&gt;
==Debug in small chunks without restarting each time==&lt;br /&gt;
&lt;br /&gt;
If you are adding code for an action, it takes time to upload your files and initiate the action.  So it's best to debug the Lua code in tiny bite sized pieces first to be sure the syntax and basic operation is ok.&lt;br /&gt;
&lt;br /&gt;
In Vera's web ui choose Devices, Luup plugins and click 'Test Lua UPnP code'.  For the device number put in the number of the device you are debugging, which is shown when you click the + button next to the device.  Or, if your Lua code isn't going to be calling any of the functions and variables you created in that device, you can just put in a device number of 0, which means this snippet of code runs by itself.  If you put in a valid device number for a Luup plugin, it runs in the context of that device meaning it can call the other functions and variables in the plugin.&lt;br /&gt;
&lt;br /&gt;
Put your small chunk of code in the input box and click 'go' to run it.  Be sure the code outputs something to the log with &amp;lt;tt&amp;gt;luup.log&amp;lt;/tt&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
In another console or putty window you can be following the logs with:&lt;br /&gt;
&lt;br /&gt;
  tail -f LuaUPnP.log | grep '^01\|^5'&lt;br /&gt;
&lt;br /&gt;
Always log critical errors, &amp;lt;tt&amp;gt;01&amp;lt;/tt&amp;gt;, because that way if your Lua code won't run due to a syntax error you will see the log entry.&lt;br /&gt;
&lt;br /&gt;
In the real implementation your Lua code will probably be passed variables.  For example, if the Lua code being run is in response to an action, it will be passed as a variable the arguments to the UPnP action.  To test your small blocks of code with this you can simply add the same variable names and hardcode some test variables.  The list of variables that are passed to your Lua code are documents here: [[Luup_Declarations]].  You can also see the complete Lua code that the Luup engine has generated including the function declarations with the &amp;lt;tt&amp;gt;lu_lua&amp;lt;/tt&amp;gt; (See [[Luup_Requests#lu_lua|Luup_Requests lu_lua]]) request on the URL.&lt;br /&gt;
&lt;br /&gt;
For example, I will view the complete Lua code for device #8: &lt;br /&gt;
&lt;br /&gt;
  http://vera_ip:49451/data_request?id=lu_lua&amp;amp;DeviceNum=8&lt;br /&gt;
&lt;br /&gt;
and I can see that the Lua code I put in my 'run' XML tag in the implementation file for the &amp;lt;tt&amp;gt;SetTarget&amp;lt;/tt&amp;gt; action looks like this is within this function declaration:&lt;br /&gt;
&lt;br /&gt;
  function SetTarget_run(lul_device,lul_settings)&lt;br /&gt;
&lt;br /&gt;
As explained in [[Luup_Declarations]] the &amp;lt;tt&amp;gt;lul_device&amp;lt;/tt&amp;gt; will the device number that is receiving the command.  So if I want to test some Lua code that I will put inside a 'run' tag for the &amp;lt;tt&amp;gt;SetTarget&amp;lt;/tt&amp;gt; action, which logs the device that received the UPnP Action (we'll use 10) and the argument &amp;lt;tt&amp;gt;newTargetValue&amp;lt;/tt&amp;gt; which indicates if the device is being turned on or off, then I would put in the test code:&lt;br /&gt;
&lt;br /&gt;
  lul_device=10&lt;br /&gt;
  lul_settings['newTargetValue']=1&lt;br /&gt;
  luup.log(&amp;quot;Got a SetTarget action for device &amp;quot; .. lul_device .. &amp;quot; to turn on/off: &amp;quot; .. lul_settings['newTargetValue'])&lt;br /&gt;
&lt;br /&gt;
now when you click 'go' see what goes in the Log.  If your log entry is ok, copy the &amp;lt;tt&amp;gt;luup.log&amp;lt;/tt&amp;gt; line from this test code, without the &amp;lt;tt&amp;gt;lul_device=&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;lul_settings=&amp;lt;/tt&amp;gt; lines, into the actual implementation and you know that it will work when the real UPnP action is called and the Luup engine passes the actual &amp;lt;tt&amp;gt;lul_device&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;lul_settings&amp;lt;/tt&amp;gt; to your Lua code.&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugins:_Static_JSON_file</id>
		<title>Luup plugins: Static JSON file</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugins:_Static_JSON_file"/>
				<updated>2013-06-09T16:58:08Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: Delete extra whitespace&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
&lt;br /&gt;
Starting in UI4, Luup plugins can specify a ''static JSON'' file.  This file describes how the plugin appears in the web interface.&lt;br /&gt;
&lt;br /&gt;
=What the static JSON file controls=&lt;br /&gt;
&lt;br /&gt;
* The icon used by the plugin in the dashboard.&lt;br /&gt;
* The text that displays in the one-to-two-row status message in the dashboard.&lt;br /&gt;
* Whether the icon changes depending on the value of a variable in the device (for example, a binary light's icon changes from lit to unlit).&lt;br /&gt;
* The tabs that appear in the device's detail dialog (when you click the wrench/spanner).&lt;br /&gt;
* The content of these tabs.&lt;br /&gt;
* The triggers or events that are available for the device in the &amp;quot;Triggers&amp;quot; or &amp;quot;Events&amp;quot; tag of a scene.&lt;br /&gt;
&lt;br /&gt;
=Referencing the static JSON file from the Device XML file=&lt;br /&gt;
&lt;br /&gt;
The device XML file (customarly '''D_PluginName.xml''') contains a reference to the static JSON file, in the '''staticJson''' element.  Place the element as a child of the '''device''' element:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
  &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
  &amp;lt;root xmlns=&amp;quot;urn:schemas-upnp-org:device-1-0&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;specVersion&amp;gt;&amp;lt;!-- ... --&amp;gt;&amp;lt;/specVersion&amp;gt;&lt;br /&gt;
    &amp;lt;device&amp;gt;&lt;br /&gt;
      &amp;lt;deviceType&amp;gt;urn:schemas-futzle-com:device:holidayvirtualswitch:1&amp;lt;/deviceType&amp;gt;&lt;br /&gt;
      &amp;lt;staticJson&amp;gt;D_HolidayVirtualSwitch1.json&amp;lt;/staticJson&amp;gt;&lt;br /&gt;
      &amp;lt;!-- ... --&amp;gt;&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Capitalization is important, as with all XML.&lt;br /&gt;
&lt;br /&gt;
The static JSON file is customarily called '''D_PluginName.json'''.&lt;br /&gt;
&lt;br /&gt;
=The static JSON file=&lt;br /&gt;
&lt;br /&gt;
The static JSON file is a single JSON object (associative array).&lt;br /&gt;
&lt;br /&gt;
==Root keys==&lt;br /&gt;
&lt;br /&gt;
These keys have been seen at the top level in static JSON files in the wild:&lt;br /&gt;
&lt;br /&gt;
; flashicon&lt;br /&gt;
: The device's icon, as a string.  Despite the name, the icon is no longer related to Adobe Flash in UI4 or UI5. See [[Luup plugin icons]].&lt;br /&gt;
; imgIconBody&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconDimmable&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconTurnable&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconMin&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconMax&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; halloIconsDir&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; inScene&lt;br /&gt;
: When included and set equal to one, it enables any buttons, etc located on the &amp;quot;dashboard box&amp;quot;, so they can be selected in the scene editor for use in scenes, rather than being grayed out.&lt;br /&gt;
; DisplayStatus&lt;br /&gt;
: A JSON object (associative array).  For devices where the icon changes based on a variable's value, describes which variable, and the range of values that produce different icon images.  See [[Luup plugin icons]].&lt;br /&gt;
; state_icons&lt;br /&gt;
: A JSON array.  For devices where the icon changes based on a variable's value, describes which icon files exist.  See [[Luup plugin icons]]. Used only in firmware 1.5.401 or later.&lt;br /&gt;
; doc_url&lt;br /&gt;
: A JSON object (associative array). In UI5, the only item in this object which is used is doc_page. It controls which page in docs5.mios.com is brought up when you click on the help ('''?''') icon in the device control panel.&lt;br /&gt;
; Tabs&lt;br /&gt;
: A JSON array.  Describes the tabs in the detail dialog, and what subset of the first tab appears in the dashboard.  See [[Luup plugin tabs]].&lt;br /&gt;
; DeviceType&lt;br /&gt;
: A JSON string.  Must match the '''deviceType''' element in the corresponding device XML ('''D_PluginName.xml''') file.&lt;br /&gt;
; eventList&lt;br /&gt;
: A JSON object (associative array).  Describes the events that this plugin can produce.  Events are triggers that can fire off actions in scenes.  This key is required for UI4, and obsoleted in UI5. For UI5, use the eventList2 tag, which has a different format. For compatibility with both UI4 and UI5 you need the same information in both keys. See [[UI4 UI5 Migration]] for more information.&lt;br /&gt;
; eventList2&lt;br /&gt;
: see eventList, above.&lt;br /&gt;
; sceneList&lt;br /&gt;
: A JSON object (associative array).  Describes the actions that this plugin can perform (for instance, as the action in a scene). This key is not required and is ignored in UI5, however it is needed for UI4 compatibility. See [[UI4 UI5 Migration]] for more information about its format.&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugins:_Static_JSON_file</id>
		<title>Luup plugins: Static JSON file</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugins:_Static_JSON_file"/>
				<updated>2013-06-09T16:57:04Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: Formatting edit.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
										[[Category:Development]]&lt;br /&gt;
&lt;br /&gt;
Starting in UI4, Luup plugins can specify a ''static JSON'' file.  This file describes how the plugin appears in the web interface.&lt;br /&gt;
&lt;br /&gt;
=What the static JSON file controls=&lt;br /&gt;
&lt;br /&gt;
* The icon used by the plugin in the dashboard.&lt;br /&gt;
* The text that displays in the one-to-two-row status message in the dashboard.&lt;br /&gt;
* Whether the icon changes depending on the value of a variable in the device (for example, a binary light's icon changes from lit to unlit).&lt;br /&gt;
* The tabs that appear in the device's detail dialog (when you click the wrench/spanner).&lt;br /&gt;
* The content of these tabs.&lt;br /&gt;
* The triggers or events that are available for the device in the &amp;quot;Triggers&amp;quot; or &amp;quot;Events&amp;quot; tag of a scene.&lt;br /&gt;
&lt;br /&gt;
=Referencing the static JSON file from the Device XML file=&lt;br /&gt;
&lt;br /&gt;
The device XML file (customarly '''D_PluginName.xml''') contains a reference to the static JSON file, in the '''staticJson''' element.  Place the element as a child of the '''device''' element:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
  &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
  &amp;lt;root xmlns=&amp;quot;urn:schemas-upnp-org:device-1-0&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;specVersion&amp;gt;&amp;lt;!-- ... --&amp;gt;&amp;lt;/specVersion&amp;gt;&lt;br /&gt;
    &amp;lt;device&amp;gt;&lt;br /&gt;
      &amp;lt;deviceType&amp;gt;urn:schemas-futzle-com:device:holidayvirtualswitch:1&amp;lt;/deviceType&amp;gt;&lt;br /&gt;
      &amp;lt;staticJson&amp;gt;D_HolidayVirtualSwitch1.json&amp;lt;/staticJson&amp;gt;&lt;br /&gt;
      &amp;lt;!-- ... --&amp;gt;&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Capitalization is important, as with all XML.&lt;br /&gt;
&lt;br /&gt;
The static JSON file is customarily called '''D_PluginName.json'''.&lt;br /&gt;
&lt;br /&gt;
=The static JSON file=&lt;br /&gt;
&lt;br /&gt;
The static JSON file is a single JSON object (associative array).&lt;br /&gt;
&lt;br /&gt;
==Root keys==&lt;br /&gt;
&lt;br /&gt;
These keys have been seen at the top level in static JSON files in the wild:&lt;br /&gt;
&lt;br /&gt;
; flashicon&lt;br /&gt;
: The device's icon, as a string.  Despite the name, the icon is no longer related to Adobe Flash in UI4 or UI5. See [[Luup plugin icons]].&lt;br /&gt;
; imgIconBody&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconDimmable&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconTurnable&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconMin&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; imgIconMax&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; halloIconsDir&lt;br /&gt;
: Ignored in UI5.&lt;br /&gt;
; inScene&lt;br /&gt;
: When included and set equal to one, it enables any buttons, etc located on the &amp;quot;dashboard box&amp;quot;, so they can be selected in the scene editor for use in scenes, rather than being grayed out.&lt;br /&gt;
; DisplayStatus&lt;br /&gt;
: A JSON object (associative array).  For devices where the icon changes based on a variable's value, describes which variable, and the range of values that produce different icon images.  See [[Luup plugin icons]].&lt;br /&gt;
; state_icons&lt;br /&gt;
: A JSON array.  For devices where the icon changes based on a variable's value, describes which icon files exist.  See [[Luup plugin icons]]. Used only in firmware 1.5.401 or later.&lt;br /&gt;
; doc_url&lt;br /&gt;
: A JSON object (associative array). In UI5, the only item in this object which is used is doc_page. It controls which page in docs5.mios.com is brought up when you click on the help ('''?''') icon in the device control panel.&lt;br /&gt;
; Tabs&lt;br /&gt;
: A JSON array.  Describes the tabs in the detail dialog, and what subset of the first tab appears in the dashboard.  See [[Luup plugin tabs]].&lt;br /&gt;
; DeviceType&lt;br /&gt;
: A JSON string.  Must match the '''deviceType''' element in the corresponding device XML ('''D_PluginName.xml''') file.&lt;br /&gt;
; eventList&lt;br /&gt;
: A JSON object (associative array).  Describes the events that this plugin can produce.  Events are triggers that can fire off actions in scenes.  This key is required for UI4, and obsoleted in UI5. For UI5, use the eventList2 tag, which has a different format. For compatibility with both UI4 and UI5 you need the same information in both keys. See [[UI4 UI5 Migration]] for more information.&lt;br /&gt;
; eventList2&lt;br /&gt;
: see eventList, above.&lt;br /&gt;
; sceneList&lt;br /&gt;
: A JSON object (associative array).  Describes the actions that this plugin can perform (for instance, as the action in a scene). This key is not required and is ignored in UI5, however it is needed for UI4 compatibility. See [[UI4 UI5 Migration]] for more information about its format.&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	<entry>
		<id>http://wiki.mios.com/index.php/Luup_plugins:_Static_JSON_file</id>
		<title>Luup plugins: Static JSON file</title>
		<link rel="alternate" type="text/html" href="http://wiki.mios.com/index.php/Luup_plugins:_Static_JSON_file"/>
				<updated>2013-06-09T16:41:34Z</updated>
		
		<summary type="html">&lt;p&gt;Gengen: Corrections for UI5&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;p&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Starting in UI4, Luup plugins can specify a &amp;lt;i&amp;gt;static JSON&amp;lt;/i&amp;gt; file.  This file describes how the plugin appears in the web interface.&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;h1&amp;gt;What the static JSON file controls&amp;lt;/h1&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt; The icon used by the plugin in the dashboard.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt; The text that displays in the one-to-two-row status message in the dashboard.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt; Whether the icon changes depending on the value of a variable in the device (for example, a binary light's icon changes from lit to unlit).&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt; The tabs that appear in the device's detail dialog (when you click the wrench/spanner).&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt; The content of these tabs.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt; The triggers or events that are available for the device in the &amp;quot;Triggers&amp;quot; or &amp;quot;Events&amp;quot; tag of a scene.&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;h1&amp;gt;Referencing the static JSON file from the Device XML file&amp;lt;/h1&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;The device XML file (customarly &amp;lt;b&amp;gt;D_PluginName.xml&amp;lt;/b&amp;gt;) contains a reference to the static JSON file, in the &amp;lt;b&amp;gt;staticJson&amp;lt;/b&amp;gt; element.  Place the element as a child of the &amp;lt;b&amp;gt;device&amp;lt;/b&amp;gt; element:&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;amp;lt;?xml version=&amp;amp;quot;1.0&amp;amp;quot;?&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;root xmlns=&amp;amp;quot;urn:schemas-upnp-org:device-1-0&amp;amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;specVersion&amp;amp;gt;&amp;amp;lt;!-- ... --&amp;amp;gt;&amp;amp;lt;/specVersion&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;device&amp;amp;gt;&lt;br /&gt;
      &amp;amp;lt;deviceType&amp;amp;gt;urn:schemas-futzle-com:device:holidayvirtualswitch:1&amp;amp;lt;/deviceType&amp;amp;gt;&lt;br /&gt;
      &amp;amp;lt;staticJson&amp;amp;gt;D_HolidayVirtualSwitch1.json&amp;amp;lt;/staticJson&amp;amp;gt;&lt;br /&gt;
      &amp;amp;lt;!-- ... --&amp;amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Capitalization is important, as with all XML.&lt;br /&gt;
&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;The static JSON file is customarily called &amp;lt;b&amp;gt;D_PluginName.json&amp;lt;/b&amp;gt;.&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;h1&amp;gt;The static JSON file&amp;lt;/h1&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;The static JSON file is a single JSON object (associative array).&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;h2&amp;gt;Root keys&amp;lt;/h2&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;These keys have been seen at the top level in static JSON files in the wild:&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt; flashicon&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; The device's icon, as a string.  Despite the name, the icon is no longer related to Adobe Flash in UI4 and UI5. See &amp;amp;lt;a _fcknotitle=&amp;quot;true&amp;quot; href=&amp;quot;Luup plugin icons&amp;quot;&amp;amp;gt;Luup plugin icons&amp;amp;lt;/a&amp;amp;gt;.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;dt&amp;gt; imgIconBody&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; Ignored in UI5.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;dt&amp;gt; imgIconDimmable&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; Ignored in UI5.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;dt&amp;gt; imgIconTurnable&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; Ignored in UI5.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;dt&amp;gt; imgIconMin&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; Ignored in UI5.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;dt&amp;gt; imgIconMax&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; Ignored in UI5.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;dt&amp;gt; halloIconsDir&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; Ignored in UI5.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;dt&amp;gt; inScene&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; When included and set equal to 1, it enables any buttons, etc located on the &amp;quot;dashboard box&amp;quot;, so they can be selected in the scene editor for use in scenes, rather than being grayed out.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;dt&amp;gt; DisplayStatus&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; A JSON object (associative array).  For devices where the icon changes based on a variable's value, describes which variable, and the range of values that produce different icon images.  See &amp;amp;lt;a _fcknotitle=&amp;quot;true&amp;quot; href=&amp;quot;Luup plugin icons&amp;quot;&amp;amp;gt;Luup plugin icons&amp;amp;lt;/a&amp;amp;gt;.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;dt&amp;gt; state_icons&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; A JSON array.  For devices where the icon changes based on a variable's value, describes which icon files exist.  See &amp;amp;lt;a _fcknotitle=&amp;quot;true&amp;quot; href=&amp;quot;Luup plugin icons&amp;quot;&amp;amp;gt;Luup plugin icons&amp;amp;lt;/a&amp;amp;gt;. Used only in firmware 1.5.401 or later.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;dt&amp;gt; doc_url&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; A JSON object (associative array). In UI4, the only item in this object which is used is doc_page. It controls which page in docs5.mios.com is brought up when you click on the help (?) icon.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;dt&amp;gt; Tabs&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; A JSON array.  Describes the tabs in the detail dialog, and what subset of the first tab appears in the dashboard.  See &amp;amp;lt;a _fcknotitle=&amp;quot;true&amp;quot; href=&amp;quot;Luup plugin tabs&amp;quot;&amp;amp;gt;Luup plugin tabs&amp;amp;lt;/a&amp;amp;gt;.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;dt&amp;gt; DeviceType&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; A JSON string.  Must match the &amp;lt;b&amp;gt;deviceType&amp;lt;/b&amp;gt; element in the corresponding device XML (&amp;lt;b&amp;gt;D_PluginName.xml&amp;lt;/b&amp;gt;) file.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;dt&amp;gt; eventList&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; A JSON object (associative array).  Describes the events that this plugin can produce.  Events are triggers that can fire off actions in scenes.  This key is required for UI4, and obsoleted in UI5. For UI5, use the eventList2 tag, which has a different format. For compatibility with both UI4 and UI5 you need the same information in both keys. See &amp;amp;lt;a _fcknotitle=&amp;quot;true&amp;quot; href=&amp;quot;UI4 UI5 Migration&amp;quot;&amp;amp;gt;UI4 UI5 Migration&amp;amp;lt;/a&amp;amp;gt; for more information.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;dt&amp;gt; eventList2&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; see eventList, above.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;dt&amp;gt; sceneList&lt;br /&gt;
&amp;amp;lt;/dt&amp;amp;gt;&amp;lt;dd&amp;gt; A JSON object (associative array).  Describes the actions that this plugin can perform (for instance, as the action in a scene). This key is not required and is ignored in UI5, however it is needed for UI4 compatibility. See &amp;amp;lt;a _fcknotitle=&amp;quot;true&amp;quot; href=&amp;quot;UI4 UI5 Migration&amp;quot;&amp;amp;gt;UI4 UI5 Migration&amp;amp;lt;/a&amp;amp;gt; for more information about its format.&lt;br /&gt;
&amp;amp;lt;/dd&amp;amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&amp;amp;lt;a _fcknotitle=&amp;quot;true&amp;quot; href=&amp;quot;Category:Development&amp;quot;&amp;amp;gt;Development&amp;amp;lt;/a&amp;amp;gt;&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;/div&gt;</summary>
		<author><name>Gengen</name></author>	</entry>

	</feed>