UI Simple
MIOS is a lightweight home automation system. The 'brain' of the MIOS software is the back-end, the engine, which runs stand-alone on a variety of internet-connected devices, such as PC's, Mac's, Wi-Fi access points, and dedicated home automation gateways. MIOS also includes a portal at mios.com, which acts as secure relay to MIOS systems that may be behind firewalls. Users can register for an account at mios.com, and that account can be linked to one or more MIOS systems to provide the user remote access to his MIOS system from anywhere. It is easy to control a MIOS system with simple http get's (normal internet requests). The URL you will open is generally data_request?id=xxx, where xxx is some sort of request or control command.
This document describes how to create a simple user interface to control a MIOS system using the simplified lu_sdata (LuaUPnP Simple Data) request. This document describes a simple control-only user interface, meaning it let's the user run scenes and control devices, but does not provide any means to change configuration or do advanced tasks. Whatever device is running the user interface (cell phone, web page, television, etc.) will be referred to as the "controller", and the MIOS engine, or system, that is being controlled by the controller is the "engine".
You can test out all the commands a normal web browser. For example, if your engine is on the same local network as your web browser, and your engine has the IP address: 192.168.2.150, you can view the status of all scenes and devices by opening this link in your browser: http://192.168.2.150:3480/data_request?id=lu_sdata and if you want to turn on device #5 you open this link in your browser: http://192.168.2.150:3480/data_request?id=lu_action&DeviceNum=5&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1
This document includes the specs describing how the UI should work because we use this document as a spec for our own UI's. If you are developing your own UI separately, feel free to change the way it works as needed.
Locating the engine
The first thing the user interface should do is find the engine on the internet. There are 2 different ways to control an engine:
1) Directly, using an IP address, where the engine is either on the same local area network as the controller or has a static IP or port forward that's publicly accessible from the internet, such as in the example links above.
2) Through one of the MIOS secure forward servers, which acts as a relay to an engine that may be behind a firewall. The URL is identical except that instead of using the IP you use a mios forward server followed by the /username/password/serial_number, where the username/password are from the user's mios.com account that he linked to his engine, and serial_number is the unique id of the engine. So, assuming you want to turn on device #5 on engine #10266 and the user linked it his mios account with the username "john" and password "tokyo", you would open this URL (note it's identical to the URL above): https://fwd2.mios.com/john/tokyo/10266/data_request?id=lu_action&DeviceNum=5&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1
NOTE: Because method #2 is much slower than method #1 when both the engine and the controller are on the same network, it is generally preferred to use method #1 when the controller is in the home, and method #2 when the controller is away from the home and needs to talk to the engine through the homeowner's firewall. If you are putting your UI in something that is only on a local network, like a TV, and you do not want to give the user the ability to control an engine outside the home, you may only implement method #1. If the UI is running on something that a mobile phone that has NO wi-fi and can only connect through the mobile network, then you may want to implement method #2 only. Normally, though, a user interface should be able to use both method #1 and method #2, and this is how we spec it for our UI's.
When the controller first starts, display this:
Enter your mios.com username: [__input_box__] [go] __I don't have a mios.com account or I want to specify the IP address.__
Where [go] is a button, [__input_box__] is for the user to type in the username, and the __I don't have... is a link, or button, or similar. If the user clicks the 'I don't have' link, display this:
Enter the IP: [__input_box__] [go]
When the user clicks 'go' attempt to the open this url: http://ip:3480/data_request?id=lu_alive (substitute the actual IP), and if you get back an "OK" in the response, store the IP address in your controller locally (ie a conf file, registry, etc) and continue to the next step. If you don't get an OK, display an error and go back.
If the user supplied a mios.com username, open this URL: http://sta1.mios.com/locator_json.php?username=user (substitute 'user' for the actual username). This will return a list of all the engines you can control, both with method #1 and with method #2, meaning you will see both engines on the local network which may or may not be tied to the user's mios.com account, and you will see engines tied to the mios.com account which may or may not be on the local network. As with most of the requests, the returned data is in JSON format. The data you get back is not formatted with new lines and spaces. If you want to be more human readable for debugging, copy/paste the results into http://jsonlint.com and it will format it nicely. The controller needs to have a json library so it can parse the json responses. The data you get back will be like this:
"units": [ { "serialNumber": "10266", "FirmwareVersion": "1.1.1052", "ipAddress": "192.168.2.117", "name": "skyvera", "users": [ "skyvera", "aaronb" ], "active_server": "fwd2.mios.com", "forwardServers": [ { "hostName": "fwd2.mios.com", "primary": true }, { "hostName": "fwd1.mios.com", "primary": false } ] }, { "serialNumber": "8035", "FirmwareVersion": "1.1.1047", "ipAddress": "192.168.2.116", "users": [ "ovidiu", "alfonsomios", "aaronb", "mrhtn" ], "active_server": "fwd1.mios.com", "forwardServers": [ { "hostName": "fwd1.mios.com", "primary": true }, { "hostName": "fwd2.mios.com", "primary": false } ] }, { "serialNumber": "10516", "FirmwareVersion": "1.1.1047", "ipAddress": "192.168.2.23", "users": [ ], "active_server": "fwd2.mios.com", "forwardServers": [ { "hostName": "fwd2.mios.com", "primary": true }, { "hostName": "fwd1.mios.com", "primary": false } ] } ]
units is an array of JSON objects, one representing each engine. If the tag ipAddress exists for an engine, that means it's available on the local network and can be controlled locally with method #1, otherwise the ipAddress tag will not exist. The users array is a list of all the mios.com usernames which have access to this engine. So, if the username you passed on the locator_json.php URL is also in the users array, then the engine can be controlled remotely with the username, using whatever server is listed in the "active_server" tag. The forwardServers tag lists the primary server for remote access and one or more backups. If the tag "name" exists, that is a name which the user assigned to his engine.
The next screen in the UI should say "What MiOS engine do you want to control?" and then display a list of all the engines with both the serialNumber and name if it exists. You should have 2 icons next to each for 'remote' and 'local' access. In the above example, assuming the username I passed in is "skyvera" 10516 will have the 'local' icon only, since there are no users, and for 8035 it will also be 'local' only because skyvera is not one of the allowed users. For 10266 display both the 'remote' and 'local' icon. At the bottom of the page you can have a legend:
[R icon] - You can access this MiOS engine from anywhere in the world over the internet using your mios.com account
[L icon] - You can access this MiOS engine locally on your home network without going through your mios.com account
Let the user pick the engine he wants to control and store in the controller's local storage the username supplied by the user, and from the JSON file for whatever engine the user picked store the contents of serialNumber, ipAddress, active_server, and store the list of servers in forwardServers.
At this point display:
mios.com password: [__input_box__] [ ] Store my password so I don't have to enter it each time [go]
If the user checks the box, store the password. When the user clicks go, if you stored an ipaddress, test it with:
http://ip:3480/data_request?id=lu_alive
and confirm you get an OK. Then test the password with:
https://xxx.mios.com/username/password/serial/data_request?id=lu_alive and substitute xxx.mios.com for the active_server, and substitute the actual username/password/serial. Again, confirm you get back an OK.
If you do not get an OK for both (or the 2nd one if there was no local ip address), display an error and go back:
Unable to connect. Please check your password. [ok]
The software architecture
Now that you have stored the connection information (ip address, etc.), you can start the main application. From now on, whenever the user starts the UI, it should go straight into the main application and not ask him for the connection information again.
Typically the software for the is written with 2 separate threads: 1. a background poll loop that continuously polls the engine for changes and which updates an object-oriented class structure.
The background poll loop
You should have a global variable in your app with the engine state.
You should have a global variable in your app with the engine state.