The Light Controller Application is designed for the Mako Server and uses the SMQ protocol for real time communication between browsers, the Light Controller Server App, and devices (Light Controller banks).
The SMQ protocol facilitates the pub/sub (one-to-many) design pattern. In this application, we use the standard pub/sub design pattern when we want to send a message to N devices, where N can be a number from 0 and up. However, the standard pub/sub design pattern is difficult (if not impossible) to use when sending messages to one specific destination. For this reason, this application is heavily dependent on the extended features in the SMQ protocol that enables one-to-one messaging and sender's address. Sender's address enables the recipient of a message to learn/store the address of the publisher and to send messages back to the publisher at any time.
See the SMQ online documentation for an introduction to this protocol.
The following figure depicts the complete Light Controller infrastructure. Notice that we have a dedicated Light Controller Application on the server. This application, which is designed in the Lua scripting Language, is responsible for coordinating all connected clients and for controlling the lights.
As shown in the above figure, the server side Light Controller Application interacts directly with the SMQ broker on the server side. The section below provides details on all Lua scripts and web pages that are part of the server side Light Controller Application.
This article should preferably be opened via an actively running server. Each source file listed will then turn into a link, which you can click on to view the source code.
The publicly available pages are index.lsp, config/index.lsp, and admin/index.lsp. However, several additional files are used and control of these additional server resources are managed by the above mentioned files.
Note that files ending with .shtml and files/directories starting with a dot are not directly accessible by an HTTP client such as a browser. These files are indirectly accessible by the above three mentioned LSP pages.
index.lsp
The main index file is the entry to the web based Light Controller with optional public access. The Lua script in this file forwards the request to LightController.shtml if the user has access to the Light Controller. The user has access if public access is enabled (for the current time) or if the user has been authenticated by one of config/ or admin/. The request is forwarded to .NoPubAccess.lsp if the user does not have access to the Light Controller.
The main index file is also the entry for SMQ connection requests. If index.lsp detects that this is an SMQ connection request, the correct SMQ broker is looked up based on the HTTP "host" header (the domain name). The request is delegated to the SMQ broker if found, where the HTTP connection is upgraded to an SMQ connection.
LightController.shtml
The HTML code for the web based Light Controller. This page includes LightController.js
LightController.js
The client side JavaScript code for controlling the lights. The JavaScript code creates an SMQ client instance, connects to the SMQ broker running at the server, and establishes a persistent real time SMQ pub/sub and one-to-one connection with the server.
.NoPubAccess.lsp
The user is forwarded to this page if the user does not have access to the Light Controller. A basic "denied access" message is returned to the browser. The user is also presented with a login link if the user has previously accessed one of config/ or admin/.
.AuthResp.lsp
The index files in config/ and admin/ are protected by authentication. The content of this file is shown if the user visits one of these pages and if the user is not authenticated. The page also provides a link to the Light Controller if public access is enabled for the current time.
admin/index.lsp
The administrator's (server owner's) main page. The administrator manages the "homeowners" that can connect to the online server. Each homeowner has his own SMQ broker instance and each broker instance is identified by a domain name. Each homeowner must have his own domain where the DNS points to the online server. The administrator is typically in charge of setting up the domain name for the homeowner by using a service such as freenom.com.
The main administrator page lets the administrator add new domain names (new homeowners). The domain names are shown in a list. When a domain name is initially clicked, the index page redirects the request to .setcred.lsp (set credentials), where the administrator sets the homeowners username/password. The index page then redirects the request to .domain.lsp.
admin/.domain.lsp
This page enables the administrator to configure the Light Controller hardware bank connected to the broker. Each connected Light Controller bank shows up on this page. An AJAX request polls the page for updates (via index.lsp) and JavaScript code automatically refreshes the page (if no editing is performed) to reflect the changes in connected hardware. In other words, Light Controller banks establishing an SMQ connection with the server will automatically show up on this page.
config/index.lsp
The homeowner's configuration page enables the homeowner to configure what times the lights should be on and the time public access is available for the Light Controller web interface.
.preload
The Light Controller's startup script includes most of the logic for managing the SMQ brokers and for managing the server side Light Controller application. The LSP pages use services (functions) defined in this script when interacting with the Light Controller logic.
.lua/StateManager.lua
The Light Controller's on/off state is managed by the logic in this file. The State Manager script is loaded by the .preload script and the State Manager script returns a Lua table that provides an API for the .preload script to use. The preload script uses the method "create" to create a State Manager instance for each broker. The state manager is attached to the broker object (by the code in .preload) as the name "st".
SMSG_SERVER_HELLO=1
Sent from the server to an SMQ client as soon as the client connects. This message tells the client the etid of the SMQ server app.
SMSG_LIGHTS=2
Sent from device to server at startup just after receiving MSG_SERVER_HELLO.
SMSG_BROWSER=3
Sent from browser to server at startup just after receiving MSG_SERVER_HELLO.
SMSG_DEVICE_INFO=3
Sent from the server to browser after receiving SMSG_BROWSER or when data is updated.
SMSG_SET_LIGHT=4
Sent from the server or a browser to a device when the server/browser wants to set a light ON/OFF.
SMSG_COMMAND_LIST=5
Sent from server to browser when browser connects. Provides a list of commands (jobs) the visitor can activate. The jobs are managed by the State Manager instance associated with this domain.
SMSG_EXEC_COMMAND=6
Sent from a browser to the server when a visitor clicks one of the commands provided by SMSG_COMMAND_LIST. The command is routed to the State Manager, which handles the job (light show).
MSG_LIGHT_STATE=1
Published by the device after receiving SMSG_SET_LIGHT and after setting the light. All browser clients and the SMQ server app must subscribe to this message.
MSG_DEVICE_INFO=2
Same as SMSG_DEVICE_INFO, but sent as a one-to-many message instead of a one-to-one message. Sent from server to all connected browsers (subscribed) when device info changes (for example via admin user interface).
MSG_DEVICE_DISCONNECT=3
Sent from server to all connected browsers (subscribed) when a device disconnects.
One configuration file for each domain (homeowner). Configuration data is stored as JSON in the server's "home directory"/XmasLightsCfg
JSON represented as Lua tables:
{ -- Homeowner's credentials credentials={username=string, password={HA1}} -- Used by SM and set by config/index.lsp time={ setOn=string, setOff=string, notBefore=string, notAfter=string}, -- Offset between server time and homeowner's time is automatically calculated -- by using browser's time when homeowner visits config/index.lsp timeZoneOffset=number, -- List of all registered devices (either currently connected or -- previously connected) devices=table, } devices={XX=table}, where XX is the b64 encoding of the unique ID for the connected device. Each device connected has its own unique entry. XX={ name=string, lightsT=lightsTable } lightsTable={ lightId={color=string,name=string} } lightId = 1 to N, where N is the number provided by SMSG_LIGHTS