1 package HomeSecurityController;
3 // Standard Java Packages
5 import java.util.Iterator;
6 import java.util.ArrayList;
7 import java.util.HashMap;
11 import java.util.HashSet;
12 import java.util.concurrent.atomic.AtomicBoolean;
13 import java.util.concurrent.ConcurrentHashMap;
16 import java.rmi.RemoteException;
17 import java.rmi.server.UnicastRemoteObject;
19 // IoT Runtime Packages
20 import iotruntime.slave.IoTSet;
21 import iotruntime.slave.IoTRelation;
22 import iotcode.annotation.*;
24 // IoT Driver Packages
25 import iotcode.interfaces.*;
27 // Checker annotations
28 //import iotchecker.qual.*;
30 /** Class Home Security Controller for the home security application benchmark
32 * @author Rahmadi Trimananda <rtrimana @ uci.edu>
36 public class HomeSecurityController implements SmartthingsSensorCallback {
41 private static final int MOTION_TIME_THRESHOLD = 60; // in seconds
42 private static final int CAMERA_FPS = 15;
43 private static final int CHECK_TIME_WAIT = 1; // in seconds
44 private static final int SECOND_TO_TURN_ON = 60; // in seconds
47 * IoT Sets and Relations
50 * 1) Multipurpose sensor (detect windows open/close) - Smartthings sensor
51 * 2) Motion sensor (detect motion in certain radius) - Smartthings sensor
52 * 3) Water leak sensor (detect leakage) - Smartthings sensor
53 * 4) Camera (detect motion)
54 * 5) Alarm (using ESP board) - assuming 1 house alarm
55 * 6) Room (object as place of device)
57 * Additionals (for more extensive home management)
58 * 7) Doorlock (detect open/locked)
59 * 8) Power outlet (detect on/off, monitor watt)
61 // This comprises multipurpose, motion, and water leak sensors
62 // TODO: Per 01/2017, doorlock and outlet are not ready, ESP board will be used for alarm
63 @config private IoTSet<SmartthingsSensorSmart> smartSensorsSet;
64 @config private IoTSet<CameraSmart> camSet;
65 @config private IoTSet<AlarmSmart> alarmSet;
66 @config private IoTSet<RoomSmart> roomSet;
67 //@config private IoTSet<DoorLock> doorlockSet;
68 //@config private IoTSet<Outlet> outletSet;
70 @config private IoTRelation<RoomSmart, SmartthingsSensorSmart> roomSensorRelation;
71 @config private IoTRelation<RoomSmart, CameraSmart> roomCameraRelation;
72 //@config private IoTRelation<RoomSmart, DoorLock> roomDoorLockRelation;
73 //@config private IoTRelation<RoomSmart, Outlet> roomOutletRelation;
75 /*******************************************************************************************************************************************
79 *******************************************************************************************************************************************/
80 long lastTimeChecked = 0;
82 private static int sensorId = 0;
84 /*******************************************************************************************************************************************
86 ** States data structures
88 *******************************************************************************************************************************************/
89 // Camera and motion detection
90 private Map<CameraSmart, MotionDetection> camMotionDetect =
91 new HashMap<CameraSmart, MotionDetection>();
92 // Smartthings sensors (true = motion, leakage, etc.)
93 private Map<Integer, Boolean> senDetectStatus =
94 new ConcurrentHashMap<Integer, Boolean>();
95 // Camera (true = motion)
96 private Map<CameraSmart, Boolean> camDetectStatus =
97 new HashMap<CameraSmart, Boolean>();
98 // Doorlock (true = open - not locked)
99 //private Map<DoorLock, Boolean> doorlockStatus =
100 // new HashMap<DoorLock, Boolean>();
101 // Outlet (true = on - outlet is used)
102 //private Map<Outlet, Boolean> outletStatus =
103 // new HashMap<Outlet, Boolean>();
106 private Map<Integer, Boolean> alarmStatus =
107 new HashMap<Integer, Boolean>();
109 public HomeSecurityController() {
114 /*******************************************************************************************************************************************
118 *******************************************************************************************************************************************/
121 /** Method to initialize Smartthings sensors
123 * @return [void] None.
125 private void initSmartthingsSensors(RoomSmart rm) {
127 // Get and init the IAS sensors for this specific room
128 HashSet<SmartthingsSensorSmart> sensors = roomSensorRelation.get(rm);
129 for (SmartthingsSensorSmart sen : sensors) {
132 // Initialize sensors
134 System.out.println("DEBUG: Initialized smartthings sensor! ID: " + sensorId + " Room ID: " + rm.getRoomID());
135 senDetectStatus.put(sensorId, false);
136 System.out.println("DEBUG: Initialized sensor detection to false!");
137 sen.setId(sensorId++);
138 sen.registerCallback(this);
139 System.out.println("DEBUG: Registered sensor callback!");
140 } catch (Exception e) {
147 /** Method to initialize cameras
149 * @return [void] None.
151 private void initCameras(RoomSmart rm) {
153 // Get and init the IAS sensors for this specific room
154 HashSet<CameraSmart> cameras = roomCameraRelation.get(rm);
155 // Setup the cameras, start them all and assign each one a motion detector
156 for (CameraSmart cam : cameras) {
158 // Each camera will have a motion detector unique to it since the motion detection has state
159 MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
161 // initialize the camera, might need to setup some stuff internally
164 // set the camera parameters.
165 cam.setFPS(CAMERA_FPS);
166 cam.setResolution(Resolution.RES_VGA);
168 // camera will call the motion detector directly with data not this controller
169 cam.registerCallback(mo);
171 // Start the camera (example is start the HTTP stream if it is a network camera)
173 System.out.println("DEBUG: Initialized camera!");
175 // Remember which motion detector is for what camera
176 camMotionDetect.put(cam, mo);
178 // Initialize detection to false
179 camDetectStatus.put(cam, false);
184 /** Method to initialize alarms
186 * @return [void] None.
188 private void initAlarms() {
190 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
191 Iterator alarmIt = alarmSet.iterator();
192 AlarmSmart alm = (AlarmSmart) alarmIt.next();
193 // init the alarm controller, do it here since it only needs to be done once per controller
196 System.out.println("DEBUG: Initialized alarm!");
197 } catch (Exception e) {
203 /** Method to initialize doorlocks
205 * @return [void] None.
207 private void initDoorLocks(RoomSmart rm) {
209 // Get and init the doorlocks for this specific room
210 /*HashSet<DoorLock> doorlocks = roomDoorLockRelation.get(rm);
211 for (DoorLock doorlock : doorlocks) {
214 // Initialize doorlocks
216 System.out.println("DEBUG: Initialized doorlock!");
217 } catch (Exception e) {
224 /** Method to initialize power outlets
226 * @return [void] None.
228 private void initOutlets(RoomSmart rm) {
230 // Get and init the outlets for this specific room
231 /*HashSet<Outlet> outlets = roomOutletRelation.get(rm);
232 for (Outlet outlet : outlets) {
235 // Initialize outlets
237 System.out.println("DEBUG: Initialized outlet!");
238 } catch (Exception e) {
245 /** Method to detect if a room has seen motion within the last few seconds (time specified as parameter).
246 * Checks all the motion detectors for the given room
248 * @param _room [RoomSmart] , Room of interest.
249 * @param _numberOfSeconds [int] , Number of seconds in the past that we consider recent.
250 * @param _upperThreshold [int] , Number of seconds as an upper bound before we turn off.
252 * @return [boolean] None.
254 private boolean roomDidHaveMotionRecently(RoomSmart _room, int _numberOfSeconds) {
255 long currentTimeSeconds = (new Date()).getTime() / 1000;
257 // Loop through all the cameras in the room
258 for (CameraSmart cam : roomCameraRelation.get(_room)) {
259 long lastDetectedMotionSeconds = currentTimeSeconds;
261 Date motionTime = ((MotionDetection)camMotionDetect.get(cam)).getTimestampOfLastMotion();
263 // Motion was detected at least once
264 if (motionTime != null) {
265 lastDetectedMotionSeconds = motionTime.getTime() / 1000;
267 // motionTime == null means this is the initialization phase
272 // Did detect motion recently
273 if (Math.abs(currentTimeSeconds - lastDetectedMotionSeconds) < _numberOfSeconds) {
282 /** Method to update state data structures for Smartthings sensors
284 * @return [void] None.
286 public void newReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
288 System.out.println("DEBUG: Sensor reading value: " + _value);
290 System.out.println("DEBUG: Sensor is detecting something: " + _activeValue);
291 senDetectStatus.put(_sensorId, true);
294 //System.out.println("DEBUG: Sensor is not detecting something: " + _activeValue);
295 senDetectStatus.put(_sensorId, false);
300 /** Method to update state data structures for doorlocks
302 * @return [void] None.
304 private void updateDoorLockStatus(RoomSmart rm) {
306 // Get and init the outlets for this specific room
307 /*HashSet<DoorLock> doorlocks = roomDoorLockRelation.get(rm);
308 for (DoorLock doorlock : doorlocks) {
310 // Change is detected! Set to true for report...
311 if(isChangeDetected()) {
313 doorlockStatus.put(doorlock, true);
316 doorlockStatus.put(doorlock, false);
322 /** Method to update state data structures for outlets
324 * @return [void] None.
326 private void updateOutletStatus(RoomSmart rm) {
328 // Get and init the outlets for this specific room
329 /*HashSet<Outlet> outlets = roomOutletRelation.get(rm);
330 for (Outlet outlet : outlets) {
332 // Change is detected! Set to true for report...
333 if(isChangeDetected()) {
335 outletStatus.put(outlet, true);
338 outletStatus.put(outlet, false);
344 /** Update the status of all devices
346 * @return [void] None.
348 private void updateUniversalStatus() {
350 // Check for motion in rooms and if there is motion then report
351 for (RoomSmart room : roomSet.values()) {
353 // Update status of camera
354 updateCameraStatus(room);
356 // Update status of doorlocks
357 //updateDoorLockStatus(room);
359 // Update status of outlets
360 //updateOutletStatus(room);
365 /** Update the status of all devices
367 * @return [void] None.
369 private void updateCameraStatus(RoomSmart room) {
371 HashSet<CameraSmart> cameras = roomCameraRelation.get(room);
372 if (roomDidHaveMotionRecently(room, MOTION_TIME_THRESHOLD)) {
374 // Motion was detected
375 System.out.println("DEBUG: Camera detected something!");
376 for(CameraSmart cam : cameras)
377 camDetectStatus.put(cam, true);
380 // No motion was detected
381 //System.out.println("DEBUG: Camera didn't detect anything!");
382 for(CameraSmart cam : cameras)
383 camDetectStatus.put(cam, false);
387 /** Method to turn on alarms
389 * @return [void] None.
391 private void turnOnAlarms(int zoneId) {
393 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
394 Iterator alarmIt = alarmSet.iterator();
395 AlarmSmart alm = (AlarmSmart) alarmIt.next();
396 alm.setZone(zoneId, true, SECOND_TO_TURN_ON);
400 /** Method to turn off alarms
402 * @return [void] None.
404 private void turnOffAlarms(int zoneId) {
406 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
407 Iterator alarmIt = alarmSet.iterator();
408 AlarmSmart alm = (AlarmSmart) alarmIt.next();
409 // Turn this alarm off indefinitely
410 alm.setZone(zoneId, false, -1);
414 /** Check status of devices and turn on alarm accordingly
416 * Simple rule is whenever any sensor or camera detect something unusual
417 * (sensor/camera becomes active) then we sound the corresponding alarm.
418 * This means we send the signal to the right zone in the EspAlarm
420 * @return [void] None.
422 private void decideToTurnOnAlarm() {
426 // Check for motion in rooms and if there is motion then report
427 for (RoomSmart room : roomSet.values()) {
429 // Loop through all the cameras in the room
430 for (CameraSmart cam : roomCameraRelation.get(room)) {
432 // Get the right camera and the right detection status (true or false)
433 if (camDetectStatus.get(cam)) {
434 zoneId = room.getRoomID();
435 turnOnAlarms(zoneId);
436 System.out.println("DETECTION: Camera active in room: " + zoneId);
440 // Loop through all the cameras in the room
441 for (SmartthingsSensorSmart sensor : roomSensorRelation.get(room)) {
443 // Get the right sensor and the right detection status (true or false)
444 //System.out.println("ABOUT TO DETECT: Reading sensor: " + sensor.getId());
445 if (senDetectStatus.get(sensor.getId())) {
446 zoneId = room.getRoomID();
447 turnOnAlarms(zoneId);
448 System.out.println("DETECTION: Sensor active in room: " + zoneId);
449 System.out.println("DETECTION: Detection by sensor: " + sensor.getId());
456 /** Check status of devices and turn off alarm accordingly
458 * If everything (camera and sensors) is set back to normal
459 * then the system will turn off the alarm
461 * @return [void] None.
463 // TODO: Need to fix this part later
464 // TODO: Perhaps we should use a phone app to turn off the alarm
465 /*private void decideToTurnOffAlarm() {
467 // Check for motion in rooms and if there is motion then report
468 for (RoomSmart room : roomSet.values()) {
470 int zoneId = room.getRoomID();
471 // Loop through all the cameras in the room
472 for (CameraSmart cam : roomCameraRelation.get(room)) {
474 // Get the right camera and the right detection status (true or false)
475 if (camDetectStatus.get(cam)) // Camera still active so alarm is still on, so false for off-alarm status
477 alarmStatus.put(zoneId, false);
481 alarmStatus.put(zoneId, true);
485 // Loop through all the cameras in the room
486 for (SmartthingsSensor sensor : roomSensorRelation.get(room)) {
488 // Get the right sensor and the right detection status (true or false)
489 if (senDetectStatus.get(sensor.getId())) // Sensor still active so alarm is still on, so false for off-alarm status
491 alarmStatus.put(zoneId, false);
495 alarmStatus.put(zoneId, true);
499 // Turn on alarm in the right zone
500 for (Map.Entry<Integer, Boolean> alEnt : alarmStatus.entrySet()) {
501 if (alEnt.getValue()) // if this zone is true, that means we need to turn off its alarm
502 turnOffAlarms(alEnt.getKey());
507 /*******************************************************************************************************************************************
511 *******************************************************************************************************************************************/
513 /** Initialization method, called by the runtime (effectively the main of the controller)
514 * This method runs a continuous loop and is blocking
516 * @return [void] None;
520 // Iterate over the set of rooms
521 for (RoomSmart rm : roomSet.values()) {
523 // Init all Smartthings sensors
524 initSmartthingsSensors(rm);
529 // Init all doorlocks
539 System.out.println("DEBUG: Initialized all devices! Now starting detection loop!");
541 // Run the main loop that will keep checking the sensors and cameras periodically
544 // Run this code every <specified time>
545 long currentTimeSeconds = (new Date()).getTime() / 1000;
546 if ((currentTimeSeconds - lastTimeChecked) > CHECK_TIME_WAIT) {
547 lastTimeChecked = currentTimeSeconds;
549 // Update the status of all devices
550 updateUniversalStatus();
552 // Decide to turn on alarm if any of the sensor/camera detects something unusual
553 decideToTurnOnAlarm();
555 // Decide to turn off alarm if every sensor/camera goes back to normal
556 //decideToTurnOffAlarm();
562 Thread.sleep(CHECK_TIME_WAIT * 100); // sleep for a tenth of the time
564 } catch (Exception e) {