Adjustment to do initializationof secure cloud later than the sensor/ZigBee initializ...
[iot2.git] / benchmarks / Java / HomeSecurityController / HomeSecurityController.java
1 package HomeSecurityController;
2
3 // Standard Java Packages
4 import java.util.Date;
5 import java.util.Iterator;
6 import java.util.ArrayList;
7 import java.util.HashMap;
8 import java.util.List;
9 import java.util.Map;
10
11 import java.util.Set;
12 import java.util.HashSet;
13 import java.util.concurrent.atomic.AtomicBoolean;
14 import java.util.concurrent.ConcurrentHashMap;
15
16 // RMI packages
17 import java.rmi.RemoteException;
18 import java.rmi.server.UnicastRemoteObject;
19
20 // IoT Runtime Packages
21 import iotruntime.slave.IoTSet;
22 import iotruntime.slave.IoTRelation;
23 import iotruntime.slave.IoTAddress;
24 import iotcode.annotation.*;
25
26 // IoT Driver Packages
27 import iotcode.interfaces.*;
28
29 // IoT Cloud
30 import iotcloud.*;
31
32 /** Class Home Security Controller for the home security application benchmark
33  *
34  * @author      Rahmadi Trimananda <rtrimana @ uci.edu>
35  * @version     1.0
36  * @since       2016-12-14
37  */
38 public class HomeSecurityController implements SmartthingsSensorCallback, SmartthingsActuatorCallback {
39
40         /*
41          *  Constants
42          */
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;
50         // For IoTCloud
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
55         
56         /**
57          *  IoT Sets and Relations
58          *  <p>
59          *  Devices involved:
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)
67          */
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;
75
76         @config private IoTRelation<RoomSmart, SmartthingsSensorSmart> roomSensorRelation;
77         @config private IoTRelation<RoomSmart, CameraSmart> roomCameraRelation;
78         //@config private IoTRelation<RoomSmart, DoorLock> roomDoorLockRelation;
79
80         /*******************************************************************************************************************************************
81         **
82         **  Variables
83         **
84         *******************************************************************************************************************************************/
85         long lastTimeChecked = 0;
86
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;
93
94         /*******************************************************************************************************************************************
95         **
96         **  States data structures
97         **
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>();
111
112         // Alarm status
113         private Map<Integer, Boolean> alarmStatus =
114                 new HashMap<Integer, Boolean>();
115
116         public HomeSecurityController() {
117
118         }
119
120
121         /*******************************************************************************************************************************************
122         **
123         **  Helper Methods
124         **
125         *******************************************************************************************************************************************/
126
127         /** Method to initialize IoTCloud server (dc-6.calit2.uci.edu)
128          *
129          *   @return [void] None.
130          */
131         private void initIoTCloudServer() {
132
133                 try {
134                         System.out.println("DEBUG: Initialize IoTCloud table!");
135                         // Get and init the IoTCloud server address
136                         // Setup table
137                         t1 = new Table(BASE_URL, PASSWORD, LOCAL_MACHINE_ID, LISTENING_PORT);
138                         t1.initTable();
139                         System.out.println("DEBUG: Table initialized!");
140                         // Initialize sensors!
141                         int id = 0;
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!");
146                         }
147
148                 } catch(Exception e) {
149                         e.printStackTrace();
150                 }
151                 System.out.println("DEBUG: Cloud server transactions committed successfully!");
152         }
153
154         /** Method to create key IoTCloud
155          *
156          *   @param key                 [String] , encrypted key.
157          *   @param val                         [IoTString] , encrypted value.
158          *
159          *   @return [void] None.
160          */
161         private void createKeyIoTCloud(String key, IoTString val) {
162
163                 try {
164                         IoTString iotKey = new IoTString(key);
165                         t1.update();
166                         t1.createNewKey(iotKey, LOCAL_MACHINE_ID);
167                         t1.startTransaction();
168                         t1.addKV(iotKey, val);
169                         t1.commitTransaction();
170                         t1.update();
171                         System.out.println("DEBUG: Successfully committed transaction for: " + key);
172                 } catch(Exception e) {
173                         e.printStackTrace();
174                 }
175         }
176
177         /** Method to update IoTCloud
178          *
179          *   @param key                 [String] , encrypted key.
180          *   @param val                         [IoTString] , encrypted value.
181          *
182          *   @return [void] None.
183          */
184         private void updateIoTCloud(String key, IoTString val) {
185                 // No key creation here!
186                 try {
187                         IoTString iotKey = new IoTString(key);
188                         t1.update();
189                         t1.startTransaction();
190                         t1.addKV(iotKey, val);
191                         t1.commitTransaction();
192                         t1.update();
193                         System.out.println("DEBUG: Successfully committed transaction for: " + key);
194                 } catch(Exception e) {
195                         e.printStackTrace();
196                 }
197         }
198
199         /** Method to read IoTCloud
200          *
201          *   @param key                 [String] , encrypted key.
202          *   @param val                         [IoTString] , encrypted value.
203          *
204          *   @return [boolean] Check if it is ACTIVE or NOT_ACTIVE (true or false).
205          */
206         private boolean readIoTCloud(String key, IoTString iotTestVal) {
207
208                 t1.update();
209                 IoTString iotKey = new IoTString(key);
210                 IoTString iotVal = t1.getCommitted(iotKey);
211
212                 // Matching value and test value
213                 if ((iotVal != null) && (iotVal.equals(iotTestVal) == true))
214             return true;
215         // Mismatch between value and test value
216         return false;
217         }
218
219         /** Method to initialize Smartthings sensors
220          *
221          *   @return [void] None.
222          */
223         private void initSmartthingsSensors(RoomSmart rm) {
224
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) {
229         
230                         try {
231                                 // Initialize sensors
232                                 sen.init();
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) {
243                                 e.printStackTrace();
244                         }
245                 }
246         }
247
248
249         /** Method to initialize cameras
250          *
251          *   @return [void] None.
252          */
253         private void initCameras() {
254
255                 // Setup the cameras, start them all and assign each one a motion detector
256                 for (CameraSmart cam : camSet.values()) {
257
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);
260
261                         // initialize the camera, might need to setup some stuff internally
262                         cam.init();
263
264                         // set the camera parameters.
265                         cam.setFPS(CAMERA_FPS);
266                         cam.setResolution(Resolution.RES_VGA);
267
268                         // camera will call the motion detector directly with data not this controller
269                         cam.registerCallback(mo);
270
271                         // Start the camera (example is start the HTTP stream if it is a network camera)
272                         cam.start();
273                         System.out.println("DEBUG: Initialized camera!");
274
275                         // Remember which motion detector is for what camera
276                         camMotionDetect.put(cam, mo);
277
278                         // Initialize detection to false
279                         camDetectStatus.put(cam, false);
280                 }
281         }
282
283
284         /** Method to initialize alarms
285          *
286          *   @return [void] None.
287          */
288         private void initAlarms() {
289
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
294                 try {
295                         alm.init();
296                         System.out.println("DEBUG: Initialized alarm!");
297                 } catch (Exception e) {
298                         e.printStackTrace();
299                 }
300         }
301
302
303         /** Method to initialize doorlocks
304          *
305          *   @return [void] None.
306          */
307         private void initDoorLocks() {
308
309                 // Get and init the doorlocks (we only assume 1 main doorlock)
310                 Set<SmartthingsActuatorSmart> doorlocks = doorlockSet.values();
311                 for (SmartthingsActuatorSmart doorlock : doorlocks) {
312         
313                         try {
314                                 // Initialize doorlocks
315                                 doorlock.init();
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) {
323                                 e.printStackTrace();
324                         }
325                 }
326         }
327
328
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
331          *
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.
335          *
336          *   @return [boolean] None.
337          */
338         private boolean roomDidHaveMotionRecently(RoomSmart _room, int _numberOfSeconds) {
339                 long currentTimeSeconds = (new Date()).getTime() / 1000;
340
341                 // Loop through all the cameras in the room
342                 for (CameraSmart cam : roomCameraRelation.get(_room)) {
343                         long lastDetectedMotionSeconds = currentTimeSeconds;
344
345                         Date motionTime = ((MotionDetection)camMotionDetect.get(cam)).getTimestampOfLastMotion();
346
347                         // Motion was detected at least once
348                         if (motionTime != null) {
349                                 lastDetectedMotionSeconds = motionTime.getTime() / 1000;
350                         } else {
351                                 // motionTime == null means this is the initialization phase
352                                 // so we put false
353                                 return false;
354                         }
355
356                         // Did detect motion recently
357                         if (Math.abs(currentTimeSeconds - lastDetectedMotionSeconds) < _numberOfSeconds) {
358                                 return true;
359                         }
360                 }
361
362                 return false;
363         }
364
365
366         /** Method to update state data structures for Smartthings sensors
367          *
368          *   @return [void] None.
369          */
370         public void newReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
371
372                 System.out.println("DEBUG: Sensor reading value: " + _value);
373
374                 String sensor = "sensor" + Integer.toString(_sensorId);
375                 if(_activeValue) {
376                         System.out.println("DEBUG: Sensor " + sensorId + " is detecting something: " + _activeValue);
377                         senDetectStatus.put(_sensorId, true);
378                 } else {
379                         System.out.println("DEBUG: Sensor " + sensorId + " is not detecting something: " + _activeValue);
380                         senDetectStatus.put(_sensorId, false);
381                 }
382         }
383
384         /** Method to update state data structures for Smartthings actuators
385          *
386          *   @return [void] None.
387          */
388         public void newActuatorReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
389
390                 System.out.println("DEBUG: Actuator " + _sensorId + " reading value: " + _value);
391
392                 // Update IoTCloud              
393                 String actuator = "doorlock" + Integer.toString(_sensorId);
394                 if(_activeValue) {
395                         System.out.println("DEBUG: Actuator " + _sensorId + " is detecting something: " + _activeValue);
396                         doorlockStatus.put(_sensorId, true);
397                 } else {
398                         //System.out.println("DEBUG: Actuator " + _sensorId + " is not detecting something: " + _activeValue);
399                         doorlockStatus.put(_sensorId, false);
400                 }
401         }
402
403
404         /** Update the status of all devices
405          *
406          *   @return [void] None.
407          */
408         private void updateUniversalStatus() {
409
410                 // Check for motion in rooms and if there is motion then report
411                 for (RoomSmart room : roomSet.values()) {
412
413                         // Update status of camera
414                         updateCameraStatus(room);
415
416                         // Update status of other devices if any
417                         // ...
418                 }
419         }
420
421
422         /** Update the status of all devices
423          *
424          *   @return [void] None.
425          */
426         private void updateCameraStatus(RoomSmart room) {
427
428                 HashSet<CameraSmart> cameras = roomCameraRelation.get(room);
429                 if (roomDidHaveMotionRecently(room, MOTION_TIME_THRESHOLD)) {
430
431                         // Motion was detected
432                         System.out.println("DEBUG: Camera detected something!");
433                         for(CameraSmart cam : cameras) {
434                                 camDetectStatus.put(cam, true);
435                         }
436                 } else {
437
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);
442                         }
443                 }
444         }
445
446         /** Method to turn on alarms
447          *
448          *   @return [void] None.
449          */
450         private void turnOnAlarms(int zoneId) {
451
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);
457         }
458
459
460         /** Method to turn off alarms
461          *
462          *   @return [void] None.
463          */
464         private void turnOffAlarms(int zoneId) {
465
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);
472         }
473
474
475         /** Method to lock doors
476          *
477          *   @return [void] None.
478          */
479         private void lockDoors() {
480
481                 // Get and lock the doorlocks (we only assume 1 main doorlock)
482                 Set<SmartthingsActuatorSmart> doorlocks = doorlockSet.values();
483                 for (SmartthingsActuatorSmart doorlock : doorlocks) {
484         
485                         doorlock.actuate(LOCK_DOOR);
486                         int doorId = doorlock.getId();
487                         System.out.println("DEBUG: Lock doorlock! ID: " + doorId);
488                         doorlockStatus.put(doorId, true);
489                 }
490         }
491
492
493         /** Check status of devices and turn on alarm accordingly
494          *  <p>
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
498          *
499          *   @return [void] None.
500          */
501         private void decideToTurnOnAlarm() {
502
503                 int zoneId = 0;
504
505                 // Check for motion in rooms and if there is motion then report
506                 for (RoomSmart room : roomSet.values()) {
507
508                         // Loop through all the cameras in the room
509                         for (CameraSmart cam : roomCameraRelation.get(room)) {
510
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);
516                                         lockDoors();
517                                         while(readIoTCloud("alarm", ACTIVE)) { 
518                                                 System.out.println("DETECTION: Now alarm is on so wait here until turned off!");
519                                                 try {
520                                                         Thread.sleep(5000); // sleep for a tenth of the time
521                                                 } catch (Exception e) {
522                                                         e.printStackTrace();
523                                                 }
524                                         } // Wait until turned off!
525                                         System.out.println("DETECTION: Now alarm is turned off!");
526                                         cleanUp();
527                                         return;
528                                 }
529                         }
530
531                         // Loop through all the cameras in the room
532                         for (SmartthingsSensorSmart sensor : roomSensorRelation.get(room)) {
533
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());
540                                         lockDoors();
541                                         while(readIoTCloud("alarm", ACTIVE)) { 
542                                                 System.out.println("DETECTION: Now alarm is on so wait here until turned off!");
543                                                 try {
544                                                         Thread.sleep(5000); // sleep for a tenth of the time
545                                                 } catch (Exception e) {
546                                                         e.printStackTrace();
547                                                 }
548                                         } // Wait until turned off!
549                                         System.out.println("DETECTION: Now alarm is turned off!");
550                                         cleanUp();
551                                         return;
552                                 }
553                         }
554                 }
555         }
556
557
558         /** Clean up all status booleans
559          *  <p>
560          *
561          *   @return [void] None.
562          */
563         private void cleanUp() {
564
565                 // Clear sensor status
566                 for (Boolean senStatus : senDetectStatus.values()) {
567                         senStatus = false;
568                 }
569                 // Clear camera status
570                 for (Boolean camStatus : camDetectStatus.values()) {
571                         camStatus = false;
572                 }
573                 // Clear doorlock status
574                 for (Boolean doorStatus : doorlockStatus.values()) {
575                         doorStatus = false;
576                 }
577                 // Clear alarm status
578                 updateIoTCloud("alarm", NOT_ACTIVE);
579                 // Turn off alaarms
580                 for (RoomSmart room : roomSet.values()) {
581                         int zoneId = room.getRoomID();
582                         turnOffAlarms(zoneId);
583                 }
584         }
585
586
587         /*******************************************************************************************************************************************
588         **
589         **  Public Methods
590         **
591         *******************************************************************************************************************************************/
592
593         /** Initialization method, called by the runtime (effectively the main of the controller)
594          *   This method runs a continuous loop and is blocking
595          *
596          *   @return [void] None;
597          */
598         public void init() {
599
600                 // Iterate over the set of rooms
601                 for (RoomSmart rm : roomSet.values()) {
602
603                         // Init all Smartthings sensors
604                         initSmartthingsSensors(rm);
605                 }
606                 // Init all doorlocks
607                 initDoorLocks();
608
609                 // Init all alarms
610                 initAlarms();
611
612                 // Init all cameras
613                 initCameras();
614
615                 // Initialize IoTCloud server
616                 initIoTCloudServer();
617
618                 System.out.println("DEBUG: Initialized all devices! Now starting detection loop!");
619
620                 // Run the main loop that will keep checking the sensors and cameras periodically
621                 while (true) {
622
623                         // Run this code every <specified time>
624                         long currentTimeSeconds = (new Date()).getTime() / 1000;
625                         if ((currentTimeSeconds - lastTimeChecked) > CHECK_TIME_WAIT) {
626                                 lastTimeChecked = currentTimeSeconds;
627
628                                 // Update the status of all devices
629                                 updateUniversalStatus();
630
631                                 // Decide to turn on alarm if any of the sensor/camera detects something unusual
632                                 decideToTurnOnAlarm();
633
634                         } else {
635
636                                 try {
637
638                                         Thread.sleep(CHECK_TIME_WAIT * 100); // sleep for a tenth of the time
639
640                                 } catch (Exception e) {
641
642                                         e.printStackTrace();
643                                 }
644                         }
645
646                 }
647         }
648 }
649
650