1 package HomeSecurityController;
3 // Standard Java Packages
5 import java.util.Iterator;
6 import java.util.ArrayList;
7 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.concurrent.atomic.AtomicBoolean;
14 import java.util.concurrent.ConcurrentHashMap;
17 import java.rmi.RemoteException;
18 import java.rmi.server.UnicastRemoteObject;
20 // IoT Runtime Packages
21 import iotruntime.slave.IoTSet;
22 import iotruntime.slave.IoTRelation;
23 import iotruntime.slave.IoTAddress;
24 import iotcode.annotation.*;
26 // IoT Driver Packages
27 import iotcode.interfaces.*;
32 /** Class Home Security Controller for the home security application benchmark
34 * @author Rahmadi Trimananda <rtrimana @ uci.edu>
38 public class HomeSecurityController implements SmartthingsSensorCallback, SmartthingsActuatorCallback {
43 private static final int MOTION_TIME_THRESHOLD = 60; // in seconds
44 private static final int CAMERA_FPS = 15;
45 private static final int CHECK_TIME_WAIT = 1; // in seconds
46 private static final int SECOND_TO_TURN_ON = -1; // in seconds
47 private static final int SECOND_TO_TURN_OFF = 1; // in seconds
48 private static final int LOCK_DOOR = 0;
49 private static final int UNLOCK_DOOR = 1;
51 private static final String BASE_URL = "http://dc-6.calit2.uci.edu/test.iotcloud/";
52 private static final String PASSWORD = "reallysecret";
53 private static final int LOCAL_MACHINE_ID = 399;
54 private static final int LISTENING_PORT = -1; // We don't use any listening port for this application
57 * IoT Sets and Relations
60 * 1) Multipurpose sensor (detect windows open/close) - Smartthings sensor
61 * 2) Motion sensor (detect motion in certain radius) - Smartthings sensor
62 * 3) Water leak sensor (detect leakage) - Smartthings sensor
63 * 4) Camera (detect motion)
64 * 5) Alarm (using ESP board) - assuming 1 house alarm
65 * 6) Room (object as place of device)
66 * 7) Doorlock (detect open/locked)
68 @config private IoTSet<SmartthingsSensorSmart> smartSensorsSet;
69 @config private IoTSet<CameraSmart> camSet;
70 @config private IoTSet<AlarmSmart> alarmSet;
71 @config private IoTSet<RoomSmart> roomSet;
72 @config private IoTSet<SmartthingsActuatorSmart> doorlockSet;
73 // Set of addresses for Fidelius connection (dc-6.calit2.uci.edu) to give access
74 @config private IoTSet<IoTAddress> iotcloudServer;
76 @config private IoTRelation<RoomSmart, SmartthingsSensorSmart> roomSensorRelation;
77 @config private IoTRelation<RoomSmart, CameraSmart> roomCameraRelation;
78 //@config private IoTRelation<RoomSmart, DoorLock> roomDoorLockRelation;
80 /*******************************************************************************************************************************************
84 *******************************************************************************************************************************************/
85 long lastTimeChecked = 0;
87 private static int sensorId = 0;
88 private static int doorlockId = 0;
89 // Initialize values 1 and 0 (for active and not active)
90 private final IoTString ACTIVE = new IoTString("1"); // ACTIVE can mean detecting or being locked for doorlock
91 private final IoTString NOT_ACTIVE = new IoTString("0"); // NOT_ACTIVE can mean not detecting or being unlocked for doorlock
92 private Table t1 = null;
94 /*******************************************************************************************************************************************
96 ** States data structures
98 *******************************************************************************************************************************************/
99 // Camera and motion detection
100 private Map<CameraSmart, MotionDetection> camMotionDetect =
101 new HashMap<CameraSmart, MotionDetection>();
102 // Smartthings sensors (true = motion, leakage, etc.)
103 private Map<Integer, Boolean> senDetectStatus =
104 new ConcurrentHashMap<Integer, Boolean>();
105 // Camera (true = motion)
106 private Map<CameraSmart, Boolean> camDetectStatus =
107 new ConcurrentHashMap<CameraSmart, Boolean>();
108 // Doorlock (true = locked)
109 private Map<Integer, Boolean> doorlockStatus =
110 new ConcurrentHashMap<Integer, Boolean>();
113 private Map<Integer, Boolean> alarmStatus =
114 new HashMap<Integer, Boolean>();
116 public HomeSecurityController() {
121 /*******************************************************************************************************************************************
125 *******************************************************************************************************************************************/
127 /** Method to initialize IoTCloud server (dc-6.calit2.uci.edu)
129 * @return [void] None.
131 private void initIoTCloudServer() {
134 System.out.println("DEBUG: Initialize IoTCloud table!");
135 // Get and init the IoTCloud server address
137 t1 = new Table(BASE_URL, PASSWORD, LOCAL_MACHINE_ID, LISTENING_PORT);
139 // Setup is done somewhere else, we just do rebuild()
141 System.out.println("DEBUG: Table initialized!");
142 // Initialize sensors!
143 // TODO: Still deciding whether to initialize all devices here or inside each init method
145 // Initialize alarms! One alarm for now
146 for(AlarmSmart alarm : alarmSet.values()) {
147 createKeyIoTCloud("alarm", NOT_ACTIVE);
148 System.out.println("DEBUG: Setting alarm to NOT-ACTIVE!");
150 // TODO: We can extend the usage of other keys to have a complete monitoring system for every device
151 /*for(SmartthingsSensorSmart smartSensor : smartSensorsSet.values()) {
153 createKeyIoTCloud("sensor" + Integer.toString(id++), NOT_ACTIVE);
154 System.out.println("DEBUG: Setting sensor" + id + " to NOT-ACTIVE!");
156 // Initialize cameras! One camera for now...
157 for(CameraSmart cam : camSet.values()) {
158 createKeyIoTCloud("camera", NOT_ACTIVE);
159 System.out.println("DEBUG: Setting camera to NOT-ACTIVE!");
162 for(SmartthingsActuatorSmart doorlock : doorlockSet.values()) {
163 createKeyIoTCloud("doorlock" + Integer.toString(doorId), NOT_ACTIVE);
164 System.out.println("DEBUG: Setting doorlock" + id + " to NOT-ACTIVE!");
167 } catch(Exception e) {
170 System.out.println("DEBUG: Cloud server transactions committed successfully!");
173 /** Method to create key IoTCloud
175 * @param key [String] , encrypted key.
176 * @param val [IoTString] , encrypted value.
178 * @return [void] None.
180 private void createKeyIoTCloud(String key, IoTString val) {
183 IoTString iotKey = new IoTString(key);
185 t1.createNewKey(iotKey, LOCAL_MACHINE_ID);
186 t1.startTransaction();
187 t1.addKV(iotKey, val);
188 t1.commitTransaction();
190 System.out.println("DEBUG: Successfully committed transaction for: " + key);
191 } catch(Exception e) {
196 /** Method to update IoTCloud
198 * @param key [String] , encrypted key.
199 * @param val [IoTString] , encrypted value.
201 * @return [void] None.
203 private void updateIoTCloud(String key, IoTString val) {
204 // No key creation here!
206 IoTString iotKey = new IoTString(key);
208 t1.startTransaction();
209 t1.addKV(iotKey, val);
210 t1.commitTransaction();
212 System.out.println("DEBUG: Successfully committed transaction for: " + key);
213 } catch(Exception e) {
218 /** Method to read IoTCloud
220 * @param key [String] , encrypted key.
221 * @param val [IoTString] , encrypted value.
223 * @return [boolean] Check if it is ACTIVE or NOT_ACTIVE (true or false).
225 private boolean readIoTCloud(String key, IoTString iotTestVal) {
228 IoTString iotKey = new IoTString(key);
229 IoTString iotVal = t1.getCommitted(iotKey);
231 // Matching value and test value
232 if ((iotVal != null) && (iotVal.equals(iotTestVal) == true))
234 // Mismatch between value and test value
238 /** Method to initialize Smartthings sensors
240 * @return [void] None.
242 private void initSmartthingsSensors(RoomSmart rm) {
244 // Get and init the IAS sensors for this specific room
245 HashSet<SmartthingsSensorSmart> sensors = roomSensorRelation.get(rm);
246 System.out.println("DEBUG: We have " + sensors.size() + " sensors!");
247 for (SmartthingsSensorSmart sen : sensors) {
250 // Initialize sensors
252 System.out.println("DEBUG: Initialized smartthings sensor! ID: " + sensorId + " Room ID: " + rm.getRoomID());
253 senDetectStatus.put(sensorId, false);
254 System.out.println("DEBUG: Initialized sensor detection to false!");
255 System.out.println("DEBUG: Now sensor ID is being set!");
256 // Initialize IoTCloud
257 sen.setId(sensorId++);
258 System.out.println("DEBUG: Set sensor ID to: " + sensorId + "!");
259 sen.registerCallback(this);
260 System.out.println("DEBUG: Registered sensor callback!");
261 } catch (Exception e) {
268 /** Method to initialize cameras
270 * @return [void] None.
272 private void initCameras() {
274 // Setup the cameras, start them all and assign each one a motion detector
275 for (CameraSmart cam : camSet.values()) {
277 // Each camera will have a motion detector unique to it since the motion detection has state
278 MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
280 // initialize the camera, might need to setup some stuff internally
283 // set the camera parameters.
284 cam.setFPS(CAMERA_FPS);
285 cam.setResolution(Resolution.RES_VGA);
287 // camera will call the motion detector directly with data not this controller
288 cam.registerCallback(mo);
290 // Start the camera (example is start the HTTP stream if it is a network camera)
292 System.out.println("DEBUG: Initialized camera!");
294 // Remember which motion detector is for what camera
295 camMotionDetect.put(cam, mo);
297 // Initialize detection to false
298 camDetectStatus.put(cam, false);
303 /** Method to initialize alarms
305 * @return [void] None.
307 private void initAlarms() {
309 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
310 Iterator alarmIt = alarmSet.iterator();
311 AlarmSmart alm = (AlarmSmart) alarmIt.next();
312 // Initialize the alarm controller, do it here since it only needs to be done once per controller
315 System.out.println("DEBUG: Initialized alarm!");
316 } catch (Exception e) {
322 /** Method to initialize doorlocks
324 * @return [void] None.
326 private void initDoorLocks() {
328 // Get and init the doorlocks (we only assume 1 main doorlock)
329 Set<SmartthingsActuatorSmart> doorlocks = doorlockSet.values();
330 for (SmartthingsActuatorSmart doorlock : doorlocks) {
333 // Initialize doorlocks
335 System.out.println("DEBUG: Initialized doorlock! ID: " + doorlockId);
336 doorlockStatus.put(doorlockId, false);
337 System.out.println("DEBUG: Initialized doorlock status to false!");
338 doorlock.setId(doorlockId++);
339 doorlock.registerCallback(this);
340 System.out.println("DEBUG: Registered doorlock callback!");
341 } catch (Exception e) {
348 /** Method to detect if a room has seen motion within the last few seconds (time specified as parameter).
349 * Checks all the motion detectors for the given room
351 * @param _room [RoomSmart] , Room of interest.
352 * @param _numberOfSeconds [int] , Number of seconds in the past that we consider recent.
353 * @param _upperThreshold [int] , Number of seconds as an upper bound before we turn off.
355 * @return [boolean] None.
357 private boolean roomDidHaveMotionRecently(RoomSmart _room, int _numberOfSeconds) {
358 long currentTimeSeconds = (new Date()).getTime() / 1000;
360 // Loop through all the cameras in the room
361 for (CameraSmart cam : roomCameraRelation.get(_room)) {
362 long lastDetectedMotionSeconds = currentTimeSeconds;
364 Date motionTime = ((MotionDetection)camMotionDetect.get(cam)).getTimestampOfLastMotion();
366 // Motion was detected at least once
367 if (motionTime != null) {
368 lastDetectedMotionSeconds = motionTime.getTime() / 1000;
370 // motionTime == null means this is the initialization phase
375 // Did detect motion recently
376 if (Math.abs(currentTimeSeconds - lastDetectedMotionSeconds) < _numberOfSeconds) {
385 /** Method to update state data structures for Smartthings sensors
387 * @return [void] None.
389 public void newReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
391 System.out.println("DEBUG: Sensor reading value: " + _value);
393 String sensor = "sensor" + Integer.toString(_sensorId);
395 System.out.println("DEBUG: Sensor " + sensorId + " is detecting something: " + _activeValue);
396 senDetectStatus.put(_sensorId, true);
397 //updateIoTCloud(sensor, ACTIVE);
399 //System.out.println("DEBUG: Sensor " + sensorId + " is not detecting something: " + _activeValue);
400 senDetectStatus.put(_sensorId, false);
401 //updateIoTCloud(sensor, NOT_ACTIVE);
405 /** Method to update state data structures for Smartthings actuators
407 * @return [void] None.
409 public void newActuatorReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
411 System.out.println("DEBUG: Actuator " + _sensorId + " reading value: " + _value);
414 String actuator = "doorlock" + Integer.toString(_sensorId);
416 System.out.println("DEBUG: Actuator " + _sensorId + " is detecting something: " + _activeValue);
417 doorlockStatus.put(_sensorId, true);
418 //updateIoTCloud(actuator, ACTIVE);
420 //System.out.println("DEBUG: Actuator " + _sensorId + " is not detecting something: " + _activeValue);
421 doorlockStatus.put(_sensorId, false);
422 //updateIoTCloud(actuator, NOT_ACTIVE);
427 /** Update the status of all devices
429 * @return [void] None.
431 private void updateUniversalStatus() {
433 // Check for motion in rooms and if there is motion then report
434 for (RoomSmart room : roomSet.values()) {
436 // Update status of camera
437 updateCameraStatus(room);
439 // Update status of other devices if any
445 /** Update the status of all devices
447 * @return [void] None.
449 private void updateCameraStatus(RoomSmart room) {
451 HashSet<CameraSmart> cameras = roomCameraRelation.get(room);
452 if (roomDidHaveMotionRecently(room, MOTION_TIME_THRESHOLD)) {
454 // Motion was detected
455 System.out.println("DEBUG: Camera detected something!");
456 for(CameraSmart cam : cameras) {
457 camDetectStatus.put(cam, true);
458 //updateIoTCloud("camera", ACTIVE);
462 // No motion was detected
463 //System.out.println("DEBUG: Camera didn't detect anything!");
464 for(CameraSmart cam : cameras) {
465 camDetectStatus.put(cam, false);
466 //updateIoTCloud("camera", NOT_ACTIVE);
471 /** Method to turn on alarms
473 * @return [void] None.
475 private void turnOnAlarms(int zoneId) {
477 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
478 Iterator alarmIt = alarmSet.iterator();
479 AlarmSmart alm = (AlarmSmart) alarmIt.next();
480 alm.setZone(zoneId, true, SECOND_TO_TURN_ON);
481 updateIoTCloud("alarm", ACTIVE);
485 /** Method to turn off alarms
487 * @return [void] None.
489 private void turnOffAlarms(int zoneId) {
491 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
492 Iterator alarmIt = alarmSet.iterator();
493 AlarmSmart alm = (AlarmSmart) alarmIt.next();
494 // Turn this alarm off indefinitely
495 alm.setZone(zoneId, false, SECOND_TO_TURN_OFF);
496 updateIoTCloud("alarm", NOT_ACTIVE);
500 /** Method to lock doors
502 * @return [void] None.
504 private void lockDoors() {
506 // Get and lock the doorlocks (we only assume 1 main doorlock)
507 Set<SmartthingsActuatorSmart> doorlocks = doorlockSet.values();
508 for (SmartthingsActuatorSmart doorlock : doorlocks) {
510 doorlock.actuate(LOCK_DOOR);
511 int doorId = doorlock.getId();
512 System.out.println("DEBUG: Lock doorlock! ID: " + doorId);
513 doorlockStatus.put(doorId, true);
514 //updateIoTCloud("doorlock" + doorId, ACTIVE);
519 /** Check status of devices and turn on alarm accordingly
521 * Simple rule is whenever any sensor or camera detect something unusual
522 * (sensor/camera becomes active) then we sound the corresponding alarm.
523 * This means we send the signal to the right zone in the EspAlarm
525 * @return [void] None.
527 private void decideToTurnOnAlarm() {
531 // Check for motion in rooms and if there is motion then report
532 for (RoomSmart room : roomSet.values()) {
534 // Loop through all the cameras in the room
535 for (CameraSmart cam : roomCameraRelation.get(room)) {
537 // Get the right camera and the right detection status (true or false)
538 if (camDetectStatus.get(cam)) {
539 zoneId = room.getRoomID();
540 turnOnAlarms(zoneId);
541 System.out.println("DETECTION: Camera active in room: " + zoneId);
543 while(readIoTCloud("alarm", ACTIVE)) {
544 System.out.println("DETECTION: Now alarm is on so wait here until turned off!");
546 Thread.sleep(5000); // sleep for a tenth of the time
547 } catch (Exception e) {
550 } // Wait until turned off!
551 System.out.println("DETECTION: Now alarm is turned off!");
557 // Loop through all the cameras in the room
558 for (SmartthingsSensorSmart sensor : roomSensorRelation.get(room)) {
560 // Get the right sensor and the right detection status (true or false)
561 //System.out.println("ABOUT TO DETECT: Reading sensor: " + sensor.getId());
562 if (senDetectStatus.get(sensor.getId())) {
563 zoneId = room.getRoomID();
564 turnOnAlarms(zoneId);
565 System.out.println("DETECTION: Sensor active in room: " + zoneId);
566 System.out.println("DETECTION: Detection by sensor: " + sensor.getId());
568 while(readIoTCloud("alarm", ACTIVE)) {
569 System.out.println("DETECTION: Now alarm is on so wait here until turned off!");
571 Thread.sleep(5000); // sleep for a tenth of the time
572 } catch (Exception e) {
575 } // Wait until turned off!
576 System.out.println("DETECTION: Now alarm is turned off!");
585 /** Clean up all status booleans
588 * @return [void] None.
590 private void cleanUp() {
592 // Clear sensor status
593 for (Boolean senStatus : senDetectStatus.values()) {
596 // Clear camera status
597 for (Boolean camStatus : camDetectStatus.values()) {
600 // Clear doorlock status
601 for (Boolean doorStatus : doorlockStatus.values()) {
604 // Clear alarm status
605 updateIoTCloud("alarm", NOT_ACTIVE);
607 for (RoomSmart room : roomSet.values()) {
608 int zoneId = room.getRoomID();
609 turnOffAlarms(zoneId);
614 /*******************************************************************************************************************************************
618 *******************************************************************************************************************************************/
620 /** Initialization method, called by the runtime (effectively the main of the controller)
621 * This method runs a continuous loop and is blocking
623 * @return [void] None;
627 // Initialize IoTCloud server
628 initIoTCloudServer();
630 // Iterate over the set of rooms
631 for (RoomSmart rm : roomSet.values()) {
633 // Init all Smartthings sensors
634 initSmartthingsSensors(rm);
636 // Init all doorlocks
645 System.out.println("DEBUG: Initialized all devices! Now starting detection loop!");
647 // Run the main loop that will keep checking the sensors and cameras periodically
650 // Run this code every <specified time>
651 long currentTimeSeconds = (new Date()).getTime() / 1000;
652 if ((currentTimeSeconds - lastTimeChecked) > CHECK_TIME_WAIT) {
653 lastTimeChecked = currentTimeSeconds;
655 // Update the status of all devices
656 updateUniversalStatus();
658 // Decide to turn on alarm if any of the sensor/camera detects something unusual
659 decideToTurnOnAlarm();
665 Thread.sleep(CHECK_TIME_WAIT * 100); // sleep for a tenth of the time
667 } catch (Exception e) {