Saturday, November 15, 2014

Controlling a powerpoint presentation from the PEBBLE watch

The winRemoteControl api is HTTP based and therefore any http client can talk to the winRemoteControl Server to remote control a Windows machine.

I developed a client for the PEBBLE Watch, that simply send the keystrokes:

  • RIGHT
  • LEFT
  • ESCAPE
 With this I can control a PowerPoint presentation from the Watch.



To create the PEBBLE app, I used the WinFormebble C library, that make developing for the Watch more friendly to C# and WinForm developer.

The all source code is part of the library WinFormebble on github.

Here is the C part of the app
   
/*
 * A Pebble Watchface that control a PowerPoint presentation
 * using the winRemoteControl api
 * Demo for Code Camp 22, MA, USA
 * (C) Torres Frederic 2014
 * Licence: MIT
 */
#include <pebble.h>  
#include <pebble_fonts.h>
#include "WinFormebble.h"

// Api data returned from JavaScript World to C world
// Dictionary properties used in communication with Pebble iOS App
// Metadata defined in project settings    
#define KEY_REQUEST_ID 0
#define KEY_REQUEST_ID_NEXT 0
#define KEY_REQUEST_ID_PREVIOUS 1
#define KEY_REQUEST_ID_QUIT 2
#define KEY_REQUEST_ID_OK = 3;
    
// Data Refresh Rate
#define DEFAULT_STRING_BUFFER_SIZE 16    

// Strings and messages
#define WATCH_DIGIT_BUFFER "00:00"
    
#define WATCH_MAX_WIDTH 144    
    
Form mainForm;

    Label lblTime;
    Label lblAction;
    Label lblPage;

    int _pageCounter = 1;

    private void butSelect_Click(ClickRecognizerRef recognizer, void *context) {
        
        Label_SetText(lblAction, "Quit");
        jsCom_SendIntMessage(KEY_REQUEST_ID, KEY_REQUEST_ID_QUIT);
    }
    private void butUp_Click(ClickRecognizerRef recognizer, void *context) {
        
        Label_SetText(lblAction, "Previous");
        jsCom_SendIntMessage(KEY_REQUEST_ID, KEY_REQUEST_ID_PREVIOUS);
        _pageCounter--;
        if(_pageCounter == 0)
            _pageCounter = 1;
    }
    private void butDown_Click(ClickRecognizerRef recognizer, void *context) {
        
        Label_SetText(lblAction, "Next");
        jsCom_SendIntMessage(KEY_REQUEST_ID, KEY_REQUEST_ID_NEXT);
        _pageCounter++;
    }
    private void mainForm_Load(Window *window) {
        
        int y = 15;
        lblTime = Label_New(GRect(0, y, WATCH_MAX_WIDTH, 50), WhiteBackground, GTextAlignmentCenter, FONT_KEY_BITHAM_30_BLACK);
        Label_SetText(lblTime, WATCH_DIGIT_BUFFER);
        Form_AddLabel(mainForm, lblTime);
        y += 45;
        
        lblAction = Label_New(GRect(0, y, WATCH_MAX_WIDTH, 50), WhiteBackground, GTextAlignmentCenter, FONT_KEY_BITHAM_30_BLACK);
        Label_SetText(lblAction, "None");
        Form_AddLabel(mainForm, lblAction);
        y += 45;
        
        lblPage = Label_New(GRect(0, y, WATCH_MAX_WIDTH, 50), WhiteBackground, GTextAlignmentCenter, FONT_KEY_BITHAM_30_BLACK);
        Label_SetText(lblPage, "Page: 1");
        Form_AddLabel(mainForm, lblPage);
        
        Form_RegisterButtonHandlers(mainForm, butSelect_Click, butUp_Click, butDown_Click);
    }
    private void mainForm_Unload(Window *window) {
        
    }  
    private void mainForm_UpdateTime() {    
        
        struct tm *tick_time   = DateTime_Now();
        static char timeBuffer [DEFAULT_STRING_BUFFER_SIZE];
        Label_SetText(lblTime,  StringFormatTime(tick_time, "%T", timeBuffer));
    }
    private void mainForm_Timer(struct tm *tick_time, TimeUnits units_changed) {
        
        mainForm_UpdateTime();                 
    }
    private void  mainForm_InboxReceivedCallback(DictionaryIterator *iterator, void *context) {
        static char pageCounterBuffer [DEFAULT_STRING_BUFFER_SIZE];
        Tuple * t = dict_read_first(iterator);
        while(t != NULL) {
            
            switch(t->key) {
                case KEY_REQUEST_ID : 
                    Label_SetText(lblPage, StringFormatInt(_pageCounter, "Page: %d", pageCounterBuffer));
                break;
                default             : APP_LOG(APP_LOG_LEVEL_ERROR, "Key %d not recognized!", (int)t->key); break;
            }
            t = dict_read_next(iterator);
        }
    }

int main(void) { 
    
    mainForm = Form_New();
    Form_Initialize(mainForm, mainForm_Load, mainForm_Unload);
    Form_Show(mainForm);
    mainForm_UpdateTime();
    jsCom_Initialize(mainForm_InboxReceivedCallback);
    Form_RegisterWatchFaceTimer(SECOND_UNIT, mainForm_Timer);    
        
    app_event_loop();
    
    Form_Destructor(mainForm);  // Also clean all associated controls
}

Here is the JavaScript part of the app
   
/*
 * A Pebble Watchface that control a PowerPoint presentation
 * using the winRemoteControl api
 * Demo for Code Camp 22, MA, USA
 * (C) Torres Frederic 2014
 * Licence: MIT
 */

var COMPUTER_IP             = "137.116.241.239";
var KEY_REQUEST_ID_NEXT     = 0;
var KEY_REQUEST_ID_PREVIOUS = 1;
var KEY_REQUEST_ID_QUIT     = 2; 
var KEY_REQUEST_ID_OK       = 3; 

var URL            = 'http://[IP]:1964/WinRemoteControl?json=';
var URL_JSON_PARAM = '{"Action":2,"Keys":"{[KEY]}"}';

function sendCommand(key) {
    
    var param, keyValue;
    switch(key) {
        case KEY_REQUEST_ID_NEXT     : keyValue = "RIGHT";  break;
        case KEY_REQUEST_ID_PREVIOUS : keyValue = "LEFT";   break;
        case KEY_REQUEST_ID_QUIT     : keyValue = "ESCAPE"; break;
        default: return;
    }
    param     = URL_JSON_PARAM.replace("[KEY]", keyValue);
    var url   = URL.replace('[IP]', COMPUTER_IP);
    Trace(url + param);
    url      += encodeURIComponent(param);
    
    httpGet(url,
        function(responseText) {            
            var dictionary  = { KEY_REQUEST_ID : KEY_REQUEST_ID_OK };
            sendMessageToWatch(dictionary, "sendCommand");
        }      
    );
}

function Init() {
    
    Pebble.addEventListener('ready',  // Listen for when the watchface is opened
        function(e) {
            Trace("App started, connected:{0}, Browser Agent:{1}".format(e.ready, navigator.userAgent));
        }
    );    
    Pebble.addEventListener('appmessage', // Listen for when an AppMessage is received
        function(e) {
            Trace("Requested From App:"+JSON.stringify(e.payload));
            sendCommand(e.payload.KEY_REQUEST_ID);
        }
    );
}

Init();

Thursday, October 16, 2014

win Remote Control - JavaScript Hello World

winRemoteControl is a JavaScript based framework to create Remote Control or Dashboard for the Internet Of Things. For now it support only iOS (It is written in C# and is portable to Android, Windows, MacOs and Linux).

Here is the Hello World program.
/*
 * winRemoteControl - Hello World
 */

// Create a button PrintHelloWorld in the remote
function PrintHelloWorld() {

    this.Type   = "Button"; // Optional Control Type
    this.Height = 75; // Optional Control Height
    this.Text   = "Print Hello World"; // Control Text
    
    this.run = function() { // Executed when the button is touched

        var msg = "Hello World!";
        this.notify( msg, new Date());
        this.getLabel1().setText(msg);
        return true;
    }
}
// Create a label in the remote
function Label1() {

    this.Type = "Label";
    this.Text = "Ready...";
}

// Remote Implementation
function Remote() {

    this.initialize = function() {
        this.Actions = {
            PrintHelloWorld : PrintHelloWorld,
            Label1         : Label1,
        }
        this.Text              = "My Hello World"; // Remote Text
        this.ControlGap        = 60;
        this.ControlYStart     = 120;
        this.RequireComputerIp = false; // This remote does not talk to any thing
    };
}


Introducing winRemoteControl

What is winRemoteControl?

The first idea was to control and visualize a PowerPoint presentation running on a Windows machine from an iPhone.


Then we added the idea to send more commands to the Windows machine
  • sendKeys()
  • executeProgram
  • killProgram
  • executeProgramAsBatch
Then we created a JavaScript based plug in framework that allow to write Programmable Remote Control and Dashboard for a Windows machine, but also for any device or system that talk HTTP.

winRemoteControl became the Programmable Remote Control and Dashboard for IoT.

winRemoteControl Components

There are 3 components in the winRemoteControl family


About this Blog

 This blog is about all aspect winRemoteControl, but also about programming the IoT with C# and/or JavaScript.
 

Saturday, October 4, 2014

win Remote Control is almost there

Almost there

winRemoteControl is an iPhone app that allows to send simple keyboard commands to a Windows machine to for example control a PowerPoint presentation.