Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Tuesday, April 24, 2018

Controlling invisible machines with emails, from Java

Here is the problem

You have your network at home, with several machines connected to it (laptops, tablets, Raspberry PIs, phones, etc). Your home network is a Local Area Network (aka LAN), the machines can see each other, but they cannot be seen from outside, from the Internet.
You may very well want to deal with those machines while away from home, to restart services, launch a new program, or even reboot.
In the configuration mentioned above, this is simple, you just cannot do it. And that is frustrating!
There is a way though. Those machines on your home LAN can send and receive emails...

Using JavaMail

JavaMail is a Java package that has been available for ever, it understands the email protocols (IMAP, POP3, SMTP, etc), and can be used to interact with email accounts programmatically.

An example

There is an example of such an interaction on this github repository.
The fastest way to get it running is to run the following commands (these are for Linux - and MacOS - on Windows, use the git shell):
$ git clone https://github.com/OlivierLD/raspberry-coffee.git
$ cd raspberry-coffee
$ cd common-utils
$ ../gradlew shadowJar
$ cp email.properties.sample email.properties
$ vi email.properties
$ # Here you modify your properties file to match your email account
$ java -cp ./build/libs/common-utils-1.0-all.jar email.examples.EmailWatcher -send:google -receive:google
The -send:google -receive:google depends on the settings in your email.properties.
Then, to the account mentioned in the email.properties, send a message like this:
Subject: execute
Content:
whoami
ifconfig
uname -a
Note: this example requires the content to be in plain/text.
Once the message is received by the EmailWatcher, it sends you an acknowledgement:
Then, the 3 commands are processed by the EmailWatcher, you would see in its console an output like that:
Start receiving.
Received:
whoami
ifconfig
uname -a

Operation: [execute], sent for processing...
pi
lo0: flags=8049 mtu 16384
 options=1203
 inet 127.0.0.1 netmask 0xff000000 
 inet6 ::1 prefixlen 128 
...
And finally, you receive an email like that:
... meaning that the commands you've sent have been executed.

You can also attach the script to execute to a blank email, with topic execute-script:
Attach a file like this:

#!/bin/bash
whoami
ifconfig
ps -ef | grep EmailWatcher
... and just wait for the result to come back to you:
Scripts execution returned: 
pi
eth0: flags=4099  mtu 1500
        ether a4:ba:db:c9:04:2e  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        device interrupt 18  

lo: flags=73  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10
        loop  txqueuelen 1  (Local Loopback)
        RX packets 9215  bytes 2022884 (1.9 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 9215  bytes 2022884 (1.9 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wlan0: flags=4163  mtu 1500
        inet 192.168.42.3  netmask 255.255.255.0  broadcast 192.168.42.255
        inet6 fe80::4038:1f53:b94f:ccc2  prefixlen 64  scopeid 0x20
        ether 78:e4:00:78:ad:8f  txqueuelen 1000  (Ethernet)
        RX packets 8848724  bytes 696021134 (663.7 MiB)
        RX errors 0  dropped 0  overruns 0  frame 18848059
        TX packets 6040965  bytes 795472510 (758.6 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        device interrupt 17  base 0xc000  

pi      12476 12472  1 16:39 pts/0    00:00:53 java -cp ./build/libs/RasPISamples-1.0-all.jar weatherstation.email.EmailWatcher -send:google -receive:google
pi      16204 16199  0 18:04 pts/0    00:00:00 grep EmailWatcher
>> sh ./attachments/2018-04-26_18-04-27/sample.sh returned status 0

Comments

This process is not synchronous, this could be a drawback... But still, it allows you to interact remotely with machines invisible from the Internet.

Having the command

java -cp ./build/libs/common-utils-1.0-all.jar email.examples.EmailWatcher -send:google -receive:google
fired when the machine boots will allow you make sure it is waiting for your emails as soon as the machine is up.

This EmailWatcher as it is also allows you to execute scripts, attached to the email. Look into the code for details ;)
It is even possible to ssh to another machine and execute a bunch of commands stored in a script... The command you send in the email's body would be like

ssh pi@192.148.42.13 bash -s < ~/nodepi.banner.sh
If a password is required, use sshpass:
sshpass -p 'secret-password' ssh pi@192.148.42.13 bash -s < ~/nodepi.sudo.sh
You can even sudo:
echo 'secret-password' | sudo -S privilegedCommand
This can be dangerous, hey? With great power come great responsibilities...

Saturday, March 17, 2018

Java Weather Station

This is a SwitchDocLabs SDLWeather80422 Weather Station, installed on the roof.

It is connected to a Raspberry PI A+, all the software is written in Java, no Python, no Arduino-like code, no C++.

There is an optional nodeJS server that runs on the Raspberry PI too, to enable WebSockets.

Find the core code here, and the example implementation here.

MySQL, PHP, Web Interface
Web-Components interface, pings the server every second.
WebSocket Web Interface, updated in real-time.
Some papers by John Shovic turned out to be very useful, specially in understanding what this debounce aspect is all about.

See it live here.

It even comes with a pebble application.

Friday, March 21, 2014

Persistence Of Vision (POV) first tests

I got the small "miniPOV" kit from Adafruit, assembled it, and attached it to the a spoke of the back wheel of a bike... The first tests are already positive.

The text you read should say "Make!".
Next, I'll be working on some utilities to help people make their own text and upload them to the chip. Stay tuned.

Thursday, December 12, 2013

Control the Raspberry PI through email, in Java

The Problem

You'll find many articles showing how to use a web server like Apache to control your Raspberry PI over the Internet.
What they don't say - or don't say loud enough - is that to do so, your web server needs to be visible to (and from) the outside world.
Otherwise, you cannot access it outside of your home or ad-hoc network.
What those articles present is a nice way to:
  • Know what the conditions are on the Raspberry PI
  • Ask remotely to the Raspberry PI to take some action
For example
You are at work, the Raspberry PI is at home.
Your Raspberry PI has a temperature sensor connected to it, and it is also connected to some heater switch.
  • When the temperature in the home drops below a given threshold, it sends you a message (through whatever channel, WebSocket or so)
  • On reception of the message, you - wherever you are - decide to ask the Raspberry PI to turn the heater on
To do so, you need to be on the same network as the Raspberry PI, or the network the Raspberry PI is on needs to be visible from your location.
This does not happen if you don't have a registered IP address for your Raspberry PI.
It kind of kills the story...

A Solution

A way to address this problem is to use emails.
This will not implement real-time messaging - like WebSocket - but this will allow a form of communication, only relying on email service - like Yahoo! or Google. Those providers offer free email accounts, which is all you would need.

Required Components

A way to address this problem is to All you need on the client side (ie you, at work) is a browser, from which you can send and receive emails.
On the server side (ie the Raspberry PI, at home) you will need: The json parser is used to parse the content of the email messages. We choosed this technology in this example; others can be used as well, like XML, plain text messages, etc. Your call.

The Java code

All the sources are available in an archive you can get at the bottom of this document.
email.properties
All properties concerning the emails accounts are externalized in this properties file.
Lines 1 to 5 concern the origins, and the destinations of the emails going back and forth, as well as the subjects used for the emails (events, acknowledgements, and requests).
Notice the URL at the top of the file (line 7), it could help you to find the servers you are interested in.
pi4j.gpio.RaspberryPIEventListener.java
This is the interface used for the callback invoked when an event is triggered on the Reaspberry Pi's side. Namely, when the button will be pressed.
      

     1  package pi4j.gpio;
     2  
     3  import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent;
     4  
     5  public interface RaspberryPIEventListener
     6  {
     7    public void manageEvent(GpioPinDigitalStateChangeEvent event);
     8  }
      
      
pi4j.email.EmailReceiver.java
This is the utility class used to receive emails. Its behavior heavily depends on the values set in email.properties.
Do check out the method named setProps() (line 119). It might need to be tweaked to fit your email environment.
      

     1  package pi4j.email;
     2  
     3  import java.io.BufferedReader;
     4  import java.io.File;
     5  import java.io.FileInputStream;
     6  import java.io.FileOutputStream;
     7  import java.io.IOException;
     8  import java.io.InputStream;
     9  import java.io.InputStreamReader;
    10  import java.io.OutputStream;
    11  
    12  import java.util.ArrayList;
    13  import java.util.List;
    14  import java.util.Properties;
    15  import java.util.Set;
    16  
    17  import javax.mail.Address;
    18  import javax.mail.Flags;
    19  import javax.mail.Folder;
    20  import javax.mail.Message;
    21  import javax.mail.Multipart;
    22  import javax.mail.Part;
    23  import javax.mail.PasswordAuthentication;
    24  import javax.mail.Session;
    25  import javax.mail.Store;
    26  import javax.mail.internet.InternetAddress;
    27  import javax.mail.search.AndTerm;
    28  import javax.mail.search.FlagTerm;
    29  import javax.mail.search.FromStringTerm;
    30  import javax.mail.search.OrTerm;
    31  import javax.mail.search.SearchTerm;
    32  import javax.mail.search.SubjectTerm;
    33  
    34  public class EmailReceiver
    35  {
    36    private static String protocol;
    37    private static int outgoingPort;
    38    private static int incomingPort;
    39    private static String username;
    40    private static String password;
    41    private static String outgoing;
    42    private static String incoming;
    43    private static String replyto;
    44    private static boolean smtpauth;
    45    
    46    private static String sendEmailsTo;
    47    private static String acceptEmailsFrom;
    48    private static String acceptSubject;
    49    private static String ackSubject;
    50    
    51    private static boolean verbose = "true".equals(System.getProperty("verbose", "false"));
    52  
    53    private EmailSender emailSender = null; // For Ack
    54    private String provider = null;
    55    
    56    public EmailReceiver(String provider) throws RuntimeException
    57    {
    58      this.provider = provider;
    59      EmailReceiver.protocol = "";
    60      EmailReceiver.outgoingPort = 0;
    61      EmailReceiver.incomingPort = 0;
    62      EmailReceiver.username = "";
    63      EmailReceiver.password = "";
    64      EmailReceiver.outgoing = "";
    65      EmailReceiver.incoming = "";
    66      EmailReceiver.replyto = "";
    67      EmailReceiver.smtpauth = false;
    68      
    69      EmailReceiver.sendEmailsTo = "";
    70      EmailReceiver.acceptEmailsFrom = "";
    71      EmailReceiver.acceptSubject = "";
    72      EmailReceiver.ackSubject = "";
    73  
    74      Properties props = new Properties();
    75      String propFile = "email.properties";
    76      try
    77      {
    78        FileInputStream fis = new FileInputStream(propFile);
    79        props.load(fis);
    80      }
    81      catch (Exception e)
    82      {
    83        System.out.println("email.properies file problem...");
    84        throw new RuntimeException("File not found:email.properies");
    85      }
    86      EmailReceiver.sendEmailsTo     = props.getProperty("pi.send.emails.to");
    87      EmailReceiver.acceptEmailsFrom = props.getProperty("pi.accept.emails.from");
    88      EmailReceiver.acceptSubject    = props.getProperty("pi.email.subject");
    89      EmailReceiver.ackSubject       = props.getProperty("pi.ack.subject");
    90      
    91      EmailReceiver.protocol     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.protocol");
    92      EmailReceiver.outgoingPort = Integer.parseInt(props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "outgoing.server.port", "0"));
    93      EmailReceiver.incomingPort = Integer.parseInt(props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "incoming.server.port", "0"));
    94      EmailReceiver.username     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.username",   "");
    95      EmailReceiver.password     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.password",   "");
    96      EmailReceiver.outgoing     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "outgoing.server", "");
    97      EmailReceiver.incoming     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "incoming.server", "");
    98      EmailReceiver.replyto      = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.replyto",    "");
    99      EmailReceiver.smtpauth     = "true".equals(props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.smtpauth", "false"));
   100      
   101      if (verbose)
   102      {
   103        System.out.println("Protocol:" + EmailReceiver.protocol);
   104        System.out.println("Usr/pswd:" + EmailReceiver.username + "/" + EmailReceiver.password);
   105      }
   106    }
   107  
   108    private static SearchTerm[] buildSearchTerm(String str)
   109    {
   110      String[] sa = str.split(",");
   111      List<SearchTerm> lst = new ArrayList<SearchTerm>();
   112      for (String s : sa)
   113        lst.add(new FromStringTerm(s.trim()));
   114      SearchTerm[] sta = new SearchTerm[lst.size()];
   115      sta = lst.toArray(sta);
   116      return sta;
   117    }
   118    
   119    private Properties setProps()
   120    {
   121      Properties props = new Properties();
   122      props.put("mail.debug", verbose?"true":"false");
   123      
   124      // TASK smtp should be irrelevant for a receiver
   125      props.put("mail.smtp.host", EmailReceiver.outgoing);
   126      props.put("mail.smtp.port", Integer.toString(EmailReceiver.outgoingPort));
   127  
   128      props.put("mail.smtp.auth", "true");
   129      props.put("mail.smtp.starttls.enable", "true"); //  See http://www.oracle.com/technetwork/java/faq-135477.html#yahoomail
   130    //  props.put("mail.smtp.starttls.required", "true");
   131      props.put("mail.smtp.ssl.enable", "true");
   132  
   133      if ("pop3".equals(EmailReceiver.protocol))
   134      {
   135        props.setProperty("mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
   136        props.setProperty("mail.pop3.socketFactory.fallback", "false");
   137        props.setProperty("mail.pop3.port", Integer.toString(EmailReceiver.incomingPort));
   138        props.setProperty("mail.pop3.socketFactory.port", Integer.toString(EmailReceiver.incomingPort));
   139      }            
   140  
   141      if ("imap".equals(protocol))
   142      {
   143        props.setProperty("mail.imap.starttls.enable", "false");
   144        // Use SSL
   145        props.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
   146        props.setProperty("mail.imap.socketFactory.fallback", "false");
   147    
   148        props.setProperty("mail.imap.port", Integer.toString(EmailReceiver.incomingPort));
   149        props.setProperty("mail.imap.socketFactory.port", Integer.toString(EmailReceiver.incomingPort));
   150    
   151        props.setProperty("mail.imaps.class", "com.sun.mail.imap.IMAPSSLStore");
   152      }
   153      return props;
   154    }
   155  
   156    public boolean isAuthRequired()
   157    {
   158      return EmailReceiver.smtpauth;
   159    }
   160  
   161    public String getUserName()
   162    {
   163      return EmailReceiver.username;
   164    }
   165  
   166    public String getPassword()
   167    {
   168      return EmailReceiver.password;
   169    }
   170  
   171    public String getReplyTo()
   172    {
   173      return EmailReceiver.replyto;
   174    }
   175  
   176    public String getIncomingServer()
   177    {
   178      return EmailReceiver.incoming;
   179    }
   180  
   181    public String getOutgoingServer()
   182    {
   183      return EmailReceiver.outgoing;
   184    }
   185  
   186    public List<String> receive()
   187      throws Exception
   188    {
   189      return receive(null);
   190    }
   191    
   192    public List<String> receive(String dir)
   193      throws Exception
   194    {
   195      if (verbose) System.out.println("Receiving...");
   196      List<String> messList = new ArrayList<String>();
   197      Store store = null;
   198      Folder folder = null;
   199      try
   200      {
   201    //  Properties props = System.getProperties();
   202        Properties props = setProps();
   203        
   204        if (verbose)
   205        {
   206          Set<Object> keys = props.keySet();
   207          for (Object o : keys)
   208            System.out.println(o.toString() + ":" + props.get(o).toString());
   209        }
   210        if (verbose) System.out.println("Getting session...");
   211  //    Session session = Session.getInstance(props, null);
   212        Session session = Session.getInstance(props,
   213                          new javax.mail.Authenticator() 
   214                          {
   215                            protected PasswordAuthentication getPasswordAuthentication() 
   216                            {
   217                              return new PasswordAuthentication(username, password);
   218                            }
   219                          });    
   220        session.setDebug(verbose);
   221        if (verbose) System.out.println("Session established.");
   222        store = session.getStore(EmailReceiver.protocol);
   223        if (EmailReceiver.incomingPort == 0)
   224          store.connect(EmailReceiver.incoming, EmailReceiver.username, EmailReceiver.password);
   225        else
   226          store.connect(EmailReceiver.incoming, EmailReceiver.incomingPort, EmailReceiver.username, EmailReceiver.password);
   227        if (verbose) System.out.println("Connected to store");        
   228        folder = store.getDefaultFolder();
   229        if (folder == null)
   230          throw new RuntimeException("No default folder");
   231  
   232        folder = store.getFolder("INBOX");
   233        if (folder == null)
   234          throw new RuntimeException("No INBOX");
   235  
   236        folder.open(Folder.READ_WRITE);
   237        if (verbose) System.out.println("Connected... filtering, please wait.");
   238        SearchTerm st = new AndTerm(new SearchTerm[] { new OrTerm(buildSearchTerm(sendEmailsTo)), 
   239                                                       new SubjectTerm(acceptSubject),
   240                                                       new FlagTerm(new Flags(Flags.Flag.SEEN), false) });
   241     // st = new SubjectTerm("PI Request");
   242        Message msgs[] = folder.search(st);
   243  //    Message msgs[] = folder.getMessages();
   244  
   245        if (verbose) System.out.println("Search completed, " + msgs.length + " message(s).");
   246        for (int msgNum=0; msgNum<msgs.length; msgNum++)
   247        {
   248          try
   249          {
   250            Message mess = msgs[msgNum];
   251            Address from[] = mess.getFrom();
   252            String sender = "";
   253            try
   254            {
   255              sender = from[0].toString();
   256            }
   257            catch(Exception exception) 
   258            {
   259              exception.printStackTrace();
   260            }
   261  //        System.out.println("Message from [" + sender + "], subject [" + subject + "], content [" + mess.getContent().toString().trim() + "]");
   262            
   263            if (true)
   264            {
   265              if (!mess.isSet(javax.mail.Flags.Flag.SEEN) && 
   266                  !mess.isSet(javax.mail.Flags.Flag.DELETED))
   267              {
   268                String txtMess = printMessage(mess, dir);
   269                messList.add(txtMess);
   270                mess.setFlag(javax.mail.Flags.Flag.SEEN, true);
   271                mess.setFlag(javax.mail.Flags.Flag.DELETED, true);
   272                // Send an ack - by email.
   273                if (this.emailSender == null)
   274                  this.emailSender = new EmailSender(this.provider);
   275                this.emailSender.send(new String[] { sender }, 
   276                                      ackSubject, 
   277                                      "Your request [" + txtMess.trim() + "] is being taken care of.");
   278                if (verbose) System.out.println("Sent an ack to " + sender);
   279              } 
   280              else
   281              {
   282                if (verbose) System.out.println("Old message in your inbox..., received " + mess.getReceivedDate().toString());
   283              }
   284            }
   285          }
   286          catch(Exception ex)
   287          {
   288  //        System.err.println(ex.getMessage());
   289            ex.printStackTrace();
   290          }
   291        }
   292      }
   293      catch(Exception ex)
   294      {
   295        throw ex;
   296      }
   297      finally
   298      {
   299        try
   300        {
   301          if (folder != null)
   302            folder.close(true);
   303          if (store != null)
   304            store.close();
   305        }
   306        catch(Exception ex2)
   307        {
   308          System.err.println("Finally ...");
   309          ex2.printStackTrace();
   310        }
   311      }
   312      return messList;
   313    }
   314  
   315    public static String printMessage(Message message, String dir)
   316    {
   317      String ret = "";
   318      try
   319      {
   320        String from = ((InternetAddress)message.getFrom()[0]).getPersonal();
   321        if(from == null)
   322          from = ((InternetAddress)message.getFrom()[0]).getAddress();
   323        if (verbose) System.out.println("From: " + from);
   324        String subject = message.getSubject();
   325        if (verbose) System.out.println("Subject: " + subject);
   326        Part messagePart = message;
   327        Object content = messagePart.getContent();
   328        if (content instanceof Multipart)
   329        {
   330  //      messagePart = ((Multipart)content).getBodyPart(0);
   331          int nbParts = ((Multipart)content).getCount();
   332          if (verbose) System.out.println("[ Multipart Message ], " + nbParts + " part(s).");
   333          for (int i=0; i<nbParts; i++)
   334          {
   335            messagePart = ((Multipart)content).getBodyPart(i);
   336            if (messagePart.getContentType().toUpperCase().startsWith("APPLICATION/OCTET-STREAM"))
   337            {
   338              if (verbose) System.out.println(messagePart.getContentType() + ":" + messagePart.getFileName());
   339              InputStream is = messagePart.getInputStream();
   340              String newFileName = "";
   341              if (dir != null)
   342                newFileName = dir + File.separator;
   343              newFileName += messagePart.getFileName();
   344              FileOutputStream fos = new FileOutputStream(newFileName);
   345              ret = messagePart.getFileName();
   346              if (verbose) System.out.println("Downloading " + messagePart.getFileName() + "...");
   347              copy(is, fos);
   348              if (verbose) System.out.println("...done.");
   349            } 
   350            else // text/plain, text/html
   351            {
   352              if (verbose) System.out.println("-- Part #" + i + " --, " + messagePart.getContentType().replace('\n', ' ').replace('\r', ' ').replace("\b", "").trim());
   353              InputStream is = messagePart.getInputStream();
   354              BufferedReader br = new BufferedReader(new InputStreamReader(is));
   355              String line = "";
   356              while (line != null)
   357              {
   358                line = br.readLine();
   359                if (line != null)
   360                {
   361                  if (verbose) System.out.println("[" + line + "]");
   362                  if (messagePart.getContentType().toUpperCase().startsWith("TEXT/PLAIN"))
   363                    ret += line;
   364                }
   365              }
   366              br.close();
   367              if (verbose) System.out.println("-------------------");
   368            }
   369          }
   370        }
   371        else
   372        {
   373  //      System.out.println("  .Message is a " + content.getClass().getName());
   374  //      System.out.println("Content:");
   375  //      System.out.println(content.toString());
   376          ret = content.toString();
   377        }
   378        if (verbose) System.out.println("-----------------------------");
   379      }
   380      catch(Exception ex)
   381      {
   382        ex.printStackTrace();
   383      }
   384      return ret;
   385    }
   386  
   387    private static void copy(InputStream in, OutputStream out)
   388      throws IOException
   389    {
   390      synchronized(in)
   391      {
   392        synchronized(out)
   393        {
   394          byte buffer[] = new byte[256];
   395          while (true)
   396          {
   397            int bytesRead = in.read(buffer);
   398            if(bytesRead == -1)
   399              break;
   400            out.write(buffer, 0, bytesRead);
   401          }
   402        }
   403      }
   404    }
   405  }
      
        
pi4j.email.EmailSender.java
Same as the previous, but for outgoing emails. Notice that the two can be different. You might very well send emails using Yahoo! and receive others using Google.
This way, the Raspberry PI and you would have your own email addresses.
As before, check out the method named setProps() (line 164). It might need to be tweaked to fit your email environment.
      

     1  package pi4j.email;
     2  
     3  import com.sun.mail.smtp.SMTPTransport;
     4  
     5  import java.io.FileInputStream;
     6  
     7  import java.util.Properties;
     8  
     9  import javax.mail.Message;
    10  import javax.mail.MessagingException;
    11  import javax.mail.PasswordAuthentication;
    12  import javax.mail.Session;
    13  import javax.mail.Transport;
    14  import javax.mail.internet.AddressException;
    15  import javax.mail.internet.InternetAddress;
    16  import javax.mail.internet.MimeMessage;
    17  
    18  public class EmailSender
    19  {
    20    private static String protocol;
    21    private static int outgoingPort;
    22    private static int incomingPort;
    23    private static String username;
    24    private static String password;
    25    private static String outgoing;
    26    private static String incoming;
    27    private static String replyto;
    28    private static boolean smtpauth;
    29    
    30    private static String sendEmailsTo;
    31    private static String eventSubject;
    32    
    33    private static boolean verbose = "true".equals(System.getProperty("verbose", "false"));
    34  
    35    public EmailSender(String provider) throws RuntimeException
    36    {
    37      EmailSender.protocol = "";
    38      EmailSender.outgoingPort = 0;
    39      EmailSender.incomingPort = 0;
    40      EmailSender.username = "";
    41      EmailSender.password = "";
    42      EmailSender.outgoing = "";
    43      EmailSender.incoming = "";
    44      EmailSender.replyto = "";
    45      EmailSender.smtpauth = false;
    46      EmailSender.sendEmailsTo = "";
    47      EmailSender.eventSubject = "";
    48  
    49      Properties props = new Properties();
    50      String propFile = "email.properties";
    51      try
    52      {
    53        FileInputStream fis = new FileInputStream(propFile);
    54        props.load(fis);
    55      }
    56      catch (Exception e)
    57      {
    58        System.out.println("email.properies file problem...");
    59        throw new RuntimeException("File not found:email.properies");
    60      }
    61      EmailSender.sendEmailsTo = props.getProperty("pi.send.emails.to");
    62      EmailSender.eventSubject = props.getProperty("pi.event.subject");
    63      
    64      EmailSender.protocol     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.protocol");
    65      EmailSender.outgoingPort = Integer.parseInt(props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "outgoing.server.port", "0"));
    66      EmailSender.incomingPort = Integer.parseInt(props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "incoming.server.port", "0"));
    67      EmailSender.username     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.username",   "");
    68      EmailSender.password     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.password",   "");
    69      EmailSender.outgoing     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "outgoing.server", "");
    70      EmailSender.incoming     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "incoming.server", "");
    71      EmailSender.replyto      = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.replyto",    "");
    72      EmailSender.smtpauth     = "true".equals(props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.smtpauth", "false"));
    73      
    74      if (verbose)
    75      {
    76        System.out.println("-------------------------------------");
    77        System.out.println("Protocol       : " + EmailSender.protocol);
    78        System.out.println("Usr/pswd       : " + EmailSender.username + "/" + EmailSender.password);
    79        System.out.println("Incoming server: " + EmailSender.incoming + ":" + EmailSender.incomingPort);
    80        System.out.println("Outgoing server: " + EmailSender.outgoing + ":" + EmailSender.outgoingPort);      
    81        System.out.println("replyto        : " + EmailSender.replyto);
    82        System.out.println("SMTPAuth       : " + EmailSender.smtpauth);
    83        System.out.println("-------------------------------------");
    84      }
    85    }
    86  
    87    public boolean isAuthRequired()
    88    {
    89      return EmailSender.smtpauth;
    90    }
    91  
    92    public String getUserName()
    93    {
    94      return EmailSender.username;
    95    }
    96  
    97    public String getPassword()
    98    {
    99      return EmailSender.password;
   100    }
   101  
   102    public String getReplyTo()
   103    {
   104      return EmailSender.replyto;
   105    }
   106  
   107    public String getIncomingServer()
   108    {
   109      return EmailSender.incoming;
   110    }
   111  
   112    public String getOutgoingServer()
   113    {
   114      return EmailSender.outgoing;
   115    }
   116    
   117    public String getEmailDest()
   118    {
   119      return EmailSender.sendEmailsTo;
   120    }
   121  
   122    public String getEventSubject()
   123    {
   124      return EmailSender.eventSubject;
   125    }
   126  
   127    public void send(String[] dest, 
   128                     String subject, 
   129                     String content)
   130      throws MessagingException, AddressException
   131    {
   132      Properties props = setProps();
   133      
   134  //  Session session = Session.getDefaultInstance(props, auth);
   135      Session session = Session.getInstance(props,
   136                                            new javax.mail.Authenticator() 
   137                                            {
   138                                              protected PasswordAuthentication getPasswordAuthentication() 
   139                                              {
   140                                                return new PasswordAuthentication(username, password);
   141                                              }
   142                                            });    
   143      session.setDebug(verbose);
   144      Transport tr = session.getTransport("smtp");
   145      if (!(tr instanceof SMTPTransport))
   146        System.out.println("This is NOT an SMTPTransport:[" + tr.getClass().getName() + "]");
   147  
   148      Message msg = new MimeMessage(session);
   149      msg.setFrom(new InternetAddress(EmailSender.replyto));
   150      if (dest == null || dest.length == 0)
   151        throw new RuntimeException("Need at least one recipient.");
   152      msg.setRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(dest[0]));
   153      for (int i=1; i<dest.length; i++)
   154        msg.addRecipient(javax.mail.Message.RecipientType.CC, new InternetAddress(dest[i]));
   155      msg.setSubject(subject);
   156      msg.setText(content != null ? content : "");
   157      msg.setContent(content, "text/plain");
   158      msg.saveChanges();
   159      if (verbose) System.out.println("sending:[" + content + "], " + Integer.toString(content.length()) + " characters");
   160  
   161      Transport.send(msg);
   162    }
   163  
   164    private Properties setProps()
   165    {
   166      Properties props = new Properties();
   167      props.put("mail.debug", verbose?"true":"false");
   168      props.put("mail.smtp.host", EmailSender.outgoing);
   169      props.put("mail.smtp.port", Integer.toString(EmailSender.outgoingPort));
   170  
   171      props.put("mail.smtp.auth", "true");
   172      props.put("mail.smtp.starttls.enable", "true"); //  See http://www.oracle.com/technetwork/java/faq-135477.html#yahoomail
   173  //  props.put("mail.smtp.starttls.required", "true");
   174      props.put("mail.smtp.ssl.enable", "true");
   175      return props;
   176    }
   177  }
      
      
pi4j.gpio.GPIOController.java
Interaction with the GPIO on the Raspberry PI.
Notice the pins we use:
  • Yellow led on pin #01 (line 23)
  • Green led on pin #04 (line 24)
  • Button on pin #02 (line 25)
We will need to make sure the wiring matches this setup.
      

     1  package pi4j.gpio;
     2  
     3  import com.pi4j.io.gpio.GpioController;
     4  import com.pi4j.io.gpio.GpioFactory;
     5  import com.pi4j.io.gpio.GpioPinDigitalInput;
     6  import com.pi4j.io.gpio.PinPullResistance;
     7  import com.pi4j.io.gpio.RaspiPin;
     8  import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent;
     9  import com.pi4j.io.gpio.event.GpioPinListenerDigital;
    10  
    11  public class GPIOController
    12  {
    13    private GpioController gpio = null;
    14    private OneLed yellowLed = null;
    15    private OneLed greenLed  = null;
    16    private GpioPinDigitalInput button = null;
    17    private RaspberryPIEventListener caller = null;
    18    
    19    public GPIOController(RaspberryPIEventListener listener)
    20    {
    21      this.caller = listener;
    22      this.gpio = GpioFactory.getInstance();
    23      this.yellowLed = new OneLed(this.gpio, RaspiPin.GPIO_01, "yellow");
    24      this.greenLed  = new OneLed(this.gpio, RaspiPin.GPIO_04, "green");    
    25      this.button = this.gpio.provisionDigitalInputPin(RaspiPin.GPIO_02, PinPullResistance.PULL_DOWN);
    26      this.button.addListener(new GpioPinListenerDigital() 
    27      {
    28        @Override
    29        public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) 
    30        {
    31          caller.manageEvent(event);
    32        }
    33      });
    34    }
    35      
    36    public void shutdown()
    37    {
    38      this.gpio.shutdown();
    39    }
    40    
    41    public void switchYellow(boolean on)
    42    {
    43      if (on)
    44        yellowLed.on();
    45      else
    46        yellowLed.off();
    47    }
    48    
    49    public void switchGreen(boolean on)
    50    {
    51      if (on)
    52        greenLed.on();
    53      else
    54        greenLed.off();
    55    }
    56  }
      
      
pi4j.gpio.OneLed.java
Utility class for led management.
      

     1  package pi4j.gpio;
     2  
     3  
     4  import com.pi4j.io.gpio.GpioController;
     5  import com.pi4j.io.gpio.GpioPinDigitalOutput;
     6  import com.pi4j.io.gpio.Pin;
     7  import com.pi4j.io.gpio.PinState;
     8  
     9  public class OneLed
    10  {
    11    private GpioPinDigitalOutput led = null;
    12    private String name;
    13    
    14    public OneLed(GpioController gpio, Pin pin, String name)
    15    {
    16      this.name = name;
    17      led = gpio.provisionDigitalOutputPin(pin, "Led", PinState.LOW);
    18    }
    19    
    20    public void on()
    21    {
    22      if ("true".equals(System.getProperty("verbose", "false")))
    23        System.out.println(this.name + " is on.");
    24      led.high();
    25    }
    26    
    27    public void off()
    28    {
    29      if ("true".equals(System.getProperty("verbose", "false")))
    30        System.out.println(this.name + " is off.");
    31      led.low();
    32    }
    33  }
      
      
pi4j.email.PIControllerMain.java
This is the main, the one to start from the Raspberry PI (see below)
      

     1  package pi4j.email;
     2  
     3  import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent;
     4  
     5  import java.text.SimpleDateFormat;
     6  
     7  import java.util.Date;
     8  import java.util.List;
     9  
    10  import org.json.JSONObject;
    11  
    12  import pi4j.gpio.GPIOController;
    13  import pi4j.gpio.RaspberryPIEventListener;
    14  
    15  public class PIControllerMain implements RaspberryPIEventListener
    16  {
    17    private final static SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
    18    private static boolean verbose = "true".equals(System.getProperty("verbose", "false"));
    19  
    20    private static String providerSend    = "google";
    21    private static String providerReceive = "google";
    22    
    23    EmailSender sender = null;
    24  
    25    /**
    26     * Invoked like:
    27     *   java pi4j.email.PIControllerMain [-verbose] -send:google -receive:yahoo -help
    28     *   
    29     * This will send emails using google, and receive using yahoo.
    30     * Do check the file email.properties for the different values associated with email servers.
    31     * 
    32     * @param args See above
    33     */
    34    public static void main(String[] args)  
    35    {
    36      for (int i=0; i<args.length; i++)
    37      {
    38        if ("-verbose".equals(args[i]))
    39        {
    40          verbose = true;
    41          System.setProperty("verbose", "true");
    42        }
    43        else if (args[i].startsWith("-send:"))
    44          providerSend = args[i].substring("-send:".length());      
    45        else if (args[i].startsWith("-receive:"))
    46          providerReceive =args[i].substring("-receive:".length());  
    47        else if ("-help".equals(args[i]))
    48        {
    49          System.out.println("Usage:");
    50          System.out.println("  java pi4j.email.PIControllerMain -verbose -send:google -receive:yahoo -help");
    51          System.exit(0);
    52        }
    53      }
    54      
    55      PIControllerMain lmc = new PIControllerMain();
    56      GPIOController piController = new GPIOController(lmc);
    57      EmailReceiver receiver = new EmailReceiver(providerReceive); // For Google, pop must be explicitely enabled at the account level
    58      try
    59      {
    60        System.out.println("Waiting for instructions.");
    61        boolean keepLooping = true;
    62        while (keepLooping)
    63        {
    64          List<String> received = receiver.receive();
    65          if (verbose || received.size() > 0)
    66          System.out.println(SDF.format(new Date())  + " - Retrieved " + received.size() + " message(s).");
    67          for (String s : received)
    68          {
    69    //      System.out.println(s);
    70            String operation = "";
    71            try
    72            {
    73              JSONObject json = new JSONObject(s);
    74              operation = json.getString("operation");
    75            }
    76            catch (Exception ex)
    77            {
    78              System.err.println(ex.getMessage());
    79              System.err.println("Message is [" + s + "]");
    80            }
    81            if ("exit".equals(operation))
    82            {
    83              keepLooping = false;
    84              System.out.println("Will exit next batch.");
    85          //  break;
    86            }
    87            else
    88            {
    89              if ("turn-green-on".equals(operation))
    90              {
    91                System.out.println("Turning green on");
    92                piController.switchGreen(true);
    93              }
    94              else if ("turn-green-off".equals(operation))
    95              {
    96                System.out.println("Turning green off");
    97                piController.switchGreen(false);
    98              }
    99              else if ("turn-yellow-on".equals(operation))
   100              {
   101                System.out.println("Turning yellow on");
   102                piController.switchYellow(true);
   103              }
   104              else if ("turn-yellow-off".equals(operation))
   105              {
   106                System.out.println("Turning yellow off");
   107                piController.switchYellow(false);
   108              }
   109              try { Thread.sleep(1000L); } catch (InterruptedException ie) { ie.printStackTrace(); }
   110            }
   111          }
   112        }
   113        piController.shutdown();
   114        System.out.println("Done.");
   115        System.exit(0);
   116      }
   117      catch (Exception ex)
   118      {
   119        ex.printStackTrace();
   120      }
   121    }
   122  
   123    public void manageEvent(GpioPinDigitalStateChangeEvent event)
   124    {
   125      if (sender == null)
   126        sender = new EmailSender(providerSend);
   127      try
   128      {
   129        String mess = "{ pin: '" + event.getPin() + "', state:'" + event.getState() + "' }";
   130        System.out.println("Sending:" + mess);
   131        sender.send(sender.getEmailDest().split(","), 
   132                    sender.getEventSubject(), 
   133                    mess);
   134      }
   135      catch (Exception ex)
   136      {
   137        ex.printStackTrace();
   138      }
   139    }
   140  }
      
      

Wiring

This diagram shows the name of the pins you want to use on the GPIO Connector of the Raspberry PI.

Raspberry PI P1 Connector map

As we said before, we need to use the GPIO pins 1, 4, and 2.
Attention!:
GPIO pin 1 is the pin #12
GPIO pin 4 is the pin #16
GPIO pin 2 is the pin #13
The two leds need a 220 Ohms resistor inline between their anode (+) and the GPIO pin they're connected on.
Their cathode (-) is connected on the Ground (pin #6)

Wiring

Demonstration

The Raspberry PI is connected to the home wireless network with the small wireless dongle you can see on the pictures below.
An ethernet cable would have done the same job.
Also notice that there is no keyboard, mouse, nor screen connected to the device.
We used ssh to connect to a Linux session, from which we started the Java program, in background.

Click to enlarge
The Raspberry PI, connected to a breadboard.

At this point, the Raspberry PI is up and running, the java program has been started:
    
 Prompt> sudo ./run -send:google -receive:google [&] 
    
Hint: there is an help option.

 Prompt> ./run -help
 Usage:
   java pi4j.email.PIControllerMain -verbose -send:google -receive:yahoo -help
 Prompt> 
    
The run script looks like this:

 #!/bin/bash
 CP=./classes
 CP=$CP:/home/pi/pi4j/pi4j-distribution/target/distro-contents/lib/pi4j-core.jar
 CP=$CP:./lib/javax.mail_1.1.0.0_1-4-4.jar
 CP=$CP:./lib/json.jar
 #
 java -classpath $CP pi4j.email.LedControllerMain $*
    
Notice the different components on the breadboard:
  • A green led
  • A yellow led
  • A push button

Click to enlarge
Event on the Raspberry PI side. Button pushed.

Some event happens on the Raspberry PI's side. Someone pushed pushed - and the released - the button.
Two emails have been sent:
    
 from   : olivier.lediouris@gmail.com
 subject: PI Event
 content: { pin: '"GPIO 2" <GPIO 2>', state:'HIGH' }
    
    
 from   : olivier.lediouris@gmail.com
 subject: PI Event
 content: { pin: '"GPIO 2" <GPIO 2>', state:'LOW' }
    
Now, you send the following email (in plain text), from your laptop, desktop, smart phone, tablet, what not:

 to     : olivier.lediouris@gmail.com
 subject: PI Request
 content: { operation: "turn-green-on" }
    

Click to enlarge
Green led is on.

The email is received by the Raspberry PI, the green led is turned on.
And you receive an acknowledgement, by email:

 from   : olivier.lediouris@gmail.com
 subject: PI Robot Ack
 content: Your request [{operation:"turn-green-on"}] is being taken care of.
    

Now you send the following email:

 to     : olivier.lediouris@gmail.com
 subject: PI Request
 content: { operation: "turn-yellow-on" }
    

Click to enlarge
Yellow led is on.

The email is received by the Raspberry PI, the yellow led is turned on.
And you receive an acknowledgement, by email:

 from   : olivier.lediouris@gmail.com
 subject: PI Robot Ack
 content: Your request [{operation:"turn-yellow-on"}] is being taken care of.
    

Now you send the following emails:

 to     : olivier.lediouris@gmail.com
 subject: PI Request
 content: { operation: "turn-yellow-off" }
    

 to     : olivier.lediouris@gmail.com
 subject: PI Request
 content: { operation: "turn-green-off" }
    

 to     : olivier.lediouris@gmail.com
 subject: PI Request
 content: { operation: "exit" }
    

Click to enlarge
Everything off, program terminated.

You receive all the acknowledgements, as usual.
All leds are turned off, and the program exits.
Done! Back to base.

Resources