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 * @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 private static final int SECOND_TO_TURN_OFF = 1; // in seconds /** * IoT Sets and Relations *

* 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 smartSensorsSet; @config private IoTSet camSet; @config private IoTSet alarmSet; @config private IoTSet roomSet; //@config private IoTSet doorlockSet; //@config private IoTSet outletSet; @config private IoTRelation roomSensorRelation; @config private IoTRelation roomCameraRelation; //@config private IoTRelation roomDoorLockRelation; //@config private IoTRelation roomOutletRelation; /******************************************************************************************************************************************* ** ** Variables ** *******************************************************************************************************************************************/ long lastTimeChecked = 0; private static int sensorId = 0; /******************************************************************************************************************************************* ** ** States data structures ** *******************************************************************************************************************************************/ // Camera and motion detection private Map camMotionDetect = new HashMap(); // Smartthings sensors (true = motion, leakage, etc.) private Map senDetectStatus = new ConcurrentHashMap(); // Camera (true = motion) private Map camDetectStatus = new HashMap(); // Doorlock (true = open - not locked) //private Map doorlockStatus = // new HashMap(); // Outlet (true = on - outlet is used) //private Map outletStatus = // new HashMap(); // Alarm status private Map alarmStatus = new HashMap(); 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 sensors = roomSensorRelation.get(rm); System.out.println("DEBUG: We have " + sensors.size() + " sensors!"); for (SmartthingsSensorSmart sen : sensors) { try { // Initialize sensors sen.init(); System.out.println("DEBUG: Initialized smartthings sensor! ID: " + sensorId + " Room ID: " + rm.getRoomID()); senDetectStatus.put(sensorId, false); System.out.println("DEBUG: Initialized sensor detection to false!"); sen.setId(sensorId++); sen.registerCallback(this); System.out.println("DEBUG: Registered sensor callback!"); } catch (Exception e) { e.printStackTrace(); } } } /** Method to initialize cameras * * @return [void] None. */ private void initCameras(RoomSmart rm) { // Get and init the IAS sensors for this specific room HashSet cameras = roomCameraRelation.get(rm); // Setup the cameras, start them all and assign each one a motion detector for (CameraSmart cam : cameras) { // 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); // Initialize detection to false camDetectStatus.put(cam, false); } } /** 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!"); // TODO: Check that this initialization works for multiple times - might be that setZone() only works once! //for (RoomSmart room : roomSet.values()) { // turnOffAlarms(room.getRoomID()); // System.out.println("DEBUG: Initialized alarm for room (turn off): " + room.getRoomID()); //} } 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 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 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 [boolean] None. */ private boolean roomDidHaveMotionRecently(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 return false; } // Did detect motion recently if (Math.abs(currentTimeSeconds - lastDetectedMotionSeconds) < _numberOfSeconds) { return true; } } return false; } /** 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) { System.out.println("DEBUG: Sensor is detecting something: " + _activeValue); senDetectStatus.put(_sensorId, true); } else { //System.out.println("DEBUG: Sensor is not detecting something: " + _activeValue); 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 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 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); // Update status of doorlocks //updateDoorLockStatus(room); // Update status of outlets //updateOutletStatus(room); } } /** Update the status of all devices * * @return [void] None. */ private void updateCameraStatus(RoomSmart room) { HashSet cameras = roomCameraRelation.get(room); if (roomDidHaveMotionRecently(room, MOTION_TIME_THRESHOLD)) { // Motion was detected System.out.println("DEBUG: Camera detected something!"); for(CameraSmart cam : cameras) camDetectStatus.put(cam, true); } else { // No motion was detected //System.out.println("DEBUG: Camera didn't detect anything!"); for(CameraSmart cam : cameras) camDetectStatus.put(cam, false); } } /** 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_OFF); } /** 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, SECOND_TO_TURN_ON); } /** Check status of devices and turn on alarm accordingly *

* 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) //System.out.println("ABOUT TO DETECT: Reading sensor: " + sensor.getId()); if (senDetectStatus.get(sensor.getId())) { zoneId = room.getRoomID(); turnOnAlarms(zoneId); System.out.println("DETECTION: Sensor active in room: " + zoneId); System.out.println("DETECTION: Detection by sensor: " + sensor.getId()); } } } } /** Check status of devices and turn off alarm accordingly *

* 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 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(); // Init all outlets //initOutlets(); } // Init all alarms initAlarms(); System.out.println("DEBUG: Initialized all devices! Now starting detection loop!"); // Run the main loop that will keep checking the sensors and cameras periodically while (true) { // Run this code every 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(); } } } } }