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 System.out.println("DEBUG: Table initialized!");
140 // Initialize sensors!
142 // Initialize alarms! One alarm for now
143 for(AlarmSmart alarm : alarmSet.values()) {
144 createKeyIoTCloud("alarm", NOT_ACTIVE);
145 System.out.println("DEBUG: Setting alarm to NOT-ACTIVE!");
148 } catch(Exception e) {
151 System.out.println("DEBUG: Cloud server transactions committed successfully!");
154 /** Method to create key IoTCloud
156 * @param key [String] , encrypted key.
157 * @param val [IoTString] , encrypted value.
159 * @return [void] None.
161 private void createKeyIoTCloud(String key, IoTString val) {
164 IoTString iotKey = new IoTString(key);
166 t1.createNewKey(iotKey, LOCAL_MACHINE_ID);
167 t1.startTransaction();
168 t1.addKV(iotKey, val);
169 t1.commitTransaction();
171 System.out.println("DEBUG: Successfully committed transaction for: " + key);
172 } catch(Exception e) {
177 /** Method to update IoTCloud
179 * @param key [String] , encrypted key.
180 * @param val [IoTString] , encrypted value.
182 * @return [void] None.
184 private void updateIoTCloud(String key, IoTString val) {
185 // No key creation here!
187 IoTString iotKey = new IoTString(key);
189 t1.startTransaction();
190 t1.addKV(iotKey, val);
191 t1.commitTransaction();
193 System.out.println("DEBUG: Successfully committed transaction for: " + key);
194 } catch(Exception e) {
199 /** Method to read IoTCloud
201 * @param key [String] , encrypted key.
202 * @param val [IoTString] , encrypted value.
204 * @return [boolean] Check if it is ACTIVE or NOT_ACTIVE (true or false).
206 private boolean readIoTCloud(String key, IoTString iotTestVal) {
209 IoTString iotKey = new IoTString(key);
210 IoTString iotVal = t1.getCommitted(iotKey);
212 // Matching value and test value
213 if ((iotVal != null) && (iotVal.equals(iotTestVal) == true))
215 // Mismatch between value and test value
219 /** Method to initialize Smartthings sensors
221 * @return [void] None.
223 private void initSmartthingsSensors(RoomSmart rm) {
225 // Get and init the IAS sensors for this specific room
226 HashSet<SmartthingsSensorSmart> sensors = roomSensorRelation.get(rm);
227 System.out.println("DEBUG: We have " + sensors.size() + " sensors!");
228 for (SmartthingsSensorSmart sen : sensors) {
231 // Initialize sensors
233 System.out.println("DEBUG: Initialized smartthings sensor! ID: " + sensorId + " Room ID: " + rm.getRoomID());
234 senDetectStatus.put(sensorId, false);
235 System.out.println("DEBUG: Initialized sensor detection to false!");
236 System.out.println("DEBUG: Now sensor ID is being set!");
237 // Initialize IoTCloud
238 sen.setId(sensorId++);
239 System.out.println("DEBUG: Set sensor ID to: " + sensorId + "!");
240 sen.registerCallback(this);
241 System.out.println("DEBUG: Registered sensor callback!");
242 } catch (Exception e) {
249 /** Method to initialize cameras
251 * @return [void] None.
253 private void initCameras() {
255 // Setup the cameras, start them all and assign each one a motion detector
256 for (CameraSmart cam : camSet.values()) {
258 // Each camera will have a motion detector unique to it since the motion detection has state
259 MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
261 // initialize the camera, might need to setup some stuff internally
264 // set the camera parameters.
265 cam.setFPS(CAMERA_FPS);
266 cam.setResolution(Resolution.RES_VGA);
268 // camera will call the motion detector directly with data not this controller
269 cam.registerCallback(mo);
271 // Start the camera (example is start the HTTP stream if it is a network camera)
273 System.out.println("DEBUG: Initialized camera!");
275 // Remember which motion detector is for what camera
276 camMotionDetect.put(cam, mo);
278 // Initialize detection to false
279 camDetectStatus.put(cam, false);
284 /** Method to initialize alarms
286 * @return [void] None.
288 private void initAlarms() {
290 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
291 Iterator alarmIt = alarmSet.iterator();
292 AlarmSmart alm = (AlarmSmart) alarmIt.next();
293 // Initialize the alarm controller, do it here since it only needs to be done once per controller
296 System.out.println("DEBUG: Initialized alarm!");
297 } catch (Exception e) {
303 /** Method to initialize doorlocks
305 * @return [void] None.
307 private void initDoorLocks() {
309 // Get and init the doorlocks (we only assume 1 main doorlock)
310 Set<SmartthingsActuatorSmart> doorlocks = doorlockSet.values();
311 for (SmartthingsActuatorSmart doorlock : doorlocks) {
314 // Initialize doorlocks
316 System.out.println("DEBUG: Initialized doorlock! ID: " + doorlockId);
317 doorlockStatus.put(doorlockId, false);
318 System.out.println("DEBUG: Initialized doorlock status to false!");
319 doorlock.setId(doorlockId++);
320 doorlock.registerCallback(this);
321 System.out.println("DEBUG: Registered doorlock callback!");
322 } catch (Exception e) {
329 /** Method to detect if a room has seen motion within the last few seconds (time specified as parameter).
330 * Checks all the motion detectors for the given room
332 * @param _room [RoomSmart] , Room of interest.
333 * @param _numberOfSeconds [int] , Number of seconds in the past that we consider recent.
334 * @param _upperThreshold [int] , Number of seconds as an upper bound before we turn off.
336 * @return [boolean] None.
338 private boolean roomDidHaveMotionRecently(RoomSmart _room, int _numberOfSeconds) {
339 long currentTimeSeconds = (new Date()).getTime() / 1000;
341 // Loop through all the cameras in the room
342 for (CameraSmart cam : roomCameraRelation.get(_room)) {
343 long lastDetectedMotionSeconds = currentTimeSeconds;
345 Date motionTime = ((MotionDetection)camMotionDetect.get(cam)).getTimestampOfLastMotion();
347 // Motion was detected at least once
348 if (motionTime != null) {
349 lastDetectedMotionSeconds = motionTime.getTime() / 1000;
351 // motionTime == null means this is the initialization phase
356 // Did detect motion recently
357 if (Math.abs(currentTimeSeconds - lastDetectedMotionSeconds) < _numberOfSeconds) {
366 /** Method to update state data structures for Smartthings sensors
368 * @return [void] None.
370 public void newReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
372 System.out.println("DEBUG: Sensor reading value: " + _value);
374 String sensor = "sensor" + Integer.toString(_sensorId);
376 System.out.println("DEBUG: Sensor " + sensorId + " is detecting something: " + _activeValue);
377 senDetectStatus.put(_sensorId, true);
379 System.out.println("DEBUG: Sensor " + sensorId + " is not detecting something: " + _activeValue);
380 senDetectStatus.put(_sensorId, false);
384 /** Method to update state data structures for Smartthings actuators
386 * @return [void] None.
388 public void newActuatorReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
390 System.out.println("DEBUG: Actuator " + _sensorId + " reading value: " + _value);
393 String actuator = "doorlock" + Integer.toString(_sensorId);
395 System.out.println("DEBUG: Actuator " + _sensorId + " is detecting something: " + _activeValue);
396 doorlockStatus.put(_sensorId, true);
398 //System.out.println("DEBUG: Actuator " + _sensorId + " is not detecting something: " + _activeValue);
399 doorlockStatus.put(_sensorId, false);
404 /** Update the status of all devices
406 * @return [void] None.
408 private void updateUniversalStatus() {
410 // Check for motion in rooms and if there is motion then report
411 for (RoomSmart room : roomSet.values()) {
413 // Update status of camera
414 updateCameraStatus(room);
416 // Update status of other devices if any
422 /** Update the status of all devices
424 * @return [void] None.
426 private void updateCameraStatus(RoomSmart room) {
428 HashSet<CameraSmart> cameras = roomCameraRelation.get(room);
429 if (roomDidHaveMotionRecently(room, MOTION_TIME_THRESHOLD)) {
431 // Motion was detected
432 System.out.println("DEBUG: Camera detected something!");
433 for(CameraSmart cam : cameras) {
434 camDetectStatus.put(cam, true);
438 // No motion was detected
439 System.out.println("DEBUG: Camera didn't detect anything!");
440 for(CameraSmart cam : cameras) {
441 camDetectStatus.put(cam, false);
446 /** Method to turn on alarms
448 * @return [void] None.
450 private void turnOnAlarms(int zoneId) {
452 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
453 Iterator alarmIt = alarmSet.iterator();
454 AlarmSmart alm = (AlarmSmart) alarmIt.next();
455 alm.setZone(zoneId, true, SECOND_TO_TURN_ON);
456 updateIoTCloud("alarm", ACTIVE);
460 /** Method to turn off alarms
462 * @return [void] None.
464 private void turnOffAlarms(int zoneId) {
466 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
467 Iterator alarmIt = alarmSet.iterator();
468 AlarmSmart alm = (AlarmSmart) alarmIt.next();
469 // Turn this alarm off indefinitely
470 alm.setZone(zoneId, false, SECOND_TO_TURN_OFF);
471 updateIoTCloud("alarm", NOT_ACTIVE);
475 /** Method to lock doors
477 * @return [void] None.
479 private void lockDoors() {
481 // Get and lock the doorlocks (we only assume 1 main doorlock)
482 Set<SmartthingsActuatorSmart> doorlocks = doorlockSet.values();
483 for (SmartthingsActuatorSmart doorlock : doorlocks) {
485 doorlock.actuate(LOCK_DOOR);
486 int doorId = doorlock.getId();
487 System.out.println("DEBUG: Lock doorlock! ID: " + doorId);
488 doorlockStatus.put(doorId, true);
493 /** Check status of devices and turn on alarm accordingly
495 * Simple rule is whenever any sensor or camera detect something unusual
496 * (sensor/camera becomes active) then we sound the corresponding alarm.
497 * This means we send the signal to the right zone in the EspAlarm
499 * @return [void] None.
501 private void decideToTurnOnAlarm() {
505 // Check for motion in rooms and if there is motion then report
506 for (RoomSmart room : roomSet.values()) {
508 // Loop through all the cameras in the room
509 for (CameraSmart cam : roomCameraRelation.get(room)) {
511 // Get the right camera and the right detection status (true or false)
512 if (camDetectStatus.get(cam)) {
513 zoneId = room.getRoomID();
514 turnOnAlarms(zoneId);
515 System.out.println("DETECTION: Camera active in room: " + zoneId);
517 while(readIoTCloud("alarm", ACTIVE)) {
518 System.out.println("DETECTION: Now alarm is on so wait here until turned off!");
520 Thread.sleep(5000); // sleep for a tenth of the time
521 } catch (Exception e) {
524 } // Wait until turned off!
525 System.out.println("DETECTION: Now alarm is turned off!");
531 // Loop through all the cameras in the room
532 for (SmartthingsSensorSmart sensor : roomSensorRelation.get(room)) {
534 // Get the right sensor and the right detection status (true or false)
535 if (senDetectStatus.get(sensor.getId())) {
536 zoneId = room.getRoomID();
537 turnOnAlarms(zoneId);
538 System.out.println("DETECTION: Sensor active in room: " + zoneId);
539 System.out.println("DETECTION: Detection by sensor: " + sensor.getId());
541 while(readIoTCloud("alarm", ACTIVE)) {
542 System.out.println("DETECTION: Now alarm is on so wait here until turned off!");
544 Thread.sleep(5000); // sleep for a tenth of the time
545 } catch (Exception e) {
548 } // Wait until turned off!
549 System.out.println("DETECTION: Now alarm is turned off!");
558 /** Clean up all status booleans
561 * @return [void] None.
563 private void cleanUp() {
565 // Clear sensor status
566 for (Boolean senStatus : senDetectStatus.values()) {
569 // Clear camera status
570 for (Boolean camStatus : camDetectStatus.values()) {
573 // Clear doorlock status
574 for (Boolean doorStatus : doorlockStatus.values()) {
577 // Clear alarm status
578 updateIoTCloud("alarm", NOT_ACTIVE);
580 for (RoomSmart room : roomSet.values()) {
581 int zoneId = room.getRoomID();
582 turnOffAlarms(zoneId);
587 /*******************************************************************************************************************************************
591 *******************************************************************************************************************************************/
593 /** Initialization method, called by the runtime (effectively the main of the controller)
594 * This method runs a continuous loop and is blocking
596 * @return [void] None;
600 // Iterate over the set of rooms
601 for (RoomSmart rm : roomSet.values()) {
603 // Init all Smartthings sensors
604 initSmartthingsSensors(rm);
606 // Init all doorlocks
615 // Initialize IoTCloud server
616 initIoTCloudServer();
618 System.out.println("DEBUG: Initialized all devices! Now starting detection loop!");
620 // Run the main loop that will keep checking the sensors and cameras periodically
623 // Run this code every <specified time>
624 long currentTimeSeconds = (new Date()).getTime() / 1000;
625 if ((currentTimeSeconds - lastTimeChecked) > CHECK_TIME_WAIT) {
626 lastTimeChecked = currentTimeSeconds;
628 // Update the status of all devices
629 updateUniversalStatus();
631 // Decide to turn on alarm if any of the sensor/camera detects something unusual
632 decideToTurnOnAlarm();
638 Thread.sleep(CHECK_TIME_WAIT * 100); // sleep for a tenth of the time
640 } catch (Exception e) {