jueves, 17 de marzo de 2016

DOMÓTICA CON RASPBERRY PI Y ANDROID EN TIEMPO REAL

DOMÓTICA CON RASPBERRY PI Y ANDROID EN TIEMPO REAL









De acuerdo a lo que se aprecia en la gráfica, se puede decir que  Firebase es el punto central del sistema el cual recibe y envía  peticiones en tiempo real a los diferentes clientes que estén conectados a él.

La aplicación Android se conecta a la URL de nuestro proyecto de Firebase, y lo que hace es actualizar y obtener los datos en tiempo real, específicamente cuando se cambia el estado del Switch,  tanto en la aplicación móvil como en la página web, este cambio se ve reflejado de inmediato en Firebase, y como Raspberry Py con Python,  está conectado a la misma URL de Firebase, puede obtener el valor de este  estado y así poder encender o apagar un diodo led.







 


En las imágenes se ve como llega una notificación en tiempo real, cada vez que ha habido un cambio en Firebase

  


Aplicación Web se conecta a la misma URL de Firebase y en tiempo real envía y recibe los datos del estado. 





El dashboard de Firebase





Diagrama esquemático 










CONTENIDO



  • CREAR UN PROYECTO EN FIREBASE
  • CÓDIGO APLICACIÓN MÓVIL ANDROID (JAVA)
  • CÓDIGO APLICACIÓN WEB (HTML/JAVASCRIPT)
  • INSTALANDO LIBRERÍAS NECESARIAS EN RASPBIAN
  • CÓDIGO APLICACIÓN RASPBERRY PI (PYTHON)









CREAR UNA CUENTA EN FIREBASE




Firebase es un proveedor de servicios en la nube,  tambien es un backend como servicio, algunos de sus servicios más destacados son:

  • Base de datos en tiempo real
  • Alojamiento (Hosting) 
  • API de autenticación usando diferentes  redes sociales


Tienen librerías para los principales lenguajes de programación, como Java, Python, Javascript, Swift, entre otros.


Para crear una cuenta es muy sencillo, abrimos el siguiente enlace, nos pide loguearnos con Google, y listo, automáticamente Firebase crea un proyecto, y  también da la opción de crear nuevos proyectos, solo hay que escribir un nombre único para el proyecto, luego le genera una URL, que es la que se va a usar para hacer peticiones y consultas en tiempo real. 












CÓDIGO APLICACIÓN MÓVIL ANDROID (JAVA)



MainActivity.java

package com.rivera.jeffer.domotica;

import android.app.AlertDialog;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.CompoundButton;
import android.widget.Switch;
import com.firebase.client.DataSnapshot;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.firebase.client.ValueEventListener;

public class MainActivity extends AppCompatActivity {

    Switch mySwitch;
    Firebase firebase;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        firebase.setAndroidContext(this);

        Intent intent = new Intent(this, FireService.class);
        startService(intent);

        mySwitch = (Switch)findViewById(R.id.mySwitch);

        firebase = new Firebase("https://android2016.firebaseio.com/luces/sala");

        firebase.addValueEventListener(new ValueEventListener() {

            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                Boolean estado = (Boolean) dataSnapshot.getValue();

                mySwitch.setChecked(estado);

                if(estado){
                    mySwitch.setText("Luz Sala Encendida");

                }else{
                    mySwitch.setText("Luz Sala Apagada");
                }
            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {

            }
        });

        mySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    mySwitch.setText("Luz Sala Encendida");
                    firebase.setValue(true);

                } else {
                    mySwitch.setText("Luz Sala Apagado");
                    firebase.setValue(false);
                }
            }
        });
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        if (id == R.id.action_settings) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Desarrollado Por:");
            builder.setMessage("Jefferson Rivera \nriverajefer@gmail.com\nBogotá Colombia");
            builder.setPositiveButton("OK",null);
            builder.create();
            builder.show();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

}

FireService.java

package com.rivera.jeffer.domotica;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.firebase.client.DataSnapshot;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.firebase.client.ValueEventListener;

public class FireService extends Service {

    private static final String TAG = "FireService";
    protected Firebase firebase;
    private PendingIntent pendingIntent;
    private NotificationManager notificationManager;
    private NotificationCompat.Builder notificationBuilder;
    private  int icono;


    @Override
    public void onCreate() {

        Log.i(TAG, "Service onCreate");
        Firebase.setAndroidContext(this);
        firebase = new Firebase("https://android2016.firebaseio.com/luces/sala");

        Intent myIntent = new Intent(this, MainActivity.class);
        pendingIntent = PendingIntent.getActivity(this, 0, myIntent, 0);
        //Initialize NotificationManager using Context.NOTIFICATION_SERVICE
        notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        Log.i(TAG, "Service onStartCommand");

        //Creating new thread for my service
        //Always write your long running tasks in a separate thread, to avoid ANR
        new Thread(new Runnable() {

            @Override
            public void run() {

                firebase.addValueEventListener(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {

                        Boolean estado = (Boolean) dataSnapshot.getValue();
                        String msgNotificacion;

                        if(estado){
                             msgNotificacion = "Luz Sala Encendida";
                             icono = R.drawable.ic_on;

                        }else{
                             msgNotificacion = "Luz Sala Apagada";
                             icono = R.drawable.ic_foco_off;
                        }
                        Log.v("CAMBIOOOOO DE:",msgNotificacion);

                        notificacion(msgNotificacion, icono);
                    }

                    @Override
                    public void onCancelled(FirebaseError firebaseError) {

                    }
                });

                //Stop service once it finishes its task
                //stopSelf();
            }
        }).start();

        return Service.START_STICKY;
    }


    public void  notificacion(String msg, int icono){

        //Prepare Notification Builder
        notificationBuilder = new NotificationCompat.Builder(this)
                .setContentTitle("Domótica").setSmallIcon(icono).setContentIntent(pendingIntent)
                .setContentText(msg);
        //add sound
        Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        notificationBuilder.setSound(uri);
        long[] v = {80,260,80};
        notificationBuilder.setVibrate(v);
        notificationManager.notify(1, notificationBuilder.build());
    }


    //The system calls this method when another component wants to bind with the service
    @Override
    public void onDestroy() {
        Log.i(TAG, "SERVICIO DESTRUIDO");
    }

    @Override
    public IBinder onBind(Intent arg0) {
        Log.i(TAG, "Service onBind");
        return null;
    }

}

contenido.xml



    



CÓDIGO APLICACIÓN WEB (HTML / JAVASCRIPT)



Estructura del proyecto:




index.html



 


 
 Domótica
    
    
    
    

    
    
    
    


  

DOMÓTICA

Luz Sala


js/script.js

/*
@autor: Jefferson Rivera
@email: riverajefer@gmail.com
*/

$(document).ready(function() {

  //creamos un objeto de firebase, y le pasamos la URL como parametro
  var ref = new Firebase("https://android2016.firebaseio.com/luces/");

  /*****************************************************************
   Obtenemos el valor del último estado 
  ******************************************************************/
  ref.once("value", function(res) {

    var luzSala = res.child("sala").val();
    $('#switch').attr('checked', luzSala); // 
    console.log("Estado actual: " +luzSala)

  });

  /*****************************************************************
   Obtenemos el valor del estado de la luz en tiempo real, 
   cada vez que haya cambio
  ******************************************************************/
  ref.on("child_changed", function(res) {

    var luz_sala = res.val();
    $('#switch').prop('checked', luz_sala);
    console.log("Cambio de estado: " +luz_sala)

  });        

 /*****************************************************************
   Actualizamos el valor, cambiado el estado del Switch 
  ******************************************************************/
  $('#switch').on('change', function(){ 
     if(this.checked) 
      {
          console.log("On")
          ref.update({ sala: true });
      }
      else{
          console.log("Off")
          ref.update({ sala: false });
      }
    });

});

css/estilos.css

body{
  background-color: #2E2E2E;
}

h1{
  font-family: 'Fjalla One', sans-serif;
  text-align: center;
  color: #FFF;
  font-size: 50px;
  text-shadow: 1px 1px 7px rgba(150, 150, 150, 1);
}
h2{
  text-align: center;
  color: #FFF;
  font-size: 45px;
  text-shadow: 1px 1px 7px rgba(150, 150, 150, 1);
}

.bild {
  position:absolute;
  display:none;
  width:1000px;
  height:735px;
}
.bg { 
  position: fixed; 
  top: 0; 
  left: 0;
  z-index: -99;
  /* Preserve aspet ratio */
  min-width: 100%;
  min-height: 100%;  
  z-index: -999;
 }
.bgwidth { width: 100%; }
.bgheight { height: 100%; } 
.switch {
  position: relative;
  display: block;
  vertical-align: top;
  width: 100px;
  height: 30px;
  padding: 3px;
  margin: 0 10px 10px 0;
  background: linear-gradient(to bottom, #eeeeee, #FFFFFF 25px);
  background-image: -webkit-linear-gradient(top, #eeeeee, #FFFFFF 25px);
  border-radius: 18px;
  box-shadow: inset 0 -1px white, inset 0 1px 1px rgba(0, 0, 0, 0.05);
  cursor: pointer;
}
.switch-input {
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
}




INSTALANDO LIBRERÍAS NECESARIAS EN RASPBIAN


sudo apt-get update
sudo apt-get install python-dev
sudo apt-get install python-gpiozero
sudo wget https://bootstrap.pypa.io/get-pip.py
sudo python get-pip.py
sudo pip install requests==1.1.0
sudo pip install python-firebase


CÓDIGO APLICACIÓN RASPBERRY PI (PYTHON)


clases/Conexion.py

from firebase import firebase
import threading
import time

class Conexion(threading.Thread):

    def __init__(self, cb):
        threading.Thread.__init__(self)
        self.callback = cb
        self.fire = firebase.FirebaseApplication('https://android2016.firebaseio.com/', None)
        self.ultimo_estado = self.fire.get('/luces/sala', None)
        self.callback(self.ultimo_estado)

    def run(self):
        E = []
        E.append(self.ultimo_estado)
        i = 0
  
      while True:
       estado_actual = self.fire.get('/luces/sala', None)
       E.append(estado_actual)

       if E[i] != E[-1]:
        self.callback(estado_actual)
   
      del E[0]
      i = i+i
      time.sleep(0.3)

main.py

import sys
import signal
from gpiozero import LED
from clases.Conexion import Conexion

led = LED(17)

def procesa(respuesta):
    print respuesta

    if respuesta:
     led.on()
     print "Encendido"
    else:
     led.off()
     print "Apagado"
    sys.stdout.flush()


try:
 print "Inicio"
 t = Conexion(procesa)
 t.daemon=True
 t.start()
 signal.pause()
except (KeyboardInterrupt, SystemExit):
 raise
 print "Salida"


DESCARGAR EL CÓDIGO DEL PROYECTO 

https://github.com/rpi-jefer/domotica_raspberry_pi







Desarrollado Por:
Jefferson Rivera Patiño
Bogotá Colombia


20 comentarios:

  1. estimado jeferson....mi comentario es porque no puedo hacer el ultimo paso en el raspbian... sudo pip install python-firebase.... me da erores... intente por varios metodos y fue imposible... me interesa mucho tu proyecto para usarlo con alumnos de domotica en arquitectura de la UP. lo mismo que el de reconocimiento de la voz aunque por ahora no funcione con la nueva api de google... quisera saber si me podes dar una mano con este ultimo paso... desde ya muchos saludos!

    ResponderEliminar
    Respuestas
    1. Hola.
      Siga los siguientes pasos:

      sudo apt-get remove python-pip
      sudo wget https://bootstrap.pypa.io/get-pip.py
      sudo python get-pip.py

      sudo pip install requests==1.1.0
      sudo pip install python-firebase



      Me cuenta si le sirvió.

      Saludos






      Eliminar
    2. Hola Jeferson,
      En mi caso, quise empezar desde cero con tu proyecto para practicar y aprender, pero empecé con el error en "sudo pip install requests==1.1.0", pero instalé "==2.0.0"
      Bien, hehco esto, no soy capaz de encender el led, me sale este error:

      Inicio
      Traceback (most recent call last):
      File "main.py", line 24, in
      t = Conexion(procesa)
      File "/home/pi/domotica_raspberry_pi-master/domotica_python/clases/Conexion.py", line 11, in __init__
      self.ultimo_estado = self.fire.get('/luces/sala', None)
      File "/usr/local/lib/python2.7/dist-packages/firebase/decorators.py", line 19, in wrapped
      return f(*args, **kwargs)
      File "/usr/local/lib/python2.7/dist-packages/firebase/firebase.py", line 274, in get
      return make_get_request(endpoint, params, headers, connection=connection)
      File "/usr/local/lib/python2.7/dist-packages/firebase/decorators.py", line 19, in wrapped
      return f(*args, **kwargs)
      File "/usr/local/lib/python2.7/dist-packages/firebase/firebase.py", line 42, in make_get_request
      response.raise_for_status()
      File "/usr/local/lib/python2.7/dist-packages/requests/models.py", line 722, in raise_for_status
      raise HTTPError(http_error_msg, response=self)
      requests.exceptions.HTTPError: 423 Client Error: Locked

      Qué estoy haciendo mal?

      Eliminar
    3. Hola.
      La actualización de este tutorial.
      La puede ver acá.
      https://www.youtube.com/watch?v=pcryAtHpvCE

      Saludos

      Eliminar
  2. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  3. Hola Jeferson , tengo una pegunta ¿la aplicación para el celular la descargaste de la App Store y con que nombre la encuentro ? , después de descargarla hay que modificare algo ?

    ResponderEliminar
    Respuestas
    1. Hola, Julio.
      La aplicación la desarrolle desde cero, Usando el IDE de Android Studio.
      Acá puede ver el proyecto.
      https://github.com/rpi-jefer/domotica_raspberry_pi/tree/master/domotica_app/Domotica

      El apk, se genera cuando compila el proyecto.

      Igual, voy a buscar la última versión del apk, y se las comparto.

      saludos






      Eliminar
  4. Hola Jeferson, excelente proyecto, tengo una pregunta, me baje tu código para probar, y me funciona, pero me cree una cuenta en firebase y en el programa main.py le puse la dirección que me dio firebase del nuevo proyecto que cree y no funciona, hay que hacer algo mas en firebase?

    ResponderEliminar
    Respuestas
    1. Hola Julio, lo que pasa es que Firebase, hace poco se actualizo, por lo cual toca usar otra librería para python.
      No he tenido tiempo para actualizar, pero quinas te pueda servir esto:https://github.com/thisbejim/Pyrebase
      Es la librería de Python para la nueva versión de Firebase.

      Saludos

      Eliminar
  5. Hola para adicionar luces de quartos quais procedimento tks

    ResponderEliminar
  6. HOLA COMPAÑERO SEAS TAN AMABLE DE DEJARME TU WHASAPP NECESITO COMUNICARME CONTIGO

    ResponderEliminar
    Respuestas
    1. Hola.
      Escribeme a mi correo.
      riverajefer@gmail.com


      Eliminar
  7. TEngo una consulta cuando esta funcionando la rasberry pi luego de un tiempo se desconecta automaticamente del servicio y ya no funciona hay q volver a iniciar el main.py como lo puedo hacer para q quede funcionando siemnpre y cuando haya corte de energia o se corte el internet arranque el programa automaticamante gracias amigo

    ResponderEliminar
  8. Buenas tardes mi estimado amigo necesito q me ayude con un problema que tengo cuando en rasbian ejecuto en el terminal el script python funciona de maravilla pero poco tiempo despues me sale el siguiente error y se desconecta tengo que salir de ese proceso y volver a ejecutar el script main.py para q siga funcionando pero deseo q funcione de forma constante que podria hacer gracias:
    Exception in thread Thread-1:
    Traceback (most recent call last):
    File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
    self.run()
    File "/var/www/html/python/clases/Conexion.py", line 22, in run
    estado_actual = self.fire.get('/luces', None)
    File "/usr/local/lib/python2.7/dist-packages/firebase/decorators.py", line 19, in wrapped
    return f(*args, **kwargs)
    File "/usr/local/lib/python2.7/dist-packages/firebase/firebase.py", line 274, in get
    return make_get_request(endpoint, params, headers, connection=connection)
    File "/usr/local/lib/python2.7/dist-packages/firebase/decorators.py", line 19, in wrapped
    return f(*args, **kwargs)
    File "/usr/local/lib/python2.7/dist-packages/firebase/firebase.py", line 38, in make_get_request
    response = connection.get(url, params=params, headers=headers, timeout=timeout)
    File "/usr/lib/python2.7/dist-packages/requests/sessions.py", line 469, in get
    return self.request('GET', url, **kwargs)
    File "/usr/lib/python2.7/dist-packages/requests/sessions.py", line 457, in request
    resp = self.send(prep, **send_kwargs)
    File "/usr/lib/python2.7/dist-packages/requests/sessions.py", line 569, in send
    r = adapter.send(request, **kwargs)
    File "/usr/lib/python2.7/dist-packages/requests/adapters.py", line 407, in send
    raise ConnectionError(err, request=request)
    ConnectionError: ('Connection aborted.', BadStatusLine("''",))

    ResponderEliminar
    Respuestas
    1. lograste solucionar el problema? me pasa lo mismo

      Eliminar
  9. Hola Jeferson. Muchas gracias por todo el trabajo que haces.
    Estoy haciendo un proyecto parecido al suyo pero he implementado un boton, el cual cuando lo presiono quiero que cambie el estado de mi variable en firebase de True a False, y no sé que código tengo que utilizar para realizar esto. Si puediera ayudarme se lo agradecería mucho. Un saludo!

    ResponderEliminar
    Respuestas
    1. Hola.
      Lo que pasa es que, toca usar ahora la nueva versión de Firebase, tanto en Android, como en Python.

      Escríbame y le envío información:
      riverajefer@gmail.com


      Eliminar
    2. Este comentario ha sido eliminado por el autor.

      Eliminar
  10. Amigo será que se podría trabajar en APache y MySql, y remplazar el FireBase?

    ResponderEliminar
  11. Hola Jeferson! De verdad quiero agradecerle por esta util informacion tan bien estructuradas y en español que nos ayuda a los latinos montones!!!! especialmente a mi que estoy empezando con los temas de android :) Solo te queria pedir una ayuda cuando compilo la aplicacion en android studio me saca el siguiente error:

    Configuration 'compile' is obsolete and has been replaced with 'implementation'.
    It will be removed at the end of 2018

    Configuration 'testCompile' is obsolete and has been replaced with 'testImplementation'.
    It will be removed at the end of 2018

    Configuration 'testApi' is obsolete and has been replaced with 'testImplementation'.
    It will be removed at the end of 2018

    Failed to resolve: com.google.firebase:firebase-messaging:12.0.1

    Me puedes por favor guiar en como se resuelve?

    Mil gracias y un fuerte abrazo!!

    Saludos

    Andres

    ResponderEliminar