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 System.out.println("DEBUG: We have " + sensors.size() + " sensors!");
131 for (SmartthingsSensorSmart sen : sensors) {
134 // Initialize sensors
136 System.out.println("DEBUG: Initialized smartthings sensor! ID: " + sensorId + " Room ID: " + rm.getRoomID());
137 senDetectStatus.put(sensorId, false);
138 System.out.println("DEBUG: Initialized sensor detection to false!");
139 sen.setId(sensorId++);
140 sen.registerCallback(this);
141 System.out.println("DEBUG: Registered sensor callback!");
142 } catch (Exception e) {
149 /** Method to initialize cameras
151 * @return [void] None.
153 private void initCameras(RoomSmart rm) {
155 // Get and init the IAS sensors for this specific room
156 HashSet<CameraSmart> cameras = roomCameraRelation.get(rm);
157 // Setup the cameras, start them all and assign each one a motion detector
158 for (CameraSmart cam : cameras) {
160 // Each camera will have a motion detector unique to it since the motion detection has state
161 MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
163 // initialize the camera, might need to setup some stuff internally
166 // set the camera parameters.
167 cam.setFPS(CAMERA_FPS);
168 cam.setResolution(Resolution.RES_VGA);
170 // camera will call the motion detector directly with data not this controller
171 cam.registerCallback(mo);
173 // Start the camera (example is start the HTTP stream if it is a network camera)
175 System.out.println("DEBUG: Initialized camera!");
177 // Remember which motion detector is for what camera
178 camMotionDetect.put(cam, mo);
180 // Initialize detection to false
181 camDetectStatus.put(cam, false);
186 /** Method to initialize alarms
188 * @return [void] None.
190 private void initAlarms() {
192 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
193 Iterator alarmIt = alarmSet.iterator();
194 AlarmSmart alm = (AlarmSmart) alarmIt.next();
195 // init the alarm controller, do it here since it only needs to be done once per controller
198 System.out.println("DEBUG: Initialized alarm!");
199 // TODO: Check that this initialization works for multiple times - might be that setZone() only works once!
200 //for (RoomSmart room : roomSet.values()) {
201 // turnOffAlarms(room.getRoomID());
202 // System.out.println("DEBUG: Initialized alarm for room (turn off): " + room.getRoomID());
204 } catch (Exception e) {
210 /** Method to initialize doorlocks
212 * @return [void] None.
214 private void initDoorLocks(RoomSmart rm) {
216 // Get and init the doorlocks for this specific room
217 /*HashSet<DoorLock> doorlocks = roomDoorLockRelation.get(rm);
218 for (DoorLock doorlock : doorlocks) {
221 // Initialize doorlocks
223 System.out.println("DEBUG: Initialized doorlock!");
224 } catch (Exception e) {
231 /** Method to initialize power outlets
233 * @return [void] None.
235 private void initOutlets(RoomSmart rm) {
237 // Get and init the outlets for this specific room
238 /*HashSet<Outlet> outlets = roomOutletRelation.get(rm);
239 for (Outlet outlet : outlets) {
242 // Initialize outlets
244 System.out.println("DEBUG: Initialized outlet!");
245 } catch (Exception e) {
252 /** Method to detect if a room has seen motion within the last few seconds (time specified as parameter).
253 * Checks all the motion detectors for the given room
255 * @param _room [RoomSmart] , Room of interest.
256 * @param _numberOfSeconds [int] , Number of seconds in the past that we consider recent.
257 * @param _upperThreshold [int] , Number of seconds as an upper bound before we turn off.
259 * @return [boolean] None.
261 private boolean roomDidHaveMotionRecently(RoomSmart _room, int _numberOfSeconds) {
262 long currentTimeSeconds = (new Date()).getTime() / 1000;
264 // Loop through all the cameras in the room
265 for (CameraSmart cam : roomCameraRelation.get(_room)) {
266 long lastDetectedMotionSeconds = currentTimeSeconds;
268 Date motionTime = ((MotionDetection)camMotionDetect.get(cam)).getTimestampOfLastMotion();
270 // Motion was detected at least once
271 if (motionTime != null) {
272 lastDetectedMotionSeconds = motionTime.getTime() / 1000;
274 // motionTime == null means this is the initialization phase
279 // Did detect motion recently
280 if (Math.abs(currentTimeSeconds - lastDetectedMotionSeconds) < _numberOfSeconds) {
289 /** Method to update state data structures for Smartthings sensors
291 * @return [void] None.
293 public void newReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
295 System.out.println("DEBUG: Sensor reading value: " + _value);
297 System.out.println("DEBUG: Sensor is detecting something: " + _activeValue);
298 senDetectStatus.put(_sensorId, true);
301 //System.out.println("DEBUG: Sensor is not detecting something: " + _activeValue);
302 senDetectStatus.put(_sensorId, false);
307 /** Method to update state data structures for doorlocks
309 * @return [void] None.
311 private void updateDoorLockStatus(RoomSmart rm) {
313 // Get and init the outlets for this specific room
314 /*HashSet<DoorLock> doorlocks = roomDoorLockRelation.get(rm);
315 for (DoorLock doorlock : doorlocks) {
317 // Change is detected! Set to true for report...
318 if(isChangeDetected()) {
320 doorlockStatus.put(doorlock, true);
323 doorlockStatus.put(doorlock, false);
329 /** Method to update state data structures for outlets
331 * @return [void] None.
333 private void updateOutletStatus(RoomSmart rm) {
335 // Get and init the outlets for this specific room
336 /*HashSet<Outlet> outlets = roomOutletRelation.get(rm);
337 for (Outlet outlet : outlets) {
339 // Change is detected! Set to true for report...
340 if(isChangeDetected()) {
342 outletStatus.put(outlet, true);
345 outletStatus.put(outlet, false);
351 /** Update the status of all devices
353 * @return [void] None.
355 private void updateUniversalStatus() {
357 // Check for motion in rooms and if there is motion then report
358 for (RoomSmart room : roomSet.values()) {
360 // Update status of camera
361 updateCameraStatus(room);
363 // Update status of doorlocks
364 //updateDoorLockStatus(room);
366 // Update status of outlets
367 //updateOutletStatus(room);
372 /** Update the status of all devices
374 * @return [void] None.
376 private void updateCameraStatus(RoomSmart room) {
378 HashSet<CameraSmart> cameras = roomCameraRelation.get(room);
379 if (roomDidHaveMotionRecently(room, MOTION_TIME_THRESHOLD)) {
381 // Motion was detected
382 System.out.println("DEBUG: Camera detected something!");
383 for(CameraSmart cam : cameras)
384 camDetectStatus.put(cam, true);
387 // No motion was detected
388 //System.out.println("DEBUG: Camera didn't detect anything!");
389 for(CameraSmart cam : cameras)
390 camDetectStatus.put(cam, false);
394 /** Method to turn on alarms
396 * @return [void] None.
398 private void turnOnAlarms(int zoneId) {
400 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
401 Iterator alarmIt = alarmSet.iterator();
402 AlarmSmart alm = (AlarmSmart) alarmIt.next();
403 alm.setZone(zoneId, true, SECOND_TO_TURN_OFF);
407 /** Method to turn off alarms
409 * @return [void] None.
411 private void turnOffAlarms(int zoneId) {
413 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
414 Iterator alarmIt = alarmSet.iterator();
415 AlarmSmart alm = (AlarmSmart) alarmIt.next();
416 // Turn this alarm off indefinitely
417 alm.setZone(zoneId, false, SECOND_TO_TURN_ON);
421 /** Check status of devices and turn on alarm accordingly
423 * Simple rule is whenever any sensor or camera detect something unusual
424 * (sensor/camera becomes active) then we sound the corresponding alarm.
425 * This means we send the signal to the right zone in the EspAlarm
427 * @return [void] None.
429 private void decideToTurnOnAlarm() {
433 // Check for motion in rooms and if there is motion then report
434 for (RoomSmart room : roomSet.values()) {
436 // Loop through all the cameras in the room
437 for (CameraSmart cam : roomCameraRelation.get(room)) {
439 // Get the right camera and the right detection status (true or false)
440 if (camDetectStatus.get(cam)) {
441 zoneId = room.getRoomID();
442 turnOnAlarms(zoneId);
443 System.out.println("DETECTION: Camera active in room: " + zoneId);
447 // Loop through all the cameras in the room
448 for (SmartthingsSensorSmart sensor : roomSensorRelation.get(room)) {
450 // Get the right sensor and the right detection status (true or false)
451 //System.out.println("ABOUT TO DETECT: Reading sensor: " + sensor.getId());
452 if (senDetectStatus.get(sensor.getId())) {
453 zoneId = room.getRoomID();
454 turnOnAlarms(zoneId);
455 System.out.println("DETECTION: Sensor active in room: " + zoneId);
456 System.out.println("DETECTION: Detection by sensor: " + sensor.getId());
463 /** Check status of devices and turn off alarm accordingly
465 * If everything (camera and sensors) is set back to normal
466 * then the system will turn off the alarm
468 * @return [void] None.
470 // TODO: Need to fix this part later
471 // TODO: Perhaps we should use a phone app to turn off the alarm
472 /*private void decideToTurnOffAlarm() {
474 // Check for motion in rooms and if there is motion then report
475 for (RoomSmart room : roomSet.values()) {
477 int zoneId = room.getRoomID();
478 // Loop through all the cameras in the room
479 for (CameraSmart cam : roomCameraRelation.get(room)) {
481 // Get the right camera and the right detection status (true or false)
482 if (camDetectStatus.get(cam)) // Camera still active so alarm is still on, so false for off-alarm status
484 alarmStatus.put(zoneId, false);
488 alarmStatus.put(zoneId, true);
492 // Loop through all the cameras in the room
493 for (SmartthingsSensor sensor : roomSensorRelation.get(room)) {
495 // Get the right sensor and the right detection status (true or false)
496 if (senDetectStatus.get(sensor.getId())) // Sensor still active so alarm is still on, so false for off-alarm status
498 alarmStatus.put(zoneId, false);
502 alarmStatus.put(zoneId, true);
506 // Turn on alarm in the right zone
507 for (Map.Entry<Integer, Boolean> alEnt : alarmStatus.entrySet()) {
508 if (alEnt.getValue()) // if this zone is true, that means we need to turn off its alarm
509 turnOffAlarms(alEnt.getKey());
514 /*******************************************************************************************************************************************
518 *******************************************************************************************************************************************/
520 /** Initialization method, called by the runtime (effectively the main of the controller)
521 * This method runs a continuous loop and is blocking
523 * @return [void] None;
527 // Iterate over the set of rooms
528 for (RoomSmart rm : roomSet.values()) {
530 // Init all Smartthings sensors
531 initSmartthingsSensors(rm);
536 // Init all doorlocks
546 System.out.println("DEBUG: Initialized all devices! Now starting detection loop!");
548 // Run the main loop that will keep checking the sensors and cameras periodically
551 // Run this code every <specified time>
552 long currentTimeSeconds = (new Date()).getTime() / 1000;
553 if ((currentTimeSeconds - lastTimeChecked) > CHECK_TIME_WAIT) {
554 lastTimeChecked = currentTimeSeconds;
556 // Update the status of all devices
557 updateUniversalStatus();
559 // Decide to turn on alarm if any of the sensor/camera detects something unusual
560 decideToTurnOnAlarm();
562 // Decide to turn off alarm if every sensor/camera goes back to normal
563 //decideToTurnOffAlarm();
569 Thread.sleep(CHECK_TIME_WAIT * 100); // sleep for a tenth of the time
571 } catch (Exception e) {