giovedì 10 settembre 2015

DIY HomeKit con Android Wear e Raspberry

Questo e' un tentativo di realizzare un homekit, un sistema di domotica, che permetta di accendere dei dispositivi con un sensore di prossimita' (tipo il lampadario quando si entra in una stanza)

I componenti di questo kit sono
1) un orologio con Android Wear
2) un Raspberry (qualsiasi modello) con un dongle Bluetooth LE
3) un rele



L'orologio e' stato programmato come emettitore di beacon (e' possibile modificare il valore del minor in modo da personalizzarlo)


Di seguito il codice Android

--------------------------------------
apply plugin: 'com.android.application'

android {
    compileSdkVersion 21    buildToolsVersion "21.1.2"
    defaultConfig {
        applicationId "luca_innocenti.beacontrasmitter"        minSdkVersion 21        targetSdkVersion 21        versionCode 1        versionName "1.0"    }
    buildTypes {
        release {
            minifyEnabled false            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }
    }
}

final def var = repositories {
    jcenter{
        url "http://jcenter.bintray.com/"    }
}
var

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.google.android.support:wearable:1.2.0'    compile 'com.google.android.gms:play-services-wearable:7.5.0'}
dependencies {
    compile 'org.altbeacon:android-beacon-library:2+'
} 

--------------------------------------

--------------------------------------
package luca_innocenti.beacontrasmitter;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.wearable.view.WatchViewStub;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.pm.PackageManager;

import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconParser;
import org.altbeacon.beacon.BeaconTransmitter;
import org.altbeacon.beacon.Identifier;

import java.util.Arrays;


@TargetApi(21)
public class MainActivity extends Activity {

    private BeaconTransmitter mBeaconTransmitter;
    private TextView testo;
    private SeekBar barra;
    private Button pulsante;
    private int stato;
    private TextView testo2;
    private TextView testo3;


    private void trasmetti(){
        if (checkPrerequisites()) {
            //            //mBeaconTransmitter = new BeaconTransmitter(this, new BeaconParser().setBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"));            mBeaconTransmitter = new BeaconTransmitter(this, new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
            //beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:0-3=4c000215,i:4-19,i:20-21,i:22-23,p:24-24"));  // iBeacons
            //beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));      // Estimotes
            //beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:0-3=a7ae2eb7,i:4-19,i:20-21,i:22-23,p:24-24"));  // easiBeacons
            // uuid estimote B9407F30-F5F8-466E-AFF9-25556B57FE6D
            Beacon beacon = new Beacon.Builder()
                    .setId1("B9407F30-F5F8-466E-AFF9-25556B57FE6D")
                    .setId2("999")
                    .setId3(testo.getText().toString())
                    .setManufacturer(0x015D) // Simula Estimote                    .setTxPower(-59)
                    .setDataFields(Arrays.asList(new Long[]{0l}))
                    .build();

            mBeaconTransmitter.startAdvertising(beacon);
            stato = 1;
        }

    }

    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);



        final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override            public void onLayoutInflated(WatchViewStub stub) {
                barra = (SeekBar) stub.findViewById(R.id.seekBar);
                testo = (TextView) stub.findViewById(R.id.textView);
                testo2 = (TextView) stub.findViewById(R.id.textView2);
                testo3 = (TextView) stub.findViewById(R.id.textView3);
                pulsante = (Button) stub.findViewById(R.id.button);


                barra.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                                                     @Override                                                     public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                                                         testo.setText(String.valueOf(new Integer(progress)));
                                                     }

                                                     @Override                                                     public void onStartTrackingTouch(SeekBar seekBar) {

                                                     }

                                                     @Override                                                     public void onStopTrackingTouch(SeekBar seekBar) {

                                                     }
                                                 }
                );

                pulsante.setOnClickListener(new View.OnClickListener() {
                    @Override                    public void onClick(View v) {
                        testo2.setText("Trasmitter ON");
                        testo3.setText("B9407F30-F5F8-466E-AF\nF9-25556B-57FE6D-999-"+testo.getText());
                        pulsante.setEnabled(false);
                        barra.setEnabled(false);
                        trasmetti();
                    }
                });
            }
        });
    }


    @Override    protected void onDestroy() {
        super.onDestroy();
        if (stato == 1) {
            mBeaconTransmitter.stopAdvertising();
        }
    }

    @TargetApi(21)
    private boolean checkPrerequisites() {

        if (android.os.Build.VERSION.SDK_INT < 18) {
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Bluetooth LE not supported by this device's operating system");
            builder.setMessage("You will not be able to transmit as a Beacon");
            builder.setPositiveButton(android.R.string.ok, null);
            builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                @Override                public void onDismiss(DialogInterface dialog) {
                    finish();
                }

            });
            builder.show();
            return false;
        }
        if (!getApplicationContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Bluetooth LE not supported by this device");
            builder.setMessage("You will not be able to transmit as a Beacon");
            builder.setPositiveButton(android.R.string.ok, null);
            builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                @Override                public void onDismiss(DialogInterface dialog) {
                    finish();
                }

            });
            builder.show();
            return false;
        }
        if (!((BluetoothManager) getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter().isEnabled()){
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Bluetooth not enabled");
            builder.setMessage("Please enable Bluetooth and restart this app.");
            builder.setPositiveButton(android.R.string.ok, null);
            builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                @Override                public void onDismiss(DialogInterface dialog) {
                    finish();
                }

            });
            builder.show();
            return false;

        }

        try {
            // Check to see if the getBluetoothLeAdvertiser is available.  If not, this will throw an exception indicating we are not running Android L            ((BluetoothManager) this.getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter().getBluetoothLeAdvertiser();
        }
        catch (Exception e) {
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Bluetooth LE advertising unavailable");
            builder.setMessage("Sorry, the operating system on this device does not support Bluetooth LE advertising.  As of July 2014, only the Android L preview OS supports this feature in user-installed apps.");
            builder.setPositiveButton(android.R.string.ok, null);
            builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
                @Override                public void onDismiss(DialogInterface dialog) {
                    finish();
                }

            });
            builder.show();
            return false;

        }

        return true;
    }
}

--------------------------------------
Per leggere la prossimita' dell'orologio ho montato sulla Raspberry un dongle Broadcome (gia' visto qui) ed il programma in Python iBeacon-Scanner (Github) leggermente modificato

Il programma cicla fino quando non vede un dispositivo con minor=1, allora controlla l'Rssi e stima la distanza. Se la distanza e' inferiore ad un determinato valore si puo' agire sulle porte GPIO ed attivare il rele' (funzione da implementare ma di facilissima realizzazione)

--------------------------------------
import blescan
import sys

import bluetooth._bluetooth as bluez

dev_id = 0
try:
    sock = bluez.hci_open_dev(dev_id)
    #print "ble thread started"

except:
    print "error accessing bluetooth device..."
        sys.exit(1)

blescan.hci_le_set_scan_parameters(sock)
blescan.hci_enable_le_scan(sock)

while True:
    returnedList = blescan.parse_events(sock, 10)
    #print "----------"
    for beacon in returnedList:
        #print beacon
        valori = beacon.split(",")
        mac = valori[0]
        uuid = valori[1]
        major = valori[2]
        minor = valori[3]
        rssi = valori[4]
        rssi2 = valori[5]
        #print minor
        if (minor == "1"):
            #print minor+" "+rssi2
            if (int(rssi2) > -75):
                print "vicino"
            else:
                print "lontano"
--------------------------------------