AccessControl.java Source code

Java tutorial

Introduction

Here is the source code for AccessControl.java

Source

/************************************************************************
 * ORGANIZATION  :  SavageHomeAutomation
 * PROJECT       :  Access Control using Pi4J & Raspberry Pi
 * FILENAME      :  AccessControl.java
 * **********************************************************************
 *
 * Copyright (C) 2013 Robert Savage
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.pi4j.component.light.LED;
import com.pi4j.component.light.impl.GpioLEDComponent;
import com.pi4j.component.relay.Relay;
import com.pi4j.component.relay.RelayListener;
import com.pi4j.component.relay.RelayState;
import com.pi4j.component.relay.RelayStateChangeEvent;
import com.pi4j.component.relay.impl.GpioRelayComponent;
import com.pi4j.component.sensor.Sensor;
import com.pi4j.component.sensor.SensorListener;
import com.pi4j.component.sensor.SensorState;
import com.pi4j.component.sensor.SensorStateChangeEvent;
import com.pi4j.component.sensor.impl.GpioSensorComponent;
import com.pi4j.component.switches.MomentarySwitch;
import com.pi4j.component.switches.SwitchListener;
import com.pi4j.component.switches.SwitchState;
import com.pi4j.component.switches.SwitchStateChangeEvent;
import com.pi4j.component.switches.impl.GpioMomentarySwitchComponent;
import com.pi4j.io.gpio.*;
import com.pi4j.io.serial.Serial;
import com.pi4j.io.serial.SerialDataEvent;
import com.pi4j.io.serial.SerialDataListener;
import com.pi4j.io.serial.SerialFactory;
import org.apache.commons.mail.Email;
import org.apache.commons.mail.SimpleEmail;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.*;
import org.eclipse.jetty.websocket.server.WebSocketHandler;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
import org.fusesource.jansi.Ansi;
import org.fusesource.jansi.AnsiConsole;

/**
 * This example code demonstrates how to create an access control
 * system using a Raspberry Pi and the Pi4J Java library.
 *
 * Please see the complete article for parts list, wiring diagrams, etc. at
 * http://www.savagehomeautomation.com/pi4j-access-control
 *
 * ATTENTION : THIS CODE IS PROVIDED A AN EXAMPLE ONLY.  THIS SAMPLE
 *             DOES NOT COVER ALL POTENTIAL ACCESS CONTROL USE CASES
 *             AND SECURITY RISKS.  THIS CODE MAY BE USED AS A STARTING
 *             POINT TO CREATING YOUR OWN ACCESS CONTROL SYSTEM.
 *             USE AT YOUR OWN RISK.  AUTHORS AND/OR CONTRIBUTORS
 *             ASSUME NO LIABILITY AND ARE NOT LIABLE FOR ANY LOSSES
 *             RELATED TO THE USE OF THIS CODE AND/OR PROJECT.
 * 
 * @author Robert Savage
 */
public class AccessControl {

    public static final boolean EMAIL_ENABLED = false; // TODO : ENABLED EMAIL TO IFTTT HERE
    public static final String EMAIL_FROM = ""; //TODO : YOUR IFTTT EMAIL GOES HERE
    public static final String EMAIL_TO = "trigger@ifttt.com";
    public static final String EMAIL_SERVER = "aspmx.l.google.com";
    public static final int EMAIL_PORT = 25;

    public static final String IFTTT_TAG_SECURITY_ALERT = "#security-alert";
    public static final String IFTTT_TAG_ACCESS_LOG = "#access-log";

    // create GPIO controller
    private static final GpioController gpio = GpioFactory.getInstance();

    // ******************************************************************
    // INITIALIZE SENSORS, SWITCHES, RELAYS, etc.
    // ******************************************************************

    // create doorbell switch
    // (momentary push-button switch; activates when button is pushed)
    private static MomentarySwitch doorbellSwitch = new GpioMomentarySwitchComponent(
            gpio.provisionDigitalInputPin(RaspiPin.GPIO_01, PinPullResistance.PULL_UP), PinState.HIGH,
            PinState.LOW); // this switch is configured to trigger ON with pin LOW

    // create manual override switch
    // (momentary push-button switch; activates when button is pushed)
    private static MomentarySwitch overrideSwitch = new GpioMomentarySwitchComponent(
            gpio.provisionDigitalInputPin(RaspiPin.GPIO_06, PinPullResistance.PULL_UP), PinState.HIGH,
            PinState.LOW); // this switch is configured to trigger ON with pin LOW

    // create keypad tamper switch
    // (momentary push-button switch; activates when button is pushed)
    private static MomentarySwitch tamperSwitch = new GpioMomentarySwitchComponent(
            gpio.provisionDigitalInputPin(RaspiPin.GPIO_03, PinPullResistance.PULL_UP), PinState.LOW,
            PinState.HIGH); // this switch is configured to trigger ON with pin LOW

    // create door sensor
    // (magnetic door sensor; senses door opened/closed events)
    private static Sensor doorSensor = new GpioSensorComponent(
            gpio.provisionDigitalInputPin(RaspiPin.GPIO_00, PinPullResistance.PULL_UP), PinState.HIGH,
            PinState.LOW); // this sensor is configured as "Closed" when the pin goes LOW

    // create unlock sensor
    // (activates when keypad request unlock event)
    private static Sensor keypadUnlockSensor = new GpioSensorComponent(
            gpio.provisionDigitalInputPin(RaspiPin.GPIO_02, PinPullResistance.PULL_UP), PinState.HIGH,
            PinState.LOW); // this sensor is configured as "Open" when the pin goes LOW

    // create door lock relay controller
    // (when relay is latched, the door solenoid is actuated, thus unlocking the door)
    private static Relay lockRelay = new GpioRelayComponent(
            gpio.provisionDigitalOutputPin(RaspiPin.GPIO_04, PinState.LOW));

    // create door opener request relay controller
    // (when relay is latched, a request is sent to the keypad controller to unlock the door)
    private static Relay keypadOpenerRelay = new GpioRelayComponent(
            gpio.provisionDigitalOutputPin(RaspiPin.GPIO_05, PinState.LOW));

    // define components
    private static LED securityLed = new GpioLEDComponent(
            gpio.provisionDigitalOutputPin(RaspiPin.GPIO_07, PinState.LOW));

    // static state members
    private static SecurityViolation securityViolation = SecurityViolation.None;

    // create an instance of the serial communications class
    private static final Serial serial = SerialFactory.createInstance();

    private static final String LOADING = "A";
    private static final String SYSTEM_READY = "B";
    private static final String DOORBELL = "C";
    private static final String DOOR_OPENED = "D";
    private static final String DOOR_CLOSED = "E";
    private static final String APPROVED = "F";
    private static final String DENIED = "G";
    private static final String SECURITY = "H";
    private static final String EMPTY = "Y";

    public static void main(String args[]) throws Exception {

        // init JANSI
        // (using jansi for color console output)
        AnsiConsole.systemInstall();

        // display welcome message
        AnsiConsole.out().println(Ansi.ansi().fg(Ansi.Color.CYAN).a(Ansi.Attribute.INTENSITY_BOLD)
                .a("-----------------------------------------------\n").a("\n")
                .a("Welcome to JavaOne 2013 - [[ CON7968 ]]\n").a("\n")
                .a("  Let's Get Physical: I/O Programming with\n").a("  Java on the Raspberry Pi with Pi4J\n")
                .a("\n").a("-----------------------------------------------\n")
                .a("<--Pi4J--> Security Access Example ... started.\n")
                .a("-----------------------------------------------\n").reset());

        // ******************************************************************
        // INIT & CONFIGURE LED MESSAGE READER
        // ******************************************************************

        // open the default serial port provided on the GPIO header
        // (this is where our LED reader is connected)
        serial.open(Serial.DEFAULT_COM_PORT, 2400); // 2400 BAUD, N, 8, 1

        // configure the LED message display
        // (setup pages of display text)
        configureMessage(LOADING, "<FD><CL>LOADING...");
        configureMessage(SYSTEM_READY, "<FD>SYSTEM READY");
        configureMessage(DOORBELL, "<FD><CQ>DOORBELL<FO>");
        configureMessage(DOOR_OPENED, "<FD><CK>OPENED");
        configureMessage(DOOR_CLOSED, "<FD><CE>CLOSED");
        configureMessage(APPROVED, "<FD><CL>APPROVED");
        configureMessage(DENIED, "<FD><CH>DENIED");
        configureMessage(SECURITY, "<FD><CC><SE>* SECURITY *<FO>");
        configureMessage(EMPTY, "<FD><CC> ");

        // create and register the serial data listener
        serial.addListener(new SerialDataListener() {
            @Override
            public void dataReceived(SerialDataEvent event) {
                // print out the data received to the console
                //System.out.print(event.getData());
            }
        });

        // ******************************************************************
        // INIT & START WEB SERVER  (using Jetty Web Server <embedded>)
        // ******************************************************************

        // create new jetty web server
        Server server = new Server(80);

        // create a resource handler to serve up the static html files
        ResourceHandler resourceHandler = new ResourceHandler();
        resourceHandler.setDirectoriesListed(true);
        resourceHandler.setWelcomeFiles(new String[] { "index.html" });
        resourceHandler.setResourceBase("html");

        // create a websocket handler to allow communication from the web pages
        WebSocketHandler websocketHandler = new WebSocketHandler() {
            @Override
            public void configure(WebSocketServletFactory factory) {
                factory.register(AccessControlWebSocket.class);
            }
        };

        // create a handler collection and include all the handlers
        HandlerCollection handlerCollection = new HandlerCollection();
        handlerCollection.setHandlers(new Handler[] { websocketHandler, resourceHandler, new DefaultHandler() });
        server.setHandler(handlerCollection);

        // start the jetty web server
        server.start();

        // ******************************************************************
        // DEFINE COMPONENT LISTENERS & EVENT LOGIC
        // ******************************************************************

        // create door sensor listener
        doorSensor.addListener(new SensorListener() {
            @Override
            public void onStateChange(SensorStateChangeEvent event) {

                // display console message
                Ansi message = Ansi.ansi().fg(Ansi.Color.WHITE).a("Door sensor event: ");
                if (event.getNewState() == SensorState.OPEN) {
                    message.fg(Ansi.Color.GREEN).a("--DOOR OPENED--");

                    // check for a security violation
                    // (this can occur if the lock solenoid is not currently engaged;
                    //  this may mean that the door was forcefully opened)
                    if (lockRelay.isOpen()) {
                        // set security violation to 'door-breach'
                        setSecurityViolation(SecurityViolation.DoorBreach);
                    } else {
                        displayMessage(DOOR_OPENED);
                    }
                } else {
                    message.fg(Ansi.Color.YELLOW).a("--DOOR CLOSED--");
                    displayMessage(DOOR_CLOSED);
                }
                AnsiConsole.out().println(message.reset());
            }
        });

        // create doorbell switch listener
        doorbellSwitch.addListener(new SwitchListener() {
            public void onStateChange(SwitchStateChangeEvent event) {
                if (event.getNewState() == SwitchState.ON) {

                    // display console message
                    AnsiConsole.out()
                            .println(Ansi.ansi().fgBright(Ansi.Color.CYAN).a("DOORBELL event triggered").reset());

                    displayMessage(DOORBELL);
                }
            }
        });

        // create tamper switch listener
        tamperSwitch.addListener(new SwitchListener() {
            public void onStateChange(SwitchStateChangeEvent event) {
                // set security violation to 'tamper'
                setSecurityViolation(SecurityViolation.Tamper);
            }
        });

        // create a door lock solenoid relay listener to monitor lock/unlock events
        lockRelay.addListener(new RelayListener() {
            @Override
            public void onStateChange(RelayStateChangeEvent event) {
                // display console message
                Ansi message = Ansi.ansi().fg(Ansi.Color.WHITE).a("Door locking solenoid:    ");
                if (event.getNewState() == RelayState.CLOSED) {
                    message.fg(Ansi.Color.GREEN).a("--UNLOCKED--");
                } else {
                    message.fg(Ansi.Color.RED).a("--LOCKED--");
                }
                AnsiConsole.out().println(message.reset());
            }
        });

        // create keypad unlock sensor listener
        keypadUnlockSensor.addListener(new SensorListener() {
            @Override
            public void onStateChange(SensorStateChangeEvent event) {
                if (event.getNewState() == SensorState.CLOSED) {

                    // first check for any security violation;
                    // if a security violation exists, then we will not unlock the
                    // door until the security violation has been reset/restored
                    if (securityViolation != SecurityViolation.None) {

                        // display console message
                        AnsiConsole.out().println(Ansi.ansi().fg(Ansi.Color.WHITE).a("Unlock request received:  ")
                                .fgBright(Ansi.Color.RED).a("--ACCESS DENIED--").reset());

                        // display access denied message
                        displayMessage(DENIED);

                        AnsiConsole.out()
                                .println(Ansi.ansi().fgBright(Ansi.Color.YELLOW)
                                        .a("A security violation has been detected; the door will ")
                                        .a("not be unlocked until the violation is reset.").reset());

                        // send notification email to update access log
                        sendEmail(IFTTT_TAG_ACCESS_LOG, "ACCESS DENIED");
                    }

                    // if no security violation exists, then its safe to unlock the door
                    else {
                        // display console message
                        AnsiConsole.out().println(Ansi.ansi().fg(Ansi.Color.WHITE).a("Unlock request received:  ")
                                .fgBright(Ansi.Color.GREEN).a("--ACCESS GRANTED--").reset());

                        // display access approved message
                        displayMessage(APPROVED);

                        // determine if the door is open or shut
                        // (if the door is already open, then there is
                        //  no need to engage the lock solenoid relay)
                        if (doorSensor.isOpen()) {
                            // display console message
                            AnsiConsole.out().println(Ansi.ansi().fg(Ansi.Color.YELLOW)
                                    .a("Unlock bypassed; the door is already open.").reset());
                        } else {
                            // unlock the physical door by latching the
                            // solenoid relay for a few seconds
                            lockRelay.pulse(3000);
                        }

                        // send notification email to update access log
                        sendEmail(IFTTT_TAG_ACCESS_LOG, "ACCESS GRANTED");
                    }
                }
            }
        });

        // create override switch listener
        overrideSwitch.addListener(new SwitchListener() {
            public void onStateChange(SwitchStateChangeEvent event) {
                if (event.getNewState() == SwitchState.ON) {

                    // check for security violation
                    if (securityViolation != SecurityViolation.None) {

                        // display console message
                        AnsiConsole.out().println(Ansi.ansi().fgBright(Ansi.Color.WHITE)
                                .a("Override switch requesting --RESET--").reset());

                        // check for tamper switch security violations
                        if (tamperSwitch.isOn()) {
                            // display console message
                            AnsiConsole.out()
                                    .println(Ansi.ansi().fgBright(Ansi.Color.YELLOW)
                                            .a("Unable to RESET security violation; ")
                                            .a("TAMPER switch is still reporting trouble.\n")
                                            .a("Tamper trouble must be resolved to reset.").reset());
                            return;
                        }

                        // check for drop open security violations
                        if (doorSensor.isOpen()) {
                            // display console message
                            AnsiConsole.out()
                                    .println(Ansi.ansi().fgBright(Ansi.Color.YELLOW)
                                            .a("Unable to RESET security violation; DOOR is still open.\n")
                                            .a("Door must be closed to reset.").reset());
                            return;
                        }

                        // reset security violation
                        setSecurityViolation(SecurityViolation.Reset);
                    } else {
                        AnsiConsole.out().println(Ansi.ansi().fg(Ansi.Color.WHITE)
                                .a("Override switch requesting --UNLOCK--").reset());
                        unlock();
                    }
                }
            }
        });

        // ******************************************************************
        // PROGRAM INIT LOGIC
        // ******************************************************************

        // display console message
        AnsiConsole.out().println(Ansi.ansi().fg(Ansi.Color.WHITE).a("SYSTEM READY").reset());

        // post read message to LED reader sign
        serial.write("<ID01><RPB>\r\n");

        // check for tamper switch security violations
        if (tamperSwitch.isOn()) {
            // set security violation to 'tamper'
            setSecurityViolation(SecurityViolation.Tamper);
        }

        // ******************************************************************
        // PROGRAM TERMINATION
        // ******************************************************************

        // wait for user input to terminate program
        AnsiConsole.out()
                .println(Ansi.ansi().fg(Ansi.Color.BLACK).bg(Ansi.Color.CYAN).a("PRESS [ENTER] TO EXIT").reset());
        System.console().readLine();

        // make sure the security LED is off
        securityLed.blink(0);
        securityLed.off();

        // shutdown jetty web server
        server.stop();

        // shutdown GPIO controller
        gpio.shutdown();

        // clear the display
        displayMessage(EMPTY);
        Thread.sleep(1000);

        // shutdown the serial controller
        serial.shutdown();
    }

    /**
     * Get the current security violation state
     *
     * @return SecurityViolation
     */
    public static SecurityViolation getSecurityViolation() {
        return securityViolation;
    }

    /**
     * Request to unlock door.
     *
     * @return 'true' if the door was successfully unlocked; otherwise 'false'
     */
    public static boolean unlock() {

        // check for security violation
        if (securityViolation == SecurityViolation.None) {
            AnsiConsole.out().println(Ansi.ansi().fg(Ansi.Color.WHITE).a("Requesting --UNLOCK--").reset());
            keypadOpenerRelay.pulse(500);
            return true;
        }
        return false;
    }

    /**
     * Display a pre-configured message (page) on the LED message reader board.
     *
     * @param messageId  The page identifier where the message is stored.
     */
    private static void displayMessage(String messageId) {
        if (securityViolation == SecurityViolation.None || messageId.equals(SECURITY)) {
            serial.write("<ID01><RP" + messageId + ">\r\n");
        }
    }

    /**
     * Configure a display message for the LED message reader board.
     *
     * @param messageId  The page identifier where the message is stored.
     * @param message  The message text to be stored at the givin page location.
     * @throws InterruptedException
     */
    private static void configureMessage(String messageId, String message) throws InterruptedException {
        serial.write("<ID01><P" + messageId + ">" + message + "\r\n");
        Thread.sleep(250);
    }

    /**
     * Update the current system security violation status
     *
     * @param violation New security violation status
     */
    private static void setSecurityViolation(SecurityViolation violation) {

        // ignore if same violation
        if (violation == securityViolation) {
            return;
        }

        // determine if this is a violation RESET
        if (violation == SecurityViolation.Reset) {

            // reset security violation tracking variable
            securityViolation = SecurityViolation.None;

            // display console message
            AnsiConsole.out()
                    .println(Ansi.ansi().fgBright(Ansi.Color.WHITE).a("The security violation has been ")
                            .fgBright(Ansi.Color.MAGENTA).a("--RESET--\n").reset().fg(Ansi.Color.WHITE)
                            .a("SYSTEM READY").reset());

            // stop security blinking LED
            securityLed.blink(0);
            securityLed.off();

            // send email notification to IFTTT
            sendEmail(IFTTT_TAG_SECURITY_ALERT, "A security violation has been restored: " + violation.getName());

            // remove security message from LED message reader
            displayMessage(SYSTEM_READY);
        } else {
            // set security violation tracking variable
            securityViolation = violation;

            // display console message
            AnsiConsole.out()
                    .println(Ansi.ansi().fgBright(Ansi.Color.YELLOW).a("A security violation has been detected: ")
                            .fgBright(Ansi.Color.RED).a("--" + violation.getName() + "--").reset());

            AnsiConsole.out()
                    .println(Ansi.ansi().fgBright(Ansi.Color.RED)
                            .a("-----------------------------------------------\n")
                            .a("SECURITY VIOLATION :: " + violation.getName() + "\n")
                            .a("-----------------------------------------------").reset());

            // start blinking the security LED indicator light
            securityLed.blink(250);

            // send email notification to IFTTT
            sendEmail(IFTTT_TAG_SECURITY_ALERT, "A security violation has been detected: " + violation.getName());

            // display the security violation on the LED message reader
            displayMessage(SECURITY);
        }
    }

    /**
     * Send email message for external notifications
     *
     * @param subject Email Subject Text
     * @param message Email Message Text
     */
    private static void sendEmail(String subject, String message) {

        try {
            if (EMAIL_ENABLED && EMAIL_FROM != null && !EMAIL_FROM.isEmpty()) {
                Email email = new SimpleEmail();
                email.setHostName(EMAIL_SERVER);
                email.setSmtpPort(EMAIL_PORT);
                email.setFrom(EMAIL_FROM);
                email.addTo(EMAIL_TO);
                email.setSubject(subject);
                email.setMsg(message);
                email.send();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}