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: 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 // Initialize IoTCloud
256 sen.setId(sensorId++);
257 sen.registerCallback(this);
258 System.out.println("DEBUG: Registered sensor callback!");
259 } catch (Exception e) {
266 /** Method to initialize cameras
268 * @return [void] None.
270 private void initCameras() {
272 // Setup the cameras, start them all and assign each one a motion detector
273 for (CameraSmart cam : camSet.values()) {
275 // Each camera will have a motion detector unique to it since the motion detection has state
276 MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
278 // initialize the camera, might need to setup some stuff internally
281 // set the camera parameters.
282 cam.setFPS(CAMERA_FPS);
283 cam.setResolution(Resolution.RES_VGA);
285 // camera will call the motion detector directly with data not this controller
286 cam.registerCallback(mo);
288 // Start the camera (example is start the HTTP stream if it is a network camera)
290 System.out.println("DEBUG: Initialized camera!");
292 // Remember which motion detector is for what camera
293 camMotionDetect.put(cam, mo);
295 // Initialize detection to false
296 camDetectStatus.put(cam, false);
301 /** Method to initialize alarms
303 * @return [void] None.
305 private void initAlarms() {
307 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
308 Iterator alarmIt = alarmSet.iterator();
309 AlarmSmart alm = (AlarmSmart) alarmIt.next();
310 // Initialize the alarm controller, do it here since it only needs to be done once per controller
313 System.out.println("DEBUG: Initialized alarm!");
314 } catch (Exception e) {
320 /** Method to initialize doorlocks
322 * @return [void] None.
324 private void initDoorLocks() {
326 // Get and init the doorlocks (we only assume 1 main doorlock)
327 Set<SmartthingsActuatorSmart> doorlocks = doorlockSet.values();
328 for (SmartthingsActuatorSmart doorlock : doorlocks) {
331 // Initialize doorlocks
333 System.out.println("DEBUG: Initialized doorlock! ID: " + doorlockId);
334 doorlockStatus.put(doorlockId, false);
335 System.out.println("DEBUG: Initialized doorlock status to false!");
336 doorlock.setId(doorlockId++);
337 doorlock.registerCallback(this);
338 System.out.println("DEBUG: Registered doorlock callback!");
339 } catch (Exception e) {
346 /** Method to detect if a room has seen motion within the last few seconds (time specified as parameter).
347 * Checks all the motion detectors for the given room
349 * @param _room [RoomSmart] , Room of interest.
350 * @param _numberOfSeconds [int] , Number of seconds in the past that we consider recent.
351 * @param _upperThreshold [int] , Number of seconds as an upper bound before we turn off.
353 * @return [boolean] None.
355 private boolean roomDidHaveMotionRecently(RoomSmart _room, int _numberOfSeconds) {
356 long currentTimeSeconds = (new Date()).getTime() / 1000;
358 // Loop through all the cameras in the room
359 for (CameraSmart cam : roomCameraRelation.get(_room)) {
360 long lastDetectedMotionSeconds = currentTimeSeconds;
362 Date motionTime = ((MotionDetection)camMotionDetect.get(cam)).getTimestampOfLastMotion();
364 // Motion was detected at least once
365 if (motionTime != null) {
366 lastDetectedMotionSeconds = motionTime.getTime() / 1000;
368 // motionTime == null means this is the initialization phase
373 // Did detect motion recently
374 if (Math.abs(currentTimeSeconds - lastDetectedMotionSeconds) < _numberOfSeconds) {
383 /** Method to update state data structures for Smartthings sensors
385 * @return [void] None.
387 public void newReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
389 System.out.println("DEBUG: Sensor reading value: " + _value);
391 String sensor = "sensor" + Integer.toString(_sensorId);
393 System.out.println("DEBUG: Sensor " + sensorId + " is detecting something: " + _activeValue);
394 senDetectStatus.put(_sensorId, true);
395 //updateIoTCloud(sensor, ACTIVE);
397 //System.out.println("DEBUG: Sensor " + sensorId + " is not detecting something: " + _activeValue);
398 senDetectStatus.put(_sensorId, false);
399 //updateIoTCloud(sensor, NOT_ACTIVE);
403 /** Method to update state data structures for Smartthings actuators
405 * @return [void] None.
407 public void newActuatorReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
409 System.out.println("DEBUG: Actuator " + _sensorId + " reading value: " + _value);
412 String actuator = "doorlock" + Integer.toString(_sensorId);
414 System.out.println("DEBUG: Actuator " + _sensorId + " is detecting something: " + _activeValue);
415 doorlockStatus.put(_sensorId, true);
416 //updateIoTCloud(actuator, ACTIVE);
418 //System.out.println("DEBUG: Actuator " + _sensorId + " is not detecting something: " + _activeValue);
419 doorlockStatus.put(_sensorId, false);
420 //updateIoTCloud(actuator, NOT_ACTIVE);
425 /** Update the status of all devices
427 * @return [void] None.
429 private void updateUniversalStatus() {
431 // Check for motion in rooms and if there is motion then report
432 for (RoomSmart room : roomSet.values()) {
434 // Update status of camera
435 updateCameraStatus(room);
437 // Update status of other devices if any
443 /** Update the status of all devices
445 * @return [void] None.
447 private void updateCameraStatus(RoomSmart room) {
449 HashSet<CameraSmart> cameras = roomCameraRelation.get(room);
450 if (roomDidHaveMotionRecently(room, MOTION_TIME_THRESHOLD)) {
452 // Motion was detected
453 System.out.println("DEBUG: Camera detected something!");
454 for(CameraSmart cam : cameras) {
455 camDetectStatus.put(cam, true);
456 //updateIoTCloud("camera", ACTIVE);
460 // No motion was detected
461 //System.out.println("DEBUG: Camera didn't detect anything!");
462 for(CameraSmart cam : cameras) {
463 camDetectStatus.put(cam, false);
464 //updateIoTCloud("camera", NOT_ACTIVE);
469 /** Method to turn on alarms
471 * @return [void] None.
473 private void turnOnAlarms(int zoneId) {
475 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
476 Iterator alarmIt = alarmSet.iterator();
477 AlarmSmart alm = (AlarmSmart) alarmIt.next();
478 alm.setZone(zoneId, true, SECOND_TO_TURN_OFF);
479 updateIoTCloud("alarm", ACTIVE);
483 /** Method to turn off alarms
485 * @return [void] None.
487 private void turnOffAlarms(int zoneId) {
489 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
490 Iterator alarmIt = alarmSet.iterator();
491 AlarmSmart alm = (AlarmSmart) alarmIt.next();
492 // Turn this alarm off indefinitely
493 alm.setZone(zoneId, false, SECOND_TO_TURN_ON);
494 updateIoTCloud("alarm", NOT_ACTIVE);
498 /** Method to lock doors
500 * @return [void] None.
502 private void lockDoors() {
504 // Get and lock the doorlocks (we only assume 1 main doorlock)
505 Set<SmartthingsActuatorSmart> doorlocks = doorlockSet.values();
506 for (SmartthingsActuatorSmart doorlock : doorlocks) {
508 doorlock.actuate(LOCK_DOOR);
509 int doorId = doorlock.getId();
510 System.out.println("DEBUG: Lock doorlock! ID: " + doorId);
511 doorlockStatus.put(doorId, true);
512 //updateIoTCloud("doorlock" + doorId, ACTIVE);
517 /** Check status of devices and turn on alarm accordingly
519 * Simple rule is whenever any sensor or camera detect something unusual
520 * (sensor/camera becomes active) then we sound the corresponding alarm.
521 * This means we send the signal to the right zone in the EspAlarm
523 * @return [void] None.
525 private void decideToTurnOnAlarm() {
529 // Check for motion in rooms and if there is motion then report
530 for (RoomSmart room : roomSet.values()) {
532 // Loop through all the cameras in the room
533 for (CameraSmart cam : roomCameraRelation.get(room)) {
535 // Get the right camera and the right detection status (true or false)
536 if (camDetectStatus.get(cam)) {
537 zoneId = room.getRoomID();
538 turnOnAlarms(zoneId);
539 System.out.println("DETECTION: Camera active in room: " + zoneId);
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!");
555 // Loop through all the cameras in the room
556 for (SmartthingsSensorSmart sensor : roomSensorRelation.get(room)) {
558 // Get the right sensor and the right detection status (true or false)
559 //System.out.println("ABOUT TO DETECT: Reading sensor: " + sensor.getId());
560 if (senDetectStatus.get(sensor.getId())) {
561 zoneId = room.getRoomID();
562 turnOnAlarms(zoneId);
563 System.out.println("DETECTION: Sensor active in room: " + zoneId);
564 System.out.println("DETECTION: Detection by sensor: " + sensor.getId());
566 while(readIoTCloud("alarm", ACTIVE)) {
567 System.out.println("DETECTION: Now alarm is on so wait here until turned off!");
569 Thread.sleep(5000); // sleep for a tenth of the time
570 } catch (Exception e) {
573 } // Wait until turned off!
574 System.out.println("DETECTION: Now alarm is turned off!");
583 /** Clean up all status booleans
586 * @return [void] None.
588 private void cleanUp() {
590 // Clear sensor status
591 for (Boolean senStatus : senDetectStatus.values()) {
594 // Clear camera status
595 for (Boolean camStatus : camDetectStatus.values()) {
598 // Clear doorlock status
599 for (Boolean doorStatus : doorlockStatus.values()) {
602 // Clear alarm status
603 updateIoTCloud("alarm", NOT_ACTIVE);
605 for (RoomSmart room : roomSet.values()) {
606 int zoneId = room.getRoomID();
607 turnOffAlarms(zoneId);
612 /*******************************************************************************************************************************************
616 *******************************************************************************************************************************************/
618 /** Initialization method, called by the runtime (effectively the main of the controller)
619 * This method runs a continuous loop and is blocking
621 * @return [void] None;
625 // Initialize IoTCloud server
626 initIoTCloudServer();
628 // Iterate over the set of rooms
629 for (RoomSmart rm : roomSet.values()) {
631 // Init all Smartthings sensors
632 initSmartthingsSensors(rm);
634 // Init all doorlocks
643 System.out.println("DEBUG: Initialized all devices! Now starting detection loop!");
645 // Run the main loop that will keep checking the sensors and cameras periodically
648 // Run this code every <specified time>
649 long currentTimeSeconds = (new Date()).getTime() / 1000;
650 if ((currentTimeSeconds - lastTimeChecked) > CHECK_TIME_WAIT) {
651 lastTimeChecked = currentTimeSeconds;
653 // Update the status of all devices
654 updateUniversalStatus();
656 // Decide to turn on alarm if any of the sensor/camera detects something unusual
657 decideToTurnOnAlarm();
663 Thread.sleep(CHECK_TIME_WAIT * 100); // sleep for a tenth of the time
665 } catch (Exception e) {