+package HomeSecurityController;
+
+// Standard Java Packages
+import java.util.Date;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import java.util.HashSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.ConcurrentHashMap;
+
+// RMI packages
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+// IoT Runtime Packages
+import iotruntime.slave.IoTSet;
+import iotruntime.slave.IoTRelation;
+import iotcode.annotation.*;
+
+// IoT Driver Packages
+import iotcode.interfaces.*;
+
+// Checker annotations
+//import iotchecker.qual.*;
+
+/** Class Home Security Controller for the home security application benchmark
+ *
+ * @author Rahmadi Trimananda <rtrimana @ uci.edu>
+ * @version 1.0
+ * @since 2016-12-14
+ */
+public class HomeSecurityController implements SmartthingsSensorCallback {
+
+ /*
+ * Constants
+ */
+ private static final int MOTION_TIME_THRESHOLD = 60; // in seconds
+ private static final int CAMERA_FPS = 15;
+ private static final int CHECK_TIME_WAIT = 1; // in seconds
+ private static final int SECOND_TO_TURN_ON = 60; // in seconds
+
+ /**
+ * IoT Sets and Relations
+ * <p>
+ * Devices involved:
+ * 1) Multipurpose sensor (detect windows open/close) - Smartthings sensor
+ * 2) Motion sensor (detect motion in certain radius) - Smartthings sensor
+ * 3) Water leak sensor (detect leakage) - Smartthings sensor
+ * 4) Camera (detect motion)
+ * 5) Alarm (using ESP board) - assuming 1 house alarm
+ * 6) Room (object as place of device)
+ *
+ * Additionals (for more extensive home management)
+ * 7) Doorlock (detect open/locked)
+ * 8) Power outlet (detect on/off, monitor watt)
+ */
+ // This comprises multipurpose, motion, and water leak sensors
+ // TODO: Per 01/2017, doorlock and outlet are not ready, ESP board will be used for alarm
+ @config private IoTSet<SmartthingsSensorSmart> smartSensorsSet;
+ @config private IoTSet<CameraSmart> camSet;
+ @config private IoTSet<AlarmSmart> alarmSet;
+ @config private IoTSet<RoomSmart> roomSet;
+ //@config private IoTSet<DoorLock> doorlockSet;
+ //@config private IoTSet<Outlet> outletSet;
+
+ @config private IoTRelation<RoomSmart, SmartthingsSensorSmart> roomSensorRelation;
+ @config private IoTRelation<RoomSmart, CameraSmart> roomCameraRelation;
+ //@config private IoTRelation<RoomSmart, DoorLock> roomDoorLockRelation;
+ //@config private IoTRelation<RoomSmart, Outlet> roomOutletRelation;
+
+ /*******************************************************************************************************************************************
+ **
+ ** Variables
+ **
+ *******************************************************************************************************************************************/
+ long lastTimeChecked = 0;
+
+ private static int sensorId = 0;
+
+ /*******************************************************************************************************************************************
+ **
+ ** States data structures
+ **
+ *******************************************************************************************************************************************/
+ // Camera and motion detection
+ private Map<CameraSmart, MotionDetection> camMotionDetect =
+ new HashMap<CameraSmart, MotionDetection>();
+ // Smartthings sensors (true = motion, leakage, etc.)
+ private Map<Integer, Boolean> senDetectStatus =
+ new ConcurrentHashMap<Integer, Boolean>();
+ // Camera (true = motion)
+ private Map<CameraSmart, Boolean> camDetectStatus =
+ new HashMap<CameraSmart, Boolean>();
+ // Doorlock (true = open - not locked)
+ //private Map<DoorLock, Boolean> doorlockStatus =
+ // new HashMap<DoorLock, Boolean>();
+ // Outlet (true = on - outlet is used)
+ //private Map<Outlet, Boolean> outletStatus =
+ // new HashMap<Outlet, Boolean>();
+
+ // Alarm status
+ private Map<Integer, Boolean> alarmStatus =
+ new HashMap<Integer, Boolean>();
+
+ public HomeSecurityController() {
+
+ }
+
+
+ /*******************************************************************************************************************************************
+ **
+ ** Helper Methods
+ **
+ *******************************************************************************************************************************************/
+
+
+ /** Method to initialize Smartthings sensors
+ *
+ * @return [void] None.
+ */
+ private void initSmartthingsSensors(RoomSmart rm) {
+
+ // Get and init the IAS sensors for this specific room
+ HashSet<SmartthingsSensorSmart> sensors = roomSensorRelation.get(rm);
+ for (SmartthingsSensorSmart sen : sensors) {
+
+ try {
+ // Initialize sensors
+ sen.init();
+ sen.setId(sensorId++);
+ System.out.println("DEBUG: Initialized smartthings sensor!");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+
+ /** Method to initialize cameras
+ *
+ * @return [void] None.
+ */
+ private void initCameras(RoomSmart rm) {
+
+ // Get and init the cameras for this specific room
+ HashSet<CameraSmart> cameras = roomCameraRelation.get(rm);
+ // Setup the cameras, start them all and assign each one a motion detector
+ for (CameraSmart cam : camSet.values()) {
+
+ // Each camera will have a motion detector unique to it since the motion detection has state
+ MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
+
+ // initialize the camera, might need to setup some stuff internally
+ cam.init();
+
+ // set the camera parameters.
+ cam.setFPS(CAMERA_FPS);
+ cam.setResolution(Resolution.RES_VGA);
+
+ // camera will call the motion detector directly with data not this controller
+ cam.registerCallback(mo);
+
+ // Start the camera (example is start the HTTP stream if it is a network camera)
+ cam.start();
+ System.out.println("DEBUG: Initialized camera!");
+
+ // Remember which motion detector is for what camera
+ camMotionDetect.put(cam, mo);
+ }
+ }
+
+
+ /** Method to initialize alarms
+ *
+ * @return [void] None.
+ */
+ private void initAlarms() {
+
+ // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
+ Iterator alarmIt = alarmSet.iterator();
+ AlarmSmart alm = (AlarmSmart) alarmIt.next();
+ // init the alarm controller, do it here since it only needs to be done once per controller
+ try {
+ alm.init();
+ System.out.println("DEBUG: Initialized alarm!");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ /** Method to initialize doorlocks
+ *
+ * @return [void] None.
+ */
+ private void initDoorLocks(RoomSmart rm) {
+
+ // Get and init the doorlocks for this specific room
+ /*HashSet<DoorLock> doorlocks = roomDoorLockRelation.get(rm);
+ for (DoorLock doorlock : doorlocks) {
+
+ try {
+ // Initialize doorlocks
+ doorlock.init();
+ System.out.println("DEBUG: Initialized doorlock!");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }*/
+ }
+
+
+ /** Method to initialize power outlets
+ *
+ * @return [void] None.
+ */
+ private void initOutlets(RoomSmart rm) {
+
+ // Get and init the outlets for this specific room
+ /*HashSet<Outlet> outlets = roomOutletRelation.get(rm);
+ for (Outlet outlet : outlets) {
+
+ try {
+ // Initialize outlets
+ outlet.init();
+ System.out.println("DEBUG: Initialized outlet!");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }*/
+ }
+
+
+ /** Method to detect if a room has seen motion within the last few seconds (time specified as parameter).
+ * Checks all the motion detectors for the given room
+ *
+ * @param _room [RoomSmart] , Room of interest.
+ * @param _numberOfSeconds [int] , Number of seconds in the past that we consider recent.
+ * @param _upperThreshold [int] , Number of seconds as an upper bound before we turn off.
+ *
+ * @return [void] None.
+ */
+ private void updateCameraStatus(RoomSmart _room, int _numberOfSeconds) {
+ long currentTimeSeconds = (new Date()).getTime() / 1000;
+
+ // Loop through all the cameras in the room
+ for (CameraSmart cam : roomCameraRelation.get(_room)) {
+ long lastDetectedMotionSeconds = currentTimeSeconds;
+
+ Date motionTime = ((MotionDetection)camMotionDetect.get(cam)).getTimestampOfLastMotion();
+
+ // Motion was detected at least once
+ if (motionTime != null) {
+ lastDetectedMotionSeconds = motionTime.getTime() / 1000;
+ } else {
+ // motionTime == null means this is the initialization phase
+ // so we put false
+ camDetectStatus.put(cam, false);
+ }
+
+ // Did detect motion recently
+ if (Math.abs(currentTimeSeconds - lastDetectedMotionSeconds) < _numberOfSeconds) {
+ camDetectStatus.put(cam, true);
+ }
+ }
+ }
+
+
+ /** Method to update state data structures for Smartthings sensors
+ *
+ * @return [void] None.
+ */
+ public void newReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
+
+ System.out.println("DEBUG: Sensor reading value: " + _value);
+ if(_activeValue) {
+
+ senDetectStatus.put(_sensorId, true);
+
+ } else {
+
+ senDetectStatus.put(_sensorId, false);
+ }
+ }
+
+
+ /** Method to update state data structures for doorlocks
+ *
+ * @return [void] None.
+ */
+ private void updateDoorLockStatus(RoomSmart rm) {
+
+ // Get and init the outlets for this specific room
+ /*HashSet<DoorLock> doorlocks = roomDoorLockRelation.get(rm);
+ for (DoorLock doorlock : doorlocks) {
+
+ // Change is detected! Set to true for report...
+ if(isChangeDetected()) {
+
+ doorlockStatus.put(doorlock, true);
+ } else {
+
+ doorlockStatus.put(doorlock, false);
+ }
+ }*/
+ }
+
+
+ /** Method to update state data structures for outlets
+ *
+ * @return [void] None.
+ */
+ private void updateOutletStatus(RoomSmart rm) {
+
+ // Get and init the outlets for this specific room
+ /*HashSet<Outlet> outlets = roomOutletRelation.get(rm);
+ for (Outlet outlet : outlets) {
+
+ // Change is detected! Set to true for report...
+ if(isChangeDetected()) {
+
+ outletStatus.put(outlet, true);
+ } else {
+
+ outletStatus.put(outlet, false);
+ }
+ }*/
+ }
+
+
+ /** Update the status of all devices
+ *
+ * @return [void] None.
+ */
+ private void updateUniversalStatus() {
+
+ // Check for motion in rooms and if there is motion then report
+ for (RoomSmart room : roomSet.values()) {
+
+ // Update status of camera
+ updateCameraStatus(room, MOTION_TIME_THRESHOLD);
+
+ // Update status of doorlocks
+ //updateDoorLockStatus(room);
+
+ // Update status of outlets
+ //updateOutletStatus(room);
+ }
+ }
+
+
+ /** Method to turn on alarms
+ *
+ * @return [void] None.
+ */
+ private void turnOnAlarms(int zoneId) {
+
+ // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
+ Iterator alarmIt = alarmSet.iterator();
+ AlarmSmart alm = (AlarmSmart) alarmIt.next();
+ alm.setZone(zoneId, true, SECOND_TO_TURN_ON);
+ }
+
+
+ /** Method to turn off alarms
+ *
+ * @return [void] None.
+ */
+ private void turnOffAlarms(int zoneId) {
+
+ // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
+ Iterator alarmIt = alarmSet.iterator();
+ AlarmSmart alm = (AlarmSmart) alarmIt.next();
+ // Turn this alarm off indefinitely
+ alm.setZone(zoneId, false, -1);
+ }
+
+
+ /** Check status of devices and turn on alarm accordingly
+ * <p>
+ * Simple rule is whenever any sensor or camera detect something unusual
+ * (sensor/camera becomes active) then we sound the corresponding alarm.
+ * This means we send the signal to the right zone in the EspAlarm
+ *
+ * @return [void] None.
+ */
+ private void decideToTurnOnAlarm() {
+
+ int zoneId = 0;
+
+ // Check for motion in rooms and if there is motion then report
+ for (RoomSmart room : roomSet.values()) {
+
+ // Loop through all the cameras in the room
+ for (CameraSmart cam : roomCameraRelation.get(room)) {
+
+ // Get the right camera and the right detection status (true or false)
+ if (camDetectStatus.get(cam)) {
+ zoneId = room.getRoomID();
+ turnOnAlarms(zoneId);
+ System.out.println("DETECTION: Camera active in room: " + zoneId);
+ }
+ }
+
+ // Loop through all the cameras in the room
+ for (SmartthingsSensorSmart sensor : roomSensorRelation.get(room)) {
+
+ // Get the right sensor and the right detection status (true or false)
+ if (senDetectStatus.get(sensor.getId())) {
+ zoneId = room.getRoomID();
+ turnOnAlarms(zoneId);
+ System.out.println("DETECTION: Sensor active in room: " + zoneId);
+ }
+ }
+ }
+ }
+
+
+ /** Check status of devices and turn off alarm accordingly
+ * <p>
+ * If everything (camera and sensors) is set back to normal
+ * then the system will turn off the alarm
+ *
+ * @return [void] None.
+ */
+ // TODO: Need to fix this part later
+ // TODO: Perhaps we should use a phone app to turn off the alarm
+ /*private void decideToTurnOffAlarm() {
+
+ // Check for motion in rooms and if there is motion then report
+ for (RoomSmart room : roomSet.values()) {
+
+ int zoneId = room.getRoomID();
+ // Loop through all the cameras in the room
+ for (CameraSmart cam : roomCameraRelation.get(room)) {
+
+ // Get the right camera and the right detection status (true or false)
+ if (camDetectStatus.get(cam)) // Camera still active so alarm is still on, so false for off-alarm status
+
+ alarmStatus.put(zoneId, false);
+
+ else
+
+ alarmStatus.put(zoneId, true);
+
+ }
+
+ // Loop through all the cameras in the room
+ for (SmartthingsSensor sensor : roomSensorRelation.get(room)) {
+
+ // Get the right sensor and the right detection status (true or false)
+ if (senDetectStatus.get(sensor.getId())) // Sensor still active so alarm is still on, so false for off-alarm status
+
+ alarmStatus.put(zoneId, false);
+
+ else
+
+ alarmStatus.put(zoneId, true);
+ }
+ }
+
+ // Turn on alarm in the right zone
+ for (Map.Entry<Integer, Boolean> alEnt : alarmStatus.entrySet()) {
+ if (alEnt.getValue()) // if this zone is true, that means we need to turn off its alarm
+ turnOffAlarms(alEnt.getKey());
+ }
+ }*/
+
+
+ /*******************************************************************************************************************************************
+ **
+ ** Public Methods
+ **
+ *******************************************************************************************************************************************/
+
+ /** Initialization method, called by the runtime (effectively the main of the controller)
+ * This method runs a continuous loop and is blocking
+ *
+ * @return [void] None;
+ */
+ public void init() {
+
+ // Iterate over the set of rooms
+ for (RoomSmart rm : roomSet.values()) {
+
+ // Init all Smartthings sensors
+ initSmartthingsSensors(rm);
+
+ // Init all cameras
+ initCameras(rm);
+
+ // Init all doorlocks
+ //initDoorLocks(rm);
+
+ // Init all outlets
+ //initOutlets(rm);
+ }
+
+ // Init all alarms
+ initAlarms();
+
+ // Run the main loop that will keep checking the sensors and cameras periodically
+ while (true) {
+
+ // Run this code every <specified time>
+ long currentTimeSeconds = (new Date()).getTime() / 1000;
+ if ((currentTimeSeconds - lastTimeChecked) > CHECK_TIME_WAIT) {
+ lastTimeChecked = currentTimeSeconds;
+
+ // Update the status of all devices
+ updateUniversalStatus();
+
+ // Decide to turn on alarm if any of the sensor/camera detects something unusual
+ decideToTurnOnAlarm();
+
+ // Decide to turn off alarm if every sensor/camera goes back to normal
+ //decideToTurnOffAlarm();
+
+ } else {
+
+ try {
+
+ Thread.sleep(CHECK_TIME_WAIT * 100); // sleep for a tenth of the time
+
+ } catch (Exception e) {
+
+ e.printStackTrace();
+ }
+ }
+
+ }
+ }
+}
+
+