NAV
Home Developers

Introduction

Module Development Process devprocess

This documentation is aimed at developers wanting to create their own modules or themes for use with CommandFusion iViewer and the CommandFusion Control Center.

Use the side menu to jump to the topics that you are interested in, or use the search tool in the side menu.

Modules

A module can be used within Control Center for a few different purposes, such as creating the logic and UI to communicate with a specific device on the network, or to create a special UI widget which can be used to display data routed from other modules.

A sample module skeleton is available, and is a great base on which to build your first module.
A second sample is available for a simple widget module.

Module Anatomy

Example folder structure

ZIP
├── manifest.json
├── some-code.js
├── cloud.js
├── promo.png
├── icon.png
├── screenshot-ipad-01.png
├── screenshot-ipad-02.png
├── icon-editor.png
├── ipad.gui
└── ipad
    ├── graphic-asset.png
    └── graphic-asset@2x.png

A module is a zip archive, consisting of a number of components described below:

Manifest

The manifest outlines the inputs, outputs, parameters and network communications of a module. This module manifest is used within the CommandFusion Module API as well as in the online module framework and directly within guiDesigner to present the module structure to users and allow them to integrate the module into their projects. The manifest is specified in a manifest.json file within the module archive, and uses the JSON syntax.

Example manifest file (taken from the Skeleton sample module)

{
    "id": "com.commandfusion.skeleton",
    "name": "Skeleton",
    "help": "Skeleton module for bootstrapping your own module for the CommandFusion module framework.",
    "version": 2,
    "versionString": "0.2",

    "code": ["skeleton.js"],

    "keywords": "skeleton module commandfusion",
    "icon": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAA...",
    "icons": [],
    "contributors": [
        {
            "name": "CommandFusion",
            "contact": "support@commandfusion.com"
        }
    ],
    "allowMultipleInstances": true,

    "dependencies": {},

    "license": {
        "type": "free-opensource",
        "name": "public domain"
    },

Properties

    "inputs": {
        "my-input": {
            "name": "My Input",
            "help": "An input example.",
            "setter": "Input_MyFunction"
        },
        "another-input": {
            "name": "Another Input Name",
            "help": "An example of a module input with params.",
            "setter": "Input_AnotherFunctionName",
            "value": {
                "name": "Some Value",
                "type": "string",
                "help": "A value that will be sent to the module.",
                "required": false
            },
            "params" : [
                {
                    "name": "Parameter Name Goes Here",
                    "help": "Is this a parameter for a module input?",
                    "type": "string",
                    "default": "yes",
                    "options": {
                        "yes": "Yup",
                        "ya": "Sure is"
                    }
                }
            ]
        },
	    "zone-change": {
		    "name": "Zone Change",
		    "help": "Used by Control Center project builder to inform your module that a new zone has been selected.",
		    "setter": "Input_ZoneChange",
		    "value": {
			    "name": "Zone Data",
			    "type": "string",
			    "help": "Zone data as JSON string",
			    "required": true
		    }
	    },
	    "activity-change": {
		    "name": "Activity Change",
		    "help": "Used by Control Center project builder to inform your module that a new activity has been selected.",
		    "setter": "Input_ActivityChange",
		    "value": {
			    "name": "Activity Data",
			    "type": "string",
			    "help": "Activity data as JSON string",
			    "required": true
		    }
	    },
	    "config-load-settings": {
		    "name": "Config Load Settings",
		    "help": "Used by project builder config pages to load settings from the cloud.",
		    "setter": "Config_LoadSettings",
		    "value": {
			    "name": "Settings data",
			    "type": "string",
			    "help": "JSON string of the settings data for the module to load.",
			    "required": true
		    }
	    },
	    "config-save-settings": {
		    "name": "Config Save Settings",
		    "help": "Used by project builder config pages to save settings back to the cloud.",
		    "setter": "Config_SaveSettings",
		    "value": {
			    "name": "Settings URL",
			    "type": "string",
			    "help": "The URL to save settings to via HTTP POST."
		    }
	    },
	    "config-show": {
		    "name": "Config Show",
		    "help": "Used by project builder config pages to perform actions when the module is being configured.",
		    "setter": "Config_Show"
	    },
	    "config-startup": {
		    "name": "Config Startup",
		    "help": "Used by project builder config pages to initialise data on startup.",
		    "setter": "Config_Startup"
	    }
    },

Inputs

    "outputs": {
        "my-output": {
            "name": "My Output",
            "help": "An output from the module that you can route to your GUI. This output does not have an associated JS function, it is only updated via code and cannot be manually requested."
        },
        "another-output": {
            "name": "Another Output Name",
            "help": "An output example with parameters. This output also has an associated JS 'getter' function which can be used to request the output state manually.",
            "getter": "Output_MyFunctionName",
            "value": {
                "name": "The Value",
                "type": "string",
                "help": "Defines what the value of the output is and how to use it."
            },
            "params" : [
                {
                    "name": "Output Parameter",
                    "help": "A parameter used to define more details of the output, like a room name or maybe a light address or something.",
                    "type": "string"
                }
            ]
        }
    },

Outputs

    "parameters": {
        "module-param": {
            "name": "My Module Parameter",
            "help": "A parameter that is set and used elsewhere in the module, like maybe a username or password.",
            "type": "string",
            "default": "default value goes here"
        }
    },

Parameters

    "systems": {
        "system-id": {
            "name": "My System Name",
            "type": "tcp-client",
            "enabled": true,
            "settings": {
                "protocol": "tcp",
                "ip": "192.168.0.100",
                "port": 10207
            },
            "feedback": [
                {
                    "name": "Feedback_Name",
                    "regex": "."
                }
            ]
        }
    }
}

Systems

JavaScript API

This section documents the JavaScript API that is available to use both within modules, and to interact with modules from the project.

Global Functions

These functions are available both internally within module code, as well as to normal projects.

CFM.feedInput([instanceId, “input_key”, …], value)

 // Feed some input into a module (string)
CFM.feedInput([myInstanceId, "example_input"], "a value");

// Feed some input into a  module (numerical)
CFM.feedInput([myInstanceId, "volume_level"], 10);

// Feed some input into a module (boolean)
CFM.feedInput([myInstanceId, "power"], true);

// Use module's extra parameters
CFM.feedInput([instanceId, "light_dimmer_1", rampRate], dimLevel);

Feed input into a module. Feeding input into a module allows you to indirectly call one of the module’s internal functions in order to effect some action or change in state of the module.

The first parameter is an array containing the instanceId (number) and input_key (string) optionally followed by any number of parameters to direct to the input function).

The instanceId is the instance of the module and input_key is the string representing any of the inputs defined in the module’s manifest by the input’s key.

The second parameter is the value to set the input to. The value and the additional parameters accepted by the module can be of any type, and are defined by each module, so be sure to read the information provided by the module you are using.

CFM.triggerOutput([instanceId, “output_key”, …], value);

// Set the volume level to 100%. Note that this output could be used to drive a 
// slider or gauge since it's 0-65535
CFM.triggerOutput([instanceId, "volume_level"], 65535);

// This output would be well suited to a serial join
CFM.triggerOutput([instanceId, "source_name"], "HDMI");

Trigger the module framework to send updates to all the GUI objects subscribed to the specified output.

triggerOutput is conventionally called from inside a module after some feedback is received or some state is changed to update the outputs that were effected.

Triggering an output change causes the module framework to send the updated value to any outputs registered with the module.

The first parameter is an array containing the instanceId (number) and output_key (string) optionally followed by any number of parameters to direct to the output).

The instanceId is the instance of the module and output_key is the string representing any of the outputs defined in the module’s manifest by the output’s key.

The second parameter is the value to set the output to. The value and the additional parameters accepted by the output must be strings, and are defined by each module, so be sure to read the information provided by the module you are using.

CFM.readOutput([instanceId, “output_key”], callback)

// If we used read output after triggering the output from the example above
CFM.readOuput([instanceId, "volume_level"], function(output, value){
    // logs "volume_level"
    CF.log(output);
    // logs "65535"
    CF.log(value);
});

Asynchronously get the value of an output on a particular module, using the output’s key.

Your callback will receive two parameters: * output (string): the output key that was requested * value (string): the value of the output

CFM.watchOutput([instanceId, “output_key”], callback)

// If we used watch output before triggering the output from the example above we can receive updates when it changes
var myWatchId = CFM.watchOutput([instanceId, "volume_level"], function(output, value){
    // logs "volume_level"
    CF.log(output);
    // logs "65535"
    CF.log(value);
})

Be notified whenever the value of an output changes, using the output’s key.

Your callback will receive two parameters (same as readOutput) * output (string): the output key that was requested * value (string): the value of the output

watchOutput returns a watchId that can be used to unwatch the output later.

CFM.unwatchOutput([instanceId, “output_key”], watchId)

// Continuing from above, if we didn't want to receive updates about the volume level anymore
CFM.unwatchOutput([instanceId, "volume_level"], myWatchId)

Stop receiving updates about a particular output.

CFM.getParameters(instanceId, callback)

// Get the the parameter from a module and log it.
CFM.getParameters(instanceId, function(parameters) {
    // Logs "CF's Audio Receiver"
    CF.log(parameters.name);
});

Asynchronously get the parameters for a particular module. This is handy if you want to get another module’s parameters from outside.

If you are writing code inside a module, it would be easier to use this.getParameters.

Your callback will receive one parameters object * parameters (object): object containing all module parameters.

CFM.setParameter(instanceId, parameter_name, value)

// Change the value of the name parameter for a particular module
CFM.setParameter(instanceId, "name", "My Audio Receiver");

Set the value of a parameter for a module. This is best used from outside a module. If you are writing code inside a module it would be easier to use CFM.parameters["param_name"] = "new_value".

Module Functions

CFM.log(string)

// Logs: "[M12]: Feedback Received.", if the calling module was instance ID 12
CFM.log("Feedback received.");

Log something from inside a module to the debugger window.

Also logs out the module instance ID for ease of debugging.

The following functions will only work within the code for a module.

this.log()

// Logs: "[M12]: Feedback Received.", if the calling module was instance ID 12
this.log("Feedback received.");

Alias to CFM.log

this.getParameter()

Unlike the CFM version of this function, this returns the value of the parameter syncronously.

// Get a param syncronously from inside the module
var name = this.getParameter(name);
//Logs "My Audio Receiver"
CF.log(name);

this.setParameter(parameter_name, value, callOnParameterChangeFunction = true)

// Set a parameter without triggering the change handler
this.setParameter("name", "My HDMI Switch", false);
// Set a parameter and trigger change handler
this.setParameter("name", "My HDMI Switch");

Set the value of a parameter on the module. This function, compared to CFM.setParameter has the advantage of having the option not to trigger change handler method for the parameter and not having to specify the instance ID.

If no value is specified for callOnParameterChangeFunction the change handler will be called, so any listeners will be notified of the change.

this.EP()

// returns ["1","light",7,"red"]
this.EP("light",7,"red")
CFM.feedInput(this.EP("light",7,"red"), "255");

Convenience function for use when calling feedInput or triggerOutput so you don’t have to type the instanceId every time you call them.

Module Properties

CFM.parameters

// Get the parameter defined as "name" in the manifest
CFM.parameters["name"];
// Change the value of a parameter
CFM.parameters["name"] = "New value";

Object containing parameters defined in the manifest.json.
Note: this is only available within the code of a module.

CloudJS

Modules can optionally include a cloud.js file, which can be used by hardware to generate macro actions.
This allows hardware to communicate with devices (over serial, infrared, or network sockets) using JavaScript to produce the command data.

The cloud.js JavaScript should closely match the JavaScript used by the module, but the input functions (setters from modules) should return a string. This string is then used as the final action that the hardware will send.

This file must NOT reference any CF.* or CFM.* calls. Only include code that any web browser can run.

Embedded UI

Modules can contain embedded user interface elements that are automatically imported by Control Center projects.
The embedded UI takes the form of a guiDesigner project.
The project can contain only subpages. Any pages will be ignored.

Multiple guiDesigner projects can be included, targeting different mobile devices, such as iPad, iPhone, Android, etc.
Note: Only iPad is currently supported. Other devices will be supported in future.

The guiDesigner project must contain the following:

Subpage Types

Embedded UI must be made up of subpages. Any pages will be ignored.
The name of the subpage defines the type of subpage it is, and how it can be used by Control Center.

The following subpage name prefixes can be used:

Layout and Config subpages must end with a digit to enable navigating to any next or previous subpage.
For example: layout_PageName1 and layout_PageName2.

The navigation JavaScript functions are automatically made available to any project that was built using Control Center.

Layout Subpages

These subpages are used within Control Center project by assigning them to Activities within your zones.
The name of the subpage must start with layout_ and end with a digit, starting at 1.
For example, if you wanted to have two pages for controlling a TV in your module, perhaps 1 for favourite channels, and a second layout for navigating the TV menu.
The subpages would be named as follows:

The name of the layout would then be automatically configured to be TV based on the subpage naming above. This is the name that will show in the Control Center UI to users when assigning pages from your module to activities in their projects.
The first layout (with a 1 suffix) will always be shown first by default when navigating to an activity that has been assigned to the layout name.

Navigation between layout and config subpages with the same name (just incremental digit suffixes) is done by calling JavaScript functions:

// Viewing layout_PageName1
prevLayout("MODULE_INSTANCE", "PageName"); // From within module UI
prevLayout(instanceID, "PageName");        // From within module JS
// Now we are viewing layout_PageName2
nextLayout("MODULE_INSTANCE", "PageName"); // From within module UI
nextLayout(instanceID, "PageName");        // From within module JS
// Now we are back to viewing layout_PageName1 again.

Config Subpages

Configuration subpages are shown when selecting the module from the configuration page of the Control Center GUI after loading the project onto your mobile device.
If the module contains no configuration pages, it will NOT be listed in this main configuration menu.

Naming of and navigation of configuration pages is similar to Layout Subpages described above, with two key differences:
1. The subpage prefix must start with layout_config 1. The subpage does NOT contain any specific layout name after the prefix, just followed immediately with a digit.

As with Layout Subpages, the first configuration page to be displayed is the page ending with 1. So if you wanted 2 configuration pages for your module, they would be name like so:

Drawer Subpages

Opening and closing drawer subpages is done by calling JavaScript functions:

openDrawer("MODULE_INSTANCE_drawer_top_page_name");  // From within module UI
closeDrawer("MODULE_INSTANCE_drawer_top_page_name"); // From within module UI
openDrawer(instanceID + "_drawer_top_page_name");    // From within module JS
closeDrawer(instanceID + "_drawer_top_page_name");   // From within module JS

These subpages are slid into and out of view using the openDrawer() and closeDrawer() JavaScript functions.
The position and direction of sliding for the drawer subpages depends on the subpage name prefix. Options are:

Showing and hiding popup subpages is done by calling JavaScript functions:

showPopup("MODULE_INSTANCE_popup_page_name"); // From within module UI
hidePopup("MODULE_INSTANCE_popup_page_name"); // From within module UI
showPopup(instanceID + "_popup_page_name");   // From within module JS
hidePopup(instanceID + "_popup_page_name");   // From within module JS
hidePopup();                                  // Hide last opened popup

These subpages can be shown and hidden, and will always popup in the center of the screen.
The name of the subpage must start with popup_.

List Subpages

Adding specific subpages to list items via JavaScript calls

CF.listAdd(instanceID + "_some_list_tag", [{subpage: instanceID + "_list_subpage_name"}]);

These subpages are used as content in dynamic lists. The module will reference these subpages as part of the list properties, or as part of JavaScript list manipulation calls.
An example to add an item to a list using a specific subpage might be:

Text Replacements

Whenever an embedded UI is added to a Control Center project, there are a number of processes that are run automatically when the project is built, resulting in changes to the embedded version of the GUI.

These processes allow you to reference inputs and outputs of your module directly in your module UI.
These replacements are done doing a global search and replace on your module project XML.
NOTE: These replacements are also run on any JavaScript code that is embedded in your UI (attached to buttons, etc). The replacements do NOT get applied to your source .js files however!

These text replacements are also processed on themes.

MODULE_INSTANCE

When a module is added to a project, it is given a unique module instance number. Some modules can have multiple instances added to a single project.

The text MODULE_INSTANCE is automatically replaced with this unique number at project build time.
There are a number of cases where this text can be used in your embedded GUI, but most commonly it is used to call various JavaScript functions directly in your UI, like showing drawers or popups.

MODULE_NAME

The name the users gives the module instance within Control can be shown anywhere in your embedded UI, simply by using the text placeholder MODULE_NAME anywhere in your GUI project.

When the project is built from Control Center, the text placeholder will be replaced with the name the user gave the module when adding it to their Control Center project.

Within Object Tags

It is a great idea to tag your objects in your module UI with a tag that will be unique even if multiple instances of the module are added to a project, or even if two modules happen to use the same tag name by chance.

The recommended approach is to start your tag names with MODULE_INSTANCE_ then any prefix after that is up to you - the module developer - to decide best describes the GUI object.

For instance, you might have button in your UI that is meant to show the power state of the device you are writing your module for.

A good tag name would then be something like MODULE_INSTANCE_power. Then anywhere in your module code, you can reference this GUI object directly by the tag.

In your module JavaScript code, you will be told the instance number that is assigned to the module instance, so you can reference objects by tags using code like CF.setJoin(instanceID + "_power", "1");

The variable instanceID is automatically provided to your module code at runtime. See the example skeleton module code for more details.

Module Inputs
// Tags to call module input ID power-on without an empty string as the value:
MODULE_INSTANCE_power-on
// Tags to call module input ID power-on with a value of 1:
MODULE_INSTANCE_power-on 1
// Tags to call module input ID set-vol-level with a param of 1 and a value of 90:
MODULE_INSTANCE_set-vol-level 1 90

By assigning tags matching the input ID’s of your module manifest, you can automatically route buttons to trigger your module inputs.

Simply give a button a tag name with a MODULE_INSTANCE_ prefix, followed by the input ID you want to call when the button is pressed.

For example, to have a button call the input ID power-on, you would give the button a tag name MODULE_INSTANCE_power-on.

If the input requires any parameters or a value, then simply enter the values to send as additional tags after the input ID, separated by spaces.

For example, if you needed to send a zone number as a value to the power-on input, your button tags would look like this:
MODULE_INSTANCE_power-on 1 - this results in sending a value of 1 to the input power-on.

NOTE: Using this method will result in the repeat property of the button being ignored. If you need the button to repeatedly send the command whilst being held, use the TRIGGER_INPUT method.

Module Outputs
// Tags to route module output ID power-state value to a button:
MODULE_INSTANCE_power-state
// Tags to route module output ID power-state with a param of 1 to a button:
MODULE_INSTANCE_power-state 1
// Tags to route module output ID vol-percentage with a param of 1 to a slider:
MODULE_INSTANCE_vol-percentage 1

Similar to Module Inputs above, the same can be done to route data from a module to a GUI Object state, such as a button state or text value.

Simply give a GUI object (button, text object, image, etc) a tag name with a MODULE_INSTANCE_ prefix, followed by the output ID you want to route data from.
For example, to have a button show the state of the output ID power-state, you would give the button a tag name MODULE_INSTANCE_power-state.

If the output has any parameters, then simply enter the parameter values to route from as additional tags after the output ID, separated by spaces.

For example, if you needed to show the power state for zone number from the power-state output, your button tags would look like this:
MODULE_INSTANCE_power-state 1 - this results in showing only the power state of zone 1 from the output power-state.

TRIGGER_INPUT

There are two variations of this text replacement - both tell Control Center to look at the button tags and adjust the JavaScript to call a module input.

These values should be assigned to the ‘JavaScript’ property of a button, and will be replaced with CFM.feedInput calls, with the input ID and parameters obtained from the tags assigned to the same button.

Color Schemes

In Control Center, the user is able to adjust colors of specific elements, such as text colors and button background colors.
Your module UI should stick to the following conventions in order to be automatically updated to match the Control Center project theme configuration as set by the user.

Look within the theme configuration ‘Colors’ tab to view a list of the latest supported theme names.
The first part of the name (before the hyphen) is simply the theme type. Your theme names must NOT include this.
The second part, after the hyphen, is the prefix that your themes should use.

For example, the main body text color theme is listed with ID text-body within the Control Center ‘Colors’ tab.
Any text themes in your modules guiDesigner project that start with body or body_ will automatically have the text color changed to match this color setting when built using Control Center.

If the color ID starts with button-inactive or button-active, then it will adjust the background color of the buttons inactive or active state.
If the color ID starts with button-text, then it will adjust the color of the button text, rather than the button background color, for the specified button state (active or inactive).

Image Processing

When a Control Center project is built, there are some special rules in place to process image files to color them to match the settings in the project Theme Configuration ‘Colors’ tab.

Step 1 - Look for exact match

Any image that exactly matches one of the color ID’s in the users Control Center Theme Configuration will be adjusted to be the exact color chosen, without affecting the transparency of the image.
For example, if you have an image named border-bottombar.png it will be colorized to match the color set in the users border-bottombar theme color configuration.

If an exact match is found, then any filename starting with the color ID and followed by -mask- will also be processed.
For instance, if you have an image background.png, which matches the color ID background, then images named background-mask-header.png and background-mask-footer.png will also be processed.

This allows the user to customise colors of elements in their UI, whilst also allowing gradient fade out masking images to follow suit.

Step 2 - Look for prefix matches

If no exact filename match is found, then any file that is prefixed with the color ID will then be processed.
For example, the color ID icon-active would force the following files to be colorized:

Themes

Within the Control Center UI, each project can be assigned one of the available themes. This documentation explains how to create your own themes which can be submitted to CommandFusion for review before being made available online.
Your themes can either be made private so that only your projects can use them, or shared publicly for anyone to use.

A sample theme is available, and is a great base on which to build your first theme.

Theme Anatomy

Example folder structure

com_commandfusion_mytheme.zip
├── com_commandfusion_mytheme.json
├── promo.png
├── screenshot-ipad-01.png
├── screenshot-ipad-02.png
├── lang-ipad-title-default.png
├── lang-ipad-home-default.png
└── ipad
    ├── mytheme-ipad.gui
    ├── main.js
    ├── graphic-asset.png
    └── graphic-asset@2x.png

The final theme must be delivered to CommandFusion as a zip file, composed of the following file and folder structure:

Theme Manifest

Example theme manifest content

{
    "id": "com.commandfusion.mytheme",
    "name": "My Theme",
    "help": "Some basic description and help text about the theme should be entered here.",
    "version": 1,
    "versionString": "0.1",
    "contributors": [
        {
            "name": "CommandFusion",
            "contact": "support@commandfusion.com"
        }
    ],
    "keywords": "mytheme ipad some other keywords that will be used for search results",
    "license": {
        "type": "free-opensource",
        "name": "MIT"
    },
	"language": {
		"greeting": "Welcome",
		"greeting2": "Please select a room to begin.",
		"zone-title-default": "Select A Room",
		"greeting-zone": "Select Activity",
		"greeting2-zone": "Please select an activity from the side menu.",
		"shutdown-title": "Zone Shutdown",
		"shutdown-body": "Are you sure you want to turn off this zone?",
		"shutdown-cancel": "CANCEL",
		"shutdown-confirm": "SHUTDOWN ZONE",
		"mute": "MUTE",
		"config-title": "Module Configuration",
		"config-body": "Select a module to configure"
	},
	"statusbar": "white",
	"scheme": "Color Scheme Name 1",
	"colors": {
		"Color Scheme Name 1": {
			"background": "rgba(45,45,45,1.0)",
			"background-topbar": "rgba(28,28,28,1.0)",
			"background-bottombar": "rgba(28,28,28,1.0)",
			"background-dialog": "rgba(181,181,181,1.0)",
			"background-modal": "rgba(0,0,0,0.9)",
			"button-inactive": "rgba(117,117,117,1.0)",
			"button-active": "rgba(24,155,231,1.0)",
			"button-text-inactive": "rgba(255,255,255,1.0)",
			"button-text-active": "rgba(255,255,255,1.0)",
			"icon-inactive": "rgba(165,165,165,1.0)",
			"icon-active": "rgba(24,155,231,1.0)",
			"text-title": "rgba(163,175,182,0.9)",
			"text-body": "rgba(255,255,255,1.0)",
			"text-sidebar": "rgba(255,255,255,1.0)",
			"text-dialogbody": "rgba(51,51,51,1.0)",
			"text-dialogtitle": "rgba(51,51,51,1.0)"
		},
		"Color Scheme Name 2": {
			"background": "rgba(224,224,224,1.0)",
			"background-topbar": "rgba(156,156,156,1.0)",
			"background-bottombar": "rgba(156,156,156,1.0)",
			"background-dialog": "rgba(181,181,181,1.0)",
			"background-modal": "rgba(0,0,0,0.9)",
			"button-inactive": "rgba(117,117,117,1.0)",
			"button-active": "rgba(24,155,231,1.0)",
			"button-text-inactive": "rgba(236,236,236,1.0)",
			"button-text-active": "rgba(255,255,255,1.0)",
			"icon-inactive": "rgba(0,0,0,0.52)",
			"icon-active": "rgba(8,125,193,0.9)",
			"text-title": "rgba(54,59,62,1.0)",
			"text-body": "rgba(50,50,50,1.0)",
			"text-sidebar": "rgba(255,255,255,1.0)",
			"text-dialogbody": "rgba(51,51,51,1.0)",
			"text-dialogtitle": "rgba(51,51,51,1.0)"
		}
	}
}

Each theme must contain a manifest file, in JSON format, which describes the theme in detail.
The manifest file uses the same name as the parent zip file, but with a .json file extension.

Colors Schemes

Themes can have one or more color schemes defined, each with a list of user configurable colors. You might want your theme to have both a Dark and Light theme for example. The different color schemes allow the user to quickly select between preconfigured color pallettes for their project. Users can then further customise each individual color within the scheme.

Colors

Colors within themes allow the user to further customise the UI to suit their tastes. This is achieved by presenting the user with color pickers for each color in your scheme, and the color is then applied to graphic files and guiDesigner themes entries when the project is built and served to the users device via Control Center.

Each color item consists of the name of the color entry and the default color value to apply. The name is used to apply the chosen color to the final built project in a variety of ways:

See also Module Color Schemes for details on how the color schemes in your themes are applied to modules and their embedded UI pages.

Required Colors

The following color names are required for every theme:

Pages

Your theme should only use one page. It is up to you, the theme developer, to choose to use one, or both orientations (landscape and portrait).
The page MUST be set as the startup page (right click a page in guiDesigner to assign the startup page of the project).
Any other pages present in your theme guiDesigner project will be ignored.

Module Pages

The layout of the theme must allow for module layout and configuration subpages to be displayed in specific sizes depending on the target platform.

Target Platform Layout Subpage Size (W x H)
iPad 768 x 588
iPhone Not supported yet
Android Not supported yet

Your theme guiDesigner project must include a subpage named layout_placeholder that will define where these module layout pages will be positioned.

UI Naming Requirements

Within the theme, there are a number of page, tag and theme naming requirements. These must exist in every theme project.

Theme Names

Subpage Names

Element Tag Names

JavaScript

Your themes can contain their own JavaScript files to handle functionality such as zone and activity menu building, startup UI cleanup, etc.
Your code will be embedded into the final project immediately after the default Control Center JavaScript include file.

Pre-defined Functions

Example object returned by getZone() call

{
    "zone_id": "43",
    "proj_id": "22",
    "name": "Lounge",
    "icon": "icon-living-room-2-90.png",
    "zone_order": "1",
    "params": null,
    "events": {
        "zone_off": [
            {
                "module": "67",
                "input": "poweroff",
                "value": "",
                "action-id": "module-input"
            }
        ]
    },
    "activity_count": "2",
    "activities": [
        {
            "activity_id": "52",
            "proj_id": "22",
            "zone_id": "43",
            "name": "TV",
            "icon": "icon-tv-90.png",
            "activity_order": "1",
            "data": null,
            "events": null,
            "proj_module_id": "67",
            "layout": "tv",
            "customer_id": "#####",
            "module_name": "Samsung TV"
        }
    ]
}

Example object returned by getActivity() call

{
    "activity_id": "52",
    "proj_id": "22",
    "zone_id": "43",
    "name": "TV",
    "icon": "icon-tv-90.png",
    "activity_order": "1",
    "data": null,
    "events": null,
    "proj_module_id": "67",
    "layout": "tv",
    "customer_id": "#####",
    "module_name": "Samsung TV"
}

There are a number of functions that are defined by Control Center projects automatically that you can take advantage of. These functions will be called automatically at the relevant times.

There are also other functions you can call manually at any time in your code: