Showing posts with label javascript. Show all posts
Showing posts with label javascript. Show all posts

Friday, January 22, 2016

Transform your Pebble Watch into a mouse

Here's a neat little project that I created for my Pebble smartwatch:  MouseRock.

No it's not about Rock'n Roll but about using the Pebble as a mouse for a computer.  I must admit, it's not a very efficient way to interact with a computer but it was fun to experiment with.

The Pebble App

Use CloudPebble.net to create the watch app in Javascript.  Nothing fancy but the following source code was created using Pebble.js

var UI = require('ui');
var serverIP = "http://192.168.0.121:8000";
var Accel = require('ui/accel');

serverIP = localStorage.getItem("serverip");
Accel.config({
  rate:10,
  samples: 1,
  subscribe:true
});
Accel.on('data', function(e) {
  var x = 0;
  var y = 0;
  if (e.accel.x  > 200) x = e.accel.x / 50;
  if (e.accel.y  > 200) y = e.accel.y / 50 *-1;
  if (e.accel.x  < -200) x = e.accel.x / 50;
  if (e.accel.y  < -200) y = e.accel.y / 50 *-1;
  if (x !== 0 || y !== 0){
    //console.log("Z: " + e.accel.z);
    getXML(serverIP + "/?mousex="+x+"&mousey="+y);
  }

});
function getXML(url) {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", url, true);
    xmlhttp.send();
}
var main = new UI.Card({
  title: 'PebbleRock',
  icon: 'images/menu_icon.png',
  subtitle: 'Current server',
  body: serverIP,
  subtitleColor: 'indigo', // Named colors
  bodyColor: '#9a0036' // Hex colors
});
main.show();
main.on("click","up",function(){
  getXML(serverIP + "/?mouseclick=1");
});
main.on("click","down",function(){
  getXML(serverIP + "/?mouserightclick=1");
});
main.on("click","select",function(){

});
The basic idea is to retrieve the data from the accelerometer and send the information to the computer via the HTTP protocol.  Some tweaking has been included for a better mouse handling.  Left and Right buttons have been integrated using the "click" event...

The MouseRock Server

Now that you've created the watch app, you need to create the computer server to handle the data from the Pebble.  Again, nothing fancy, just a simple HTTP server built in Java.  Here's the source code...

package pebblemouse;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.awt.event.InputEvent;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
/**
 *
 * @author patrick
 */
public class MouseRock {
    public static void main(String[] args) throws Exception {
        HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
        server.createContext("/", new MyHandler());
        server.setExecutor(null); // creates a default executor
        System.out.println("MouseRock Server:");
        System.out.println("Server Address: http://" + InetAddress.getLocalHost().getHostName() + ":" + 8000);
        System.out.println("----------------------------");
        System.out.println("Enter this URL into your MouseRock configuration page.");
        server.start();
    }
    static class MyHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange t) throws IOException {
            try {
                java.awt.Robot robot = new java.awt.Robot();
                int currentX = (int) java.awt.MouseInfo.getPointerInfo().getLocation().getX();
                int currentY = (int) java.awt.MouseInfo.getPointerInfo().getLocation().getY();
                String response = "Command received...";
                if (t.getRequestURI().getQuery() != null) {
                    Map<String, String> maps = queryToMap(t.getRequestURI().getQuery());
                    for (String param : maps.keySet()) {
                        String value = maps.get(param);
                        switch (param) {
                            case "mousex":
                                int x = new Double(value).intValue();
                                currentX += x;
                                robot.mouseMove(currentX, currentY);
                                //System.out.println("Mouse X");
                                break;
                            case "mousey":
                                int y = new Double(value).intValue();
                                currentY += y;
                                robot.mouseMove(currentX, currentY);
                                //System.out.println("Mouse Y");
                                break;
                            case "mouseclick":
                                robot.mousePress(InputEvent.BUTTON1_MASK);
                                robot.mouseRelease(InputEvent.BUTTON1_MASK);
                                System.out.println("Mouse Clicked");
                                break;
                            case "mouserightclick":
                                robot.mousePress(InputEvent.BUTTON3_MASK);
                                robot.mouseRelease(InputEvent.BUTTON3_MASK);
                                System.out.println("Mouse Right Clicked");
                                break;
                        }
                    }
                }
                t.sendResponseHeaders(200, response.length());
                OutputStream os = t.getResponseBody();
                os.write(response.getBytes());
                os.close();
            } catch (Exception ex) {
                System.out.println(ex.getMessage());
            }
        }
    }
    public static Map<String, String> queryToMap(String query) {
        Map<String, String> result = new HashMap<>();
        for (String param : query.split("&")) {
            String pair[] = param.split("=");
            if (pair.length > 1) {
                result.put(pair[0], pair[1]);
            } else {
                result.put(pair[0], "");
            }
        }
        return result;
    }
}
What the server does is get the Mouse X, Mouse Y values sent from the Pebble smartwatch and uses the Robot class to move the mouse.  The server is moving the mouse by adding/subtracting a value to the current mouse location.  Since the Pebble watch will send a lot of data, those values must be small increments.  Buttons were also handled by activating them when the "Up" and "Down" were pressed on the Pebble.

Since the server was coded in Java, it should work on any operating system: Linux, Windows or OSX.

The Result

This hack won't replace a real mouse as it is a bit cumbersome to use.  But it's a fun experiment to create a motion activated mouse.

Here's a video of the experiment:



Sunday, March 29, 2015

Webapp for iOS: How to avoid links opening in Safari

If you're a web developer, you may have found out that any website can be added to the home screen of any iOS devices.

It's a feature that is builtin iOS and with the proper meta-tags in your HTML code, you can create a webapp for iPhones and iPads quite easily.

The main issue is when your webpage has links.  The default behaviour in iOS is to open Safari to view the link, meaning that your webapp will open in Safari as soon as the user clicks a link in your webpage.  This is annoying as you may want the link to open inside the webapp and not in Safari.

Do not despair, there is a simply trick that you can use with Javascript to avoid this issue.  Nothing fancy and nothing complicated.  Just a simple line of Javascript will solve this problem...

Here's a common link in a webpage:

<a href="viewer.php">View this link</a>
The problem is that as the user will click this link on his iPhone, Safari will popup to display the content of the link instead displaying the "viewer.php" from inside the webapp.  Instead, create your links as such:
<div onclick="document.location='viewer.php';">View this link</div>
This will load the new webpage and stay inside the webapp instead of opening in Safari.  As a bonus, the "onclick" event is available on almost all elements, meaning that you can transform any element into a clickable link.

The, you just have to use CSS to set the proper look on the element to it looks like it is a link and behaves like a link...

Have fun!