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 = 60; // 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: Just use alarm for now as a status to cloud
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) {
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 // Initialize IoTCloud
256 // createKeyIoTCloud("sensor" + Integer.toString(sensorId), NOT_ACTIVE);
257 // System.out.println("DEBUG: Setting sensor" + sensorId + " to NOT-ACTIVE!");
258 sen.setId(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);
300 // Initialize IoTCloud
301 // createKeyIoTCloud("camera", NOT_ACTIVE);
302 // System.out.println("DEBUG: Setting camera to NOT-ACTIVE!");
307 /** Method to initialize alarms
309 * @return [void] None.
311 private void initAlarms() {
313 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
314 Iterator alarmIt = alarmSet.iterator();
315 AlarmSmart alm = (AlarmSmart) alarmIt.next();
316 // Initialize IoTCloud - only 1 alarm
317 // createKeyIoTCloud("alarm", NOT_ACTIVE);
318 // System.out.println("DEBUG: Setting alarm to NOT-ACTIVE!");
319 // Initialize the alarm controller, do it here since it only needs to be done once per controller
322 System.out.println("DEBUG: Initialized alarm!");
323 // TODO: Check that this initialization works for multiple times - might be that setZone() only works once!
324 //for (RoomSmart room : roomSet.values()) {
325 // turnOffAlarms(room.getRoomID());
326 // System.out.println("DEBUG: Initialized alarm for room (turn off): " + room.getRoomID());
328 } catch (Exception e) {
334 /** Method to initialize doorlocks
336 * @return [void] None.
338 private void initDoorLocks() {
340 // Get and init the doorlocks (we only assume 1 main doorlock)
341 Set<SmartthingsActuatorSmart> doorlocks = doorlockSet.values();
342 for (SmartthingsActuatorSmart doorlock : doorlocks) {
345 // Initialize doorlocks
347 System.out.println("DEBUG: Initialized doorlock! ID: " + doorlockId);
348 doorlockStatus.put(doorlockId, false);
349 System.out.println("DEBUG: Initialized doorlock status to false!");
350 // Initialize IoTCloud
351 // createKeyIoTCloud("doorlock" + Integer.toString(doorlockId), NOT_ACTIVE);
352 // System.out.println("DEBUG: Setting doorlock to NOT-ACTIVE!");
353 doorlock.setId(doorlockId++);
354 doorlock.registerCallback(this);
355 System.out.println("DEBUG: Registered doorlock callback!");
356 } catch (Exception e) {
363 /** Method to detect if a room has seen motion within the last few seconds (time specified as parameter).
364 * Checks all the motion detectors for the given room
366 * @param _room [RoomSmart] , Room of interest.
367 * @param _numberOfSeconds [int] , Number of seconds in the past that we consider recent.
368 * @param _upperThreshold [int] , Number of seconds as an upper bound before we turn off.
370 * @return [boolean] None.
372 private boolean roomDidHaveMotionRecently(RoomSmart _room, int _numberOfSeconds) {
373 long currentTimeSeconds = (new Date()).getTime() / 1000;
375 // Loop through all the cameras in the room
376 for (CameraSmart cam : roomCameraRelation.get(_room)) {
377 long lastDetectedMotionSeconds = currentTimeSeconds;
379 Date motionTime = ((MotionDetection)camMotionDetect.get(cam)).getTimestampOfLastMotion();
381 // Motion was detected at least once
382 if (motionTime != null) {
383 lastDetectedMotionSeconds = motionTime.getTime() / 1000;
385 // motionTime == null means this is the initialization phase
390 // Did detect motion recently
391 if (Math.abs(currentTimeSeconds - lastDetectedMotionSeconds) < _numberOfSeconds) {
400 /** Method to update state data structures for Smartthings sensors
402 * @return [void] None.
404 public void newReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
406 System.out.println("DEBUG: Sensor reading value: " + _value);
408 String sensor = "sensor" + Integer.toString(_sensorId);
410 System.out.println("DEBUG: Sensor " + sensorId + " is detecting something: " + _activeValue);
411 senDetectStatus.put(_sensorId, true);
412 //updateIoTCloud(sensor, ACTIVE);
414 //System.out.println("DEBUG: Sensor " + sensorId + " is not detecting something: " + _activeValue);
415 senDetectStatus.put(_sensorId, false);
416 //updateIoTCloud(sensor, NOT_ACTIVE);
420 /** Method to update state data structures for Smartthings actuators
422 * @return [void] None.
424 public void newActuatorReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
426 System.out.println("DEBUG: Actuator " + _sensorId + " reading value: " + _value);
429 String actuator = "doorlock" + Integer.toString(_sensorId);
431 System.out.println("DEBUG: Actuator " + _sensorId + " is detecting something: " + _activeValue);
432 doorlockStatus.put(_sensorId, true);
433 //updateIoTCloud(actuator, ACTIVE);
435 //System.out.println("DEBUG: Actuator " + _sensorId + " is not detecting something: " + _activeValue);
436 doorlockStatus.put(_sensorId, false);
437 //updateIoTCloud(actuator, NOT_ACTIVE);
442 /** Update the status of all devices
444 * @return [void] None.
446 private void updateUniversalStatus() {
448 // Check for motion in rooms and if there is motion then report
449 for (RoomSmart room : roomSet.values()) {
451 // Update status of camera
452 updateCameraStatus(room);
454 // Update status of other devices if any
460 /** Update the status of all devices
462 * @return [void] None.
464 private void updateCameraStatus(RoomSmart room) {
466 HashSet<CameraSmart> cameras = roomCameraRelation.get(room);
467 if (roomDidHaveMotionRecently(room, MOTION_TIME_THRESHOLD)) {
469 // Motion was detected
470 System.out.println("DEBUG: Camera detected something!");
471 for(CameraSmart cam : cameras) {
472 camDetectStatus.put(cam, true);
473 //updateIoTCloud("camera", ACTIVE);
477 // No motion was detected
478 //System.out.println("DEBUG: Camera didn't detect anything!");
479 for(CameraSmart cam : cameras) {
480 camDetectStatus.put(cam, false);
481 //updateIoTCloud("camera", NOT_ACTIVE);
486 /** Method to turn on alarms
488 * @return [void] None.
490 private void turnOnAlarms(int zoneId) {
492 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
493 Iterator alarmIt = alarmSet.iterator();
494 AlarmSmart alm = (AlarmSmart) alarmIt.next();
495 alm.setZone(zoneId, true, SECOND_TO_TURN_OFF);
496 updateIoTCloud("alarm", ACTIVE);
500 /** Method to turn off alarms
502 * @return [void] None.
504 private void turnOffAlarms(int zoneId) {
506 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
507 Iterator alarmIt = alarmSet.iterator();
508 AlarmSmart alm = (AlarmSmart) alarmIt.next();
509 // Turn this alarm off indefinitely
510 alm.setZone(zoneId, false, SECOND_TO_TURN_ON);
511 updateIoTCloud("alarm", NOT_ACTIVE);
515 /** Method to lock doors
517 * @return [void] None.
519 private void lockDoors() {
521 // Get and lock the doorlocks (we only assume 1 main doorlock)
522 Set<SmartthingsActuatorSmart> doorlocks = doorlockSet.values();
523 for (SmartthingsActuatorSmart doorlock : doorlocks) {
525 doorlock.actuate(LOCK_DOOR);
526 int doorId = doorlock.getId();
527 System.out.println("DEBUG: Lock doorlock! ID: " + doorId);
528 doorlockStatus.put(doorId, true);
529 //updateIoTCloud("doorlock" + doorId, ACTIVE);
534 /** Check status of devices and turn on alarm accordingly
536 * Simple rule is whenever any sensor or camera detect something unusual
537 * (sensor/camera becomes active) then we sound the corresponding alarm.
538 * This means we send the signal to the right zone in the EspAlarm
540 * @return [void] None.
542 private void decideToTurnOnAlarm() {
546 // Check for motion in rooms and if there is motion then report
547 for (RoomSmart room : roomSet.values()) {
549 // Loop through all the cameras in the room
550 for (CameraSmart cam : roomCameraRelation.get(room)) {
552 // Get the right camera and the right detection status (true or false)
553 if (camDetectStatus.get(cam)) {
554 zoneId = room.getRoomID();
555 turnOnAlarms(zoneId);
556 System.out.println("DETECTION: Camera active in room: " + zoneId);
558 while(readIoTCloud("alarm", ACTIVE)) {
559 System.out.println("DETECTION: Now alarm is on so wait here until turned off!");
561 Thread.sleep(5000); // sleep for a tenth of the time
562 } catch (Exception e) {
565 } // Wait until turned off!
566 System.out.println("DETECTION: Now alarm is turned off!");
572 // Loop through all the cameras in the room
573 for (SmartthingsSensorSmart sensor : roomSensorRelation.get(room)) {
575 // Get the right sensor and the right detection status (true or false)
576 //System.out.println("ABOUT TO DETECT: Reading sensor: " + sensor.getId());
577 if (senDetectStatus.get(sensor.getId())) {
578 zoneId = room.getRoomID();
579 turnOnAlarms(zoneId);
580 System.out.println("DETECTION: Sensor active in room: " + zoneId);
581 System.out.println("DETECTION: Detection by sensor: " + sensor.getId());
583 while(readIoTCloud("alarm", ACTIVE)) {
584 System.out.println("DETECTION: Now alarm is on so wait here until turned off!");
586 Thread.sleep(5000); // sleep for a tenth of the time
587 } catch (Exception e) {
590 } // Wait until turned off!
591 System.out.println("DETECTION: Now alarm is turned off!");
600 /** Clean up all status booleans
603 * @return [void] None.
605 private void cleanUp() {
607 // Clear sensor status
608 for (Boolean senStatus : senDetectStatus.values()) {
611 // Clear camera status
612 for (Boolean camStatus : camDetectStatus.values()) {
615 // Clear doorlock status
616 for (Boolean doorStatus : doorlockStatus.values()) {
619 // Clear alarm status
620 updateIoTCloud("alarm", NOT_ACTIVE);
622 for (RoomSmart room : roomSet.values()) {
623 int zoneId = room.getRoomID();
624 turnOffAlarms(zoneId);
629 /*******************************************************************************************************************************************
633 *******************************************************************************************************************************************/
635 /** Initialization method, called by the runtime (effectively the main of the controller)
636 * This method runs a continuous loop and is blocking
638 * @return [void] None;
642 // Initialize IoTCloud server
643 initIoTCloudServer();
645 // Iterate over the set of rooms
646 for (RoomSmart rm : roomSet.values()) {
648 // Init all Smartthings sensors
649 initSmartthingsSensors(rm);
651 // Thread.sleep(5000);
652 //} catch (Exception e) {
653 // e.printStackTrace();
657 // Init all doorlocks
666 System.out.println("DEBUG: Initialized all devices! Now starting detection loop!");
668 // Run the main loop that will keep checking the sensors and cameras periodically
671 // Run this code every <specified time>
672 long currentTimeSeconds = (new Date()).getTime() / 1000;
673 if ((currentTimeSeconds - lastTimeChecked) > CHECK_TIME_WAIT) {
674 lastTimeChecked = currentTimeSeconds;
676 // Update the status of all devices
677 updateUniversalStatus();
679 // Decide to turn on alarm if any of the sensor/camera detects something unusual
680 decideToTurnOnAlarm();
686 Thread.sleep(CHECK_TIME_WAIT * 100); // sleep for a tenth of the time
688 } catch (Exception e) {