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
45 private static final int SECOND_TO_TURN_OFF = 1; // in seconds
48 * IoT Sets and Relations
51 * 1) Multipurpose sensor (detect windows open/close) - Smartthings sensor
52 * 2) Motion sensor (detect motion in certain radius) - Smartthings sensor
53 * 3) Water leak sensor (detect leakage) - Smartthings sensor
54 * 4) Camera (detect motion)
55 * 5) Alarm (using ESP board) - assuming 1 house alarm
56 * 6) Room (object as place of device)
58 * Additionals (for more extensive home management)
59 * 7) Doorlock (detect open/locked)
60 * 8) Power outlet (detect on/off, monitor watt)
62 // This comprises multipurpose, motion, and water leak sensors
63 // TODO: Per 01/2017, doorlock and outlet are not ready, ESP board will be used for alarm
64 @config private IoTSet<SmartthingsSensorSmart> smartSensorsSet;
65 @config private IoTSet<CameraSmart> camSet;
66 @config private IoTSet<AlarmSmart> alarmSet;
67 @config private IoTSet<RoomSmart> roomSet;
68 //@config private IoTSet<DoorLock> doorlockSet;
69 //@config private IoTSet<Outlet> outletSet;
71 @config private IoTRelation<RoomSmart, SmartthingsSensorSmart> roomSensorRelation;
72 @config private IoTRelation<RoomSmart, CameraSmart> roomCameraRelation;
73 //@config private IoTRelation<RoomSmart, DoorLock> roomDoorLockRelation;
74 //@config private IoTRelation<RoomSmart, Outlet> roomOutletRelation;
76 /*******************************************************************************************************************************************
80 *******************************************************************************************************************************************/
81 long lastTimeChecked = 0;
83 private static int sensorId = 0;
85 /*******************************************************************************************************************************************
87 ** States data structures
89 *******************************************************************************************************************************************/
90 // Camera and motion detection
91 private Map<CameraSmart, MotionDetection> camMotionDetect =
92 new HashMap<CameraSmart, MotionDetection>();
93 // Smartthings sensors (true = motion, leakage, etc.)
94 private Map<Integer, Boolean> senDetectStatus =
95 new ConcurrentHashMap<Integer, Boolean>();
96 // Camera (true = motion)
97 private Map<CameraSmart, Boolean> camDetectStatus =
98 new HashMap<CameraSmart, Boolean>();
99 // Doorlock (true = open - not locked)
100 //private Map<DoorLock, Boolean> doorlockStatus =
101 // new HashMap<DoorLock, Boolean>();
102 // Outlet (true = on - outlet is used)
103 //private Map<Outlet, Boolean> outletStatus =
104 // new HashMap<Outlet, Boolean>();
107 private Map<Integer, Boolean> alarmStatus =
108 new HashMap<Integer, Boolean>();
110 public HomeSecurityController() {
115 /*******************************************************************************************************************************************
119 *******************************************************************************************************************************************/
122 /** Method to initialize Smartthings sensors
124 * @return [void] None.
126 private void initSmartthingsSensors(RoomSmart rm) {
128 // Get and init the IAS sensors for this specific room
129 HashSet<SmartthingsSensorSmart> sensors = roomSensorRelation.get(rm);
130 for (SmartthingsSensorSmart sen : sensors) {
133 // Initialize sensors
135 System.out.println("DEBUG: Initialized smartthings sensor! ID: " + sensorId + " Room ID: " + rm.getRoomID());
136 senDetectStatus.put(sensorId, false);
137 System.out.println("DEBUG: Initialized sensor detection to false!");
138 sen.setId(sensorId++);
139 sen.registerCallback(this);
140 System.out.println("DEBUG: Registered sensor callback!");
141 } catch (Exception e) {
148 /** Method to initialize cameras
150 * @return [void] None.
152 private void initCameras(RoomSmart rm) {
154 // Get and init the IAS sensors for this specific room
155 HashSet<CameraSmart> cameras = roomCameraRelation.get(rm);
156 // Setup the cameras, start them all and assign each one a motion detector
157 for (CameraSmart cam : cameras) {
159 // Each camera will have a motion detector unique to it since the motion detection has state
160 MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
162 // initialize the camera, might need to setup some stuff internally
165 // set the camera parameters.
166 cam.setFPS(CAMERA_FPS);
167 cam.setResolution(Resolution.RES_VGA);
169 // camera will call the motion detector directly with data not this controller
170 cam.registerCallback(mo);
172 // Start the camera (example is start the HTTP stream if it is a network camera)
174 System.out.println("DEBUG: Initialized camera!");
176 // Remember which motion detector is for what camera
177 camMotionDetect.put(cam, mo);
179 // Initialize detection to false
180 camDetectStatus.put(cam, false);
185 /** Method to initialize alarms
187 * @return [void] None.
189 private void initAlarms() {
191 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
192 Iterator alarmIt = alarmSet.iterator();
193 AlarmSmart alm = (AlarmSmart) alarmIt.next();
194 // init the alarm controller, do it here since it only needs to be done once per controller
197 System.out.println("DEBUG: Initialized alarm!");
198 // TODO: Check that this initialization works for multiple times - might be that setZone() only works once!
199 //for (RoomSmart room : roomSet.values()) {
200 // turnOffAlarms(room.getRoomID());
201 // System.out.println("DEBUG: Initialized alarm for room (turn off): " + room.getRoomID());
203 } catch (Exception e) {
209 /** Method to initialize doorlocks
211 * @return [void] None.
213 private void initDoorLocks(RoomSmart rm) {
215 // Get and init the doorlocks for this specific room
216 /*HashSet<DoorLock> doorlocks = roomDoorLockRelation.get(rm);
217 for (DoorLock doorlock : doorlocks) {
220 // Initialize doorlocks
222 System.out.println("DEBUG: Initialized doorlock!");
223 } catch (Exception e) {
230 /** Method to initialize power outlets
232 * @return [void] None.
234 private void initOutlets(RoomSmart rm) {
236 // Get and init the outlets for this specific room
237 /*HashSet<Outlet> outlets = roomOutletRelation.get(rm);
238 for (Outlet outlet : outlets) {
241 // Initialize outlets
243 System.out.println("DEBUG: Initialized outlet!");
244 } catch (Exception e) {
251 /** Method to detect if a room has seen motion within the last few seconds (time specified as parameter).
252 * Checks all the motion detectors for the given room
254 * @param _room [RoomSmart] , Room of interest.
255 * @param _numberOfSeconds [int] , Number of seconds in the past that we consider recent.
256 * @param _upperThreshold [int] , Number of seconds as an upper bound before we turn off.
258 * @return [boolean] None.
260 private boolean roomDidHaveMotionRecently(RoomSmart _room, int _numberOfSeconds) {
261 long currentTimeSeconds = (new Date()).getTime() / 1000;
263 // Loop through all the cameras in the room
264 for (CameraSmart cam : roomCameraRelation.get(_room)) {
265 long lastDetectedMotionSeconds = currentTimeSeconds;
267 Date motionTime = ((MotionDetection)camMotionDetect.get(cam)).getTimestampOfLastMotion();
269 // Motion was detected at least once
270 if (motionTime != null) {
271 lastDetectedMotionSeconds = motionTime.getTime() / 1000;
273 // motionTime == null means this is the initialization phase
278 // Did detect motion recently
279 if (Math.abs(currentTimeSeconds - lastDetectedMotionSeconds) < _numberOfSeconds) {
288 /** Method to update state data structures for Smartthings sensors
290 * @return [void] None.
292 public void newReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
294 System.out.println("DEBUG: Sensor reading value: " + _value);
296 System.out.println("DEBUG: Sensor is detecting something: " + _activeValue);
297 senDetectStatus.put(_sensorId, true);
300 //System.out.println("DEBUG: Sensor is not detecting something: " + _activeValue);
301 senDetectStatus.put(_sensorId, false);
306 /** Method to update state data structures for doorlocks
308 * @return [void] None.
310 private void updateDoorLockStatus(RoomSmart rm) {
312 // Get and init the outlets for this specific room
313 /*HashSet<DoorLock> doorlocks = roomDoorLockRelation.get(rm);
314 for (DoorLock doorlock : doorlocks) {
316 // Change is detected! Set to true for report...
317 if(isChangeDetected()) {
319 doorlockStatus.put(doorlock, true);
322 doorlockStatus.put(doorlock, false);
328 /** Method to update state data structures for outlets
330 * @return [void] None.
332 private void updateOutletStatus(RoomSmart rm) {
334 // Get and init the outlets for this specific room
335 /*HashSet<Outlet> outlets = roomOutletRelation.get(rm);
336 for (Outlet outlet : outlets) {
338 // Change is detected! Set to true for report...
339 if(isChangeDetected()) {
341 outletStatus.put(outlet, true);
344 outletStatus.put(outlet, false);
350 /** Update the status of all devices
352 * @return [void] None.
354 private void updateUniversalStatus() {
356 // Check for motion in rooms and if there is motion then report
357 for (RoomSmart room : roomSet.values()) {
359 // Update status of camera
360 updateCameraStatus(room);
362 // Update status of doorlocks
363 //updateDoorLockStatus(room);
365 // Update status of outlets
366 //updateOutletStatus(room);
371 /** Update the status of all devices
373 * @return [void] None.
375 private void updateCameraStatus(RoomSmart room) {
377 HashSet<CameraSmart> cameras = roomCameraRelation.get(room);
378 if (roomDidHaveMotionRecently(room, MOTION_TIME_THRESHOLD)) {
380 // Motion was detected
381 System.out.println("DEBUG: Camera detected something!");
382 for(CameraSmart cam : cameras)
383 camDetectStatus.put(cam, true);
386 // No motion was detected
387 //System.out.println("DEBUG: Camera didn't detect anything!");
388 for(CameraSmart cam : cameras)
389 camDetectStatus.put(cam, false);
393 /** Method to turn on alarms
395 * @return [void] None.
397 private void turnOnAlarms(int zoneId) {
399 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
400 Iterator alarmIt = alarmSet.iterator();
401 AlarmSmart alm = (AlarmSmart) alarmIt.next();
402 alm.setZone(zoneId, true, SECOND_TO_TURN_OFF);
406 /** Method to turn off alarms
408 * @return [void] None.
410 private void turnOffAlarms(int zoneId) {
412 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
413 Iterator alarmIt = alarmSet.iterator();
414 AlarmSmart alm = (AlarmSmart) alarmIt.next();
415 // Turn this alarm off indefinitely
416 alm.setZone(zoneId, false, SECOND_TO_TURN_ON);
420 /** Check status of devices and turn on alarm accordingly
422 * Simple rule is whenever any sensor or camera detect something unusual
423 * (sensor/camera becomes active) then we sound the corresponding alarm.
424 * This means we send the signal to the right zone in the EspAlarm
426 * @return [void] None.
428 private void decideToTurnOnAlarm() {
432 // Check for motion in rooms and if there is motion then report
433 for (RoomSmart room : roomSet.values()) {
435 // Loop through all the cameras in the room
436 for (CameraSmart cam : roomCameraRelation.get(room)) {
438 // Get the right camera and the right detection status (true or false)
439 if (camDetectStatus.get(cam)) {
440 zoneId = room.getRoomID();
441 turnOnAlarms(zoneId);
442 System.out.println("DETECTION: Camera active in room: " + zoneId);
446 // Loop through all the cameras in the room
447 for (SmartthingsSensorSmart sensor : roomSensorRelation.get(room)) {
449 // Get the right sensor and the right detection status (true or false)
450 //System.out.println("ABOUT TO DETECT: Reading sensor: " + sensor.getId());
451 if (senDetectStatus.get(sensor.getId())) {
452 zoneId = room.getRoomID();
453 turnOnAlarms(zoneId);
454 System.out.println("DETECTION: Sensor active in room: " + zoneId);
455 System.out.println("DETECTION: Detection by sensor: " + sensor.getId());
462 /** Check status of devices and turn off alarm accordingly
464 * If everything (camera and sensors) is set back to normal
465 * then the system will turn off the alarm
467 * @return [void] None.
469 // TODO: Need to fix this part later
470 // TODO: Perhaps we should use a phone app to turn off the alarm
471 /*private void decideToTurnOffAlarm() {
473 // Check for motion in rooms and if there is motion then report
474 for (RoomSmart room : roomSet.values()) {
476 int zoneId = room.getRoomID();
477 // Loop through all the cameras in the room
478 for (CameraSmart cam : roomCameraRelation.get(room)) {
480 // Get the right camera and the right detection status (true or false)
481 if (camDetectStatus.get(cam)) // Camera still active so alarm is still on, so false for off-alarm status
483 alarmStatus.put(zoneId, false);
487 alarmStatus.put(zoneId, true);
491 // Loop through all the cameras in the room
492 for (SmartthingsSensor sensor : roomSensorRelation.get(room)) {
494 // Get the right sensor and the right detection status (true or false)
495 if (senDetectStatus.get(sensor.getId())) // Sensor still active so alarm is still on, so false for off-alarm status
497 alarmStatus.put(zoneId, false);
501 alarmStatus.put(zoneId, true);
505 // Turn on alarm in the right zone
506 for (Map.Entry<Integer, Boolean> alEnt : alarmStatus.entrySet()) {
507 if (alEnt.getValue()) // if this zone is true, that means we need to turn off its alarm
508 turnOffAlarms(alEnt.getKey());
513 /*******************************************************************************************************************************************
517 *******************************************************************************************************************************************/
519 /** Initialization method, called by the runtime (effectively the main of the controller)
520 * This method runs a continuous loop and is blocking
522 * @return [void] None;
526 // Iterate over the set of rooms
527 for (RoomSmart rm : roomSet.values()) {
529 // Init all Smartthings sensors
530 initSmartthingsSensors(rm);
535 // Init all doorlocks
545 System.out.println("DEBUG: Initialized all devices! Now starting detection loop!");
547 // Run the main loop that will keep checking the sensors and cameras periodically
550 // Run this code every <specified time>
551 long currentTimeSeconds = (new Date()).getTime() / 1000;
552 if ((currentTimeSeconds - lastTimeChecked) > CHECK_TIME_WAIT) {
553 lastTimeChecked = currentTimeSeconds;
555 // Update the status of all devices
556 updateUniversalStatus();
558 // Decide to turn on alarm if any of the sensor/camera detects something unusual
559 decideToTurnOnAlarm();
561 // Decide to turn off alarm if every sensor/camera goes back to normal
562 //decideToTurnOffAlarm();
568 Thread.sleep(CHECK_TIME_WAIT * 100); // sleep for a tenth of the time
570 } catch (Exception e) {