ChumbyOrbiter
This page details how to get an Orbiter running on a chumby...
What is a Chumby?
Have a look here for the details but basically it is an internet device that is designed to sit on a bedside table or something and feed you information in the form of widgets..
Anyway, the widgets that run on this device are flash programs(movies) and there is a version of flashplayer lite on the device.
I figured that this device should be able to act as an orbiter so have developed a flash based orbiter to do just that.
Rather than implement an orbiter in flash I have taken the much easier path of implementing an alternate client to the proxy orbiter. This is the same orbiter that is utilized by the web based orbiter. It works by getting the orbiter display rendered into a bitmap which is then retrieved from a server. Mouse click events, or in this case touches on the screen, send back the co-ordinates of the event to the server and this interacts with the orbiter as if it had received the mouse event itself. A new screen is rendered and the client can retrieve it from the server. There is also a regular refresh that retrieves the latest image from the server so that events happening elsewhere in the system that cause orbiter changes can be displayed on the client.
So all that needs to run on the chumby (or any flash player for that matter) is a fairly lightweight flash movie. It uses a config file to allow the server details to be configured.
Installation
First, get the "SWF" file and a config file onto the chumby. The way I have been doing this is to run the sshd (see the chumby wiki for details) and scp the files over to the chumby /tmp directory. This is a fairly temporary measure and ideally it should be stored on a flash drive which plugs into the back of the chumby.
If sshd is running the you should be able to copy files using scp quite easily (use the root user with no password)
Once you have the file there then you need to create a config file. I usually find that this is easier to create on my dev machine and scp it over as well.
Firstly, if you haven't already - make sure that you have a web obiter installed and running correctly. Most of the config can be taken straight from the web client itself.
You will need to create a file called FlashOrbiter.conf and have it in the same directory as the FlashOrbiter.swf file. It will contain the following variables in a URL encoded format;
userID : this is the numeric id of the user logged in pass : MD5 encoded version of the users password installationID : Numeric identifier for your installation deviceID : Numeric ID of the proxy orbiter device baseurl : URL of pluto admin for your system
So the file might look something like this
userID=1&pass=12345654231&installationID=12345&deviceID=64&baseurl=http://192.168.0.1/pluto-admin/
To find out these values for your installation do the following;
- login to the web based orbiter
- once it is up and visible do "view source" on the page. There a few spots in the page source that will show a string very similar to that above. It will have all the values except the baseurl. Copy this string into your conf file (starting at the userID part and ignoring any command bits at the end. Then add the baseurl part (which you would use to get to web admin) and you should be set.
To run the orbiter, I currently ssh to a terminal on the chumby (details on chumby wiki) and do the following sequence;
- Stop the existing control panel using "stop_control_panel"
- Run the Orbiter using "chumbyflashplayer.x -i FlashOrbiter.swf"
- Use it :)
- Exit it from the command line where you started it with Ctrl-C (exit button doesn't work at the moment)
Known Issues
- Exit button doesn't work. Don't know why, tried a few things already.
- Error handling is pretty poor. Most of the time if it is not working you will still get the last image displayed. It just won't change to a new screen on refresh.
- I have had an error come up with mysql running out of connections. This seems to be a problem with proxyorbiter or the php scripts on the server. Needs investigating. Fix by restarting mysql on core.
Download File:FlashOrbiter.swf.jpg by doing save as and removing the ".jpg" extension.
Source Code
I have developed this using just open source tools and specifically the Action Script Development Tool for eclipse [1]
Even though I have been a developer for years this is my first use of flash and actionscript so if you are experienced with this language/platform then I am certainly open to suggested improvements...
/** * Flash based orbiter for linuxMCE */ /** * */ class FlashOrbiter { /** * Private variables */ private var mc:MovieClip; // Server details var baseUrl; //var baseUrl = "http://192.168.0.1/pluto-admin/"; var imageUrl; var imageUrlPage = "include/image.php?imagepath=/var/www/pluto-admin/security_images/"; //private var postUrl = baseUrl + "weborbiter.php"; static private var targetWidth = 320; static private var targetHeight = 220; private var imageWidth = 0; private var imageHeight = 0; // Time between refreshes (in milliseconds) var refreshInterval = 10000; // Post url parameters var deviceid; var userid; var userpass; var installation; var loader:MovieClipLoader = new MovieClipLoader(); /** * Constructor */ public function FlashOrbiter(_mc : MovieClip) { mc = _mc; var owner:FlashOrbiter = this; // Create the main image movie clip mc.createEmptyMovieClip("image_mc", 1); // Create the buttons mc.createEmptyMovieClip("homeButton", mc.getNextHighestDepth()); mc.homeButton.lineStyle(1,0x000000,100); mc.homeButton._x = 5; mc.homeButton._y = 220; mc.homeButton.lineTo(75,0); mc.homeButton.lineTo(75,20); mc.homeButton.lineTo(0,20); mc.homeButton.lineTo(0,0); mc.homeButton.createTextField("labelText", mc.homeButton.getNextHighestDepth(), 0, 0, mc.homeButton._width, 20); mc.homeButton.labelText.text = "Home"; mc.homeButton.onRelease = function() { owner.sendHome(); }; mc.createEmptyMovieClip("backButton", mc.getNextHighestDepth()); mc.backButton.lineStyle(1,0x000000,100); mc.backButton._x = 80; mc.backButton._y = 220; mc.backButton.lineTo(75,0); mc.backButton.lineTo(75,20); mc.backButton.lineTo(0,20); mc.backButton.lineTo(0,0); mc.backButton.createTextField("labelText", mc.backButton.getNextHighestDepth(), 0, 0, mc.backButton._width, 20); mc.backButton.labelText.text = "Back"; mc.backButton.onRelease = function() { owner.sendBack(); }; mc.createEmptyMovieClip("refreshButton", mc.getNextHighestDepth()); mc.refreshButton.lineStyle(1,0x000000,100); mc.refreshButton._x = 155; mc.refreshButton._y = 220; mc.refreshButton.lineTo(75,0); mc.refreshButton.lineTo(75,20); mc.refreshButton.lineTo(0,20); mc.refreshButton.lineTo(0,0); mc.refreshButton.createTextField("labelText", mc.refreshButton.getNextHighestDepth(), 0, 0, mc.refreshButton._width, 20); mc.refreshButton.labelText.text = "Refresh"; mc.refreshButton.onRelease = function() { owner.loadImage(); }; mc.createEmptyMovieClip("exitButton", mc.getNextHighestDepth()); mc.exitButton.lineStyle(1,0x000000,100); mc.exitButton._x = 230; mc.exitButton._y = 220; mc.exitButton.lineTo(75,0); mc.exitButton.lineTo(75,20); mc.exitButton.lineTo(0,20); mc.exitButton.lineTo(0,0); mc.exitButton.createTextField("labelText", mc.exitButton.getNextHighestDepth(), 0, 0, mc.exitButton._width, 20); mc.exitButton.labelText.text = "Exit"; mc.exitButton.onRelease= function() { owner.clear(); fscommand("quit"); } // Draw and position the buttons // Setup as a movieclip loader listener loader.addListener(this); // Set up a refresh timer setInterval(function() { owner.loadImage(); }, refreshInterval); // Load the config var config:LoadVars = new LoadVars(); config.load("FlashOrbiter.conf"); config.onLoad = function (success) { if (success) { trace (" variables loaded "); for( var prop in this ) { trace (" key " + prop + " = " + this[prop]); } owner.userid = this.userID; owner.installation = this.installationID; owner.userpass = this.pass; owner.deviceid = this.deviceID; trace("BaseURL = " + this.baseurl); owner.baseUrl = this.baseurl; owner.imageUrl = owner.baseUrl + owner.imageUrlPage + owner.deviceid + "_web.png"; trace("ownerURL = " + owner.baseUrl + " image=" + owner.imageUrl); // Can load the first image once config is done owner.loadImage(); } else { trace (" Error loading variables "); } } } /** * Movie clip loader listener function - invoked when loader has finished loading */ function onLoadInit(loadedMC:MovieClip) { trace("onLoadInitCalled! Received image height=" + loadedMC._height + " width=" + loadedMC._width); trace(loadedMC._name); // On first load if (imageHeight == 0) { imageHeight = loadedMC._height; loadedMC._yscale = Math.abs(targetHeight/imageHeight*100); } if (imageWidth == 0) { imageWidth = loadedMC._width; loadedMC._xscale = Math.abs(targetWidth/imageWidth*100); } // Setup a callback for mouse press or touch on image var owner:FlashOrbiter = this; loadedMC.onPress = function() { owner.imagePress(); }; } /** * Movie clip loader listener function - invoked when loader has finished loading */ function onLoadError(targetMC, errorCode) { trace("onLoadErrorCalled! "); } /** * Callback for when the main image has been 'pressed' */ function imagePress() { // Send a touch command to the backend with the correct parameters var xtouch = Math.abs(mc.image_mc._xmouse); var ytouch = Math.abs(mc.image_mc._ymouse); trace("Image Pressed, Using" + xtouch + "x" + ytouch); sendTouch(xtouch,ytouch); } /** * Send a touch command at a position on the image to the backend */ function sendTouch(x,y) { trace("Send touch received x=" + x + " and y=" + y); // Build a loadvars object to POST the parameters to the URL var sendLV:LoadVars = new LoadVars(); sendLV.command = "TOUCH " + x + "x" + y; sendLV.deviceID = deviceid; sendLV.installationID = installation; sendLV.pass= userpass; sendLV.userID = userid; trace("sendLV=" + sendLV.toString()); // Create a target loadVars, and setup listener to refresh the image var resultLV:LoadVars = new LoadVars(); var owner:FlashOrbiter = this; resultLV.onLoad = function() { owner.loadImage(); }; // Do the post sendLV.sendAndLoad(baseUrl + "weborbiter.php", resultLV, "POST"); trace("POST done"); } /** * Send a home command */ function sendHome() { trace("Send home received"); // Build a loadvars object to POST the parameters to the URL var sendLV:LoadVars = new LoadVars(); sendLV.command = "PLUTO_KEY 10"; sendLV.deviceID = deviceid; sendLV.installationID = installation; sendLV.pass= userpass; sendLV.userID = userid; trace("sendLV=" + sendLV.toString()); // Create a target loadVars, and setup listener to refresh the image var resultLV:LoadVars = new LoadVars(); var owner:FlashOrbiter = this; resultLV.onLoad = function() { owner.loadImage(); }; // Do the post sendLV.sendAndLoad(baseUrl + "weborbiter.php", resultLV, "POST"); trace("POST done"); } /** * Send a back command */ function sendBack() { trace("Send back received"); // Build a loadvars object to POST the parameters to the URL var sendLV:LoadVars = new LoadVars(); sendLV.command = "PLUTO_KEY 11"; sendLV.deviceID = deviceid; sendLV.installationID = installation; sendLV.pass= userpass; sendLV.userID = userid; trace("sendLV=" + sendLV.toString()); // Create a target loadVars, and setup listener to refresh the image var resultLV:LoadVars = new LoadVars(); var owner:FlashOrbiter = this; resultLV.onLoad = function() { owner.loadImage(); }; // Do the post sendLV.sendAndLoad(baseUrl + "weborbiter.php", resultLV, "POST"); trace("POST done"); } /** * Function to clear the screen - doesn't work yet! */ function clear() { trace("Clear"); mc.clear(); } /** * Function to start the load of a new image */ function loadImage() { loader.loadClip(this.imageUrl,mc.image_mc); trace("loaded image url " + this.imageUrl); } public static function main() { var flashObiter:FlashOrbiter = new FlashOrbiter(_root); //flashObiter.loadImage(); trace("Main executed"); } }