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 iotcode.annotation.*;
25 // IoT Driver Packages
26 import iotcode.interfaces.*;
28 // Checker annotations
29 //import iotchecker.qual.*;
31 /** Class Home Security Controller for the home security application benchmark
33 * @author Rahmadi Trimananda <rtrimana @ uci.edu>
37 public class HomeSecurityController implements SmartthingsSensorCallback, SmartthingsActuatorCallback {
42 private static final int MOTION_TIME_THRESHOLD = 60; // in seconds
43 private static final int CAMERA_FPS = 15;
44 private static final int CHECK_TIME_WAIT = 1; // in seconds
45 private static final int SECOND_TO_TURN_ON = 60; // in seconds
46 private static final int SECOND_TO_TURN_OFF = 1; // in seconds
47 private static final int LOCK_DOOR = 0;
48 private static final int UNLOCK_DOOR = 1;
51 * IoT Sets and Relations
54 * 1) Multipurpose sensor (detect windows open/close) - Smartthings sensor
55 * 2) Motion sensor (detect motion in certain radius) - Smartthings sensor
56 * 3) Water leak sensor (detect leakage) - Smartthings sensor
57 * 4) Camera (detect motion)
58 * 5) Alarm (using ESP board) - assuming 1 house alarm
59 * 6) Room (object as place of device)
60 * 7) Doorlock (detect open/locked)
62 @config private IoTSet<SmartthingsSensorSmart> smartSensorsSet;
63 @config private IoTSet<CameraSmart> camSet;
64 @config private IoTSet<AlarmSmart> alarmSet;
65 @config private IoTSet<RoomSmart> roomSet;
66 @config private IoTSet<SmartthingsActuatorSmart> doorlockSet;
68 @config private IoTRelation<RoomSmart, SmartthingsSensorSmart> roomSensorRelation;
69 @config private IoTRelation<RoomSmart, CameraSmart> roomCameraRelation;
70 //@config private IoTRelation<RoomSmart, DoorLock> roomDoorLockRelation;
72 /*******************************************************************************************************************************************
76 *******************************************************************************************************************************************/
77 long lastTimeChecked = 0;
79 private static int sensorId = 0;
80 private static int doorlockId = 0;
82 /*******************************************************************************************************************************************
84 ** States data structures
86 *******************************************************************************************************************************************/
87 // Camera and motion detection
88 private Map<CameraSmart, MotionDetection> camMotionDetect =
89 new HashMap<CameraSmart, MotionDetection>();
90 // Smartthings sensors (true = motion, leakage, etc.)
91 private Map<Integer, Boolean> senDetectStatus =
92 new ConcurrentHashMap<Integer, Boolean>();
93 // Camera (true = motion)
94 private Map<CameraSmart, Boolean> camDetectStatus =
95 new HashMap<CameraSmart, Boolean>();
96 // Doorlock (true = locked)
97 private Map<Integer, Boolean> doorlockStatus =
98 new HashMap<Integer, Boolean>();
101 private Map<Integer, Boolean> alarmStatus =
102 new HashMap<Integer, Boolean>();
104 public HomeSecurityController() {
109 /*******************************************************************************************************************************************
113 *******************************************************************************************************************************************/
116 /** Method to initialize Smartthings sensors
118 * @return [void] None.
120 private void initSmartthingsSensors(RoomSmart rm) {
122 // Get and init the IAS sensors for this specific room
123 HashSet<SmartthingsSensorSmart> sensors = roomSensorRelation.get(rm);
124 System.out.println("DEBUG: We have " + sensors.size() + " sensors!");
125 for (SmartthingsSensorSmart sen : sensors) {
128 // Initialize sensors
130 System.out.println("DEBUG: Initialized smartthings sensor! ID: " + sensorId + " Room ID: " + rm.getRoomID());
131 senDetectStatus.put(sensorId, false);
132 System.out.println("DEBUG: Initialized sensor detection to false!");
133 sen.setId(sensorId++);
134 sen.registerCallback(this);
135 System.out.println("DEBUG: Registered sensor callback!");
136 } catch (Exception e) {
143 /** Method to initialize cameras
145 * @return [void] None.
147 private void initCameras(RoomSmart rm) {
149 // Get and init the IAS sensors for this specific room
150 HashSet<CameraSmart> cameras = roomCameraRelation.get(rm);
151 // Setup the cameras, start them all and assign each one a motion detector
152 for (CameraSmart cam : cameras) {
154 // Each camera will have a motion detector unique to it since the motion detection has state
155 MotionDetection mo = new MotionDetection(12, 0.5f, 10, 10);
157 // initialize the camera, might need to setup some stuff internally
160 // set the camera parameters.
161 cam.setFPS(CAMERA_FPS);
162 cam.setResolution(Resolution.RES_VGA);
164 // camera will call the motion detector directly with data not this controller
165 cam.registerCallback(mo);
167 // Start the camera (example is start the HTTP stream if it is a network camera)
169 System.out.println("DEBUG: Initialized camera!");
171 // Remember which motion detector is for what camera
172 camMotionDetect.put(cam, mo);
174 // Initialize detection to false
175 camDetectStatus.put(cam, false);
180 /** Method to initialize alarms
182 * @return [void] None.
184 private void initAlarms() {
186 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
187 Iterator alarmIt = alarmSet.iterator();
188 AlarmSmart alm = (AlarmSmart) alarmIt.next();
189 // init the alarm controller, do it here since it only needs to be done once per controller
192 System.out.println("DEBUG: Initialized alarm!");
193 // TODO: Check that this initialization works for multiple times - might be that setZone() only works once!
194 //for (RoomSmart room : roomSet.values()) {
195 // turnOffAlarms(room.getRoomID());
196 // System.out.println("DEBUG: Initialized alarm for room (turn off): " + room.getRoomID());
198 } catch (Exception e) {
204 /** Method to initialize doorlocks
206 * @return [void] None.
208 private void initDoorLocks() {
210 // Get and init the doorlocks (we only assume 1 main doorlock)
211 Set<SmartthingsActuatorSmart> doorlocks = doorlockSet.values();
212 for (SmartthingsActuatorSmart doorlock : doorlocks) {
215 // Initialize doorlocks
217 System.out.println("DEBUG: Initialized doorlock! ID: " + doorlockId);
218 doorlockStatus.put(doorlockId, false);
219 System.out.println("DEBUG: Initialized doorlock status to false!");
220 doorlock.setId(doorlockId++);
221 doorlock.registerCallback(this);
222 System.out.println("DEBUG: Registered doorlock callback!");
223 } catch (Exception e) {
230 /** Method to detect if a room has seen motion within the last few seconds (time specified as parameter).
231 * Checks all the motion detectors for the given room
233 * @param _room [RoomSmart] , Room of interest.
234 * @param _numberOfSeconds [int] , Number of seconds in the past that we consider recent.
235 * @param _upperThreshold [int] , Number of seconds as an upper bound before we turn off.
237 * @return [boolean] None.
239 private boolean roomDidHaveMotionRecently(RoomSmart _room, int _numberOfSeconds) {
240 long currentTimeSeconds = (new Date()).getTime() / 1000;
242 // Loop through all the cameras in the room
243 for (CameraSmart cam : roomCameraRelation.get(_room)) {
244 long lastDetectedMotionSeconds = currentTimeSeconds;
246 Date motionTime = ((MotionDetection)camMotionDetect.get(cam)).getTimestampOfLastMotion();
248 // Motion was detected at least once
249 if (motionTime != null) {
250 lastDetectedMotionSeconds = motionTime.getTime() / 1000;
252 // motionTime == null means this is the initialization phase
257 // Did detect motion recently
258 if (Math.abs(currentTimeSeconds - lastDetectedMotionSeconds) < _numberOfSeconds) {
267 /** Method to update state data structures for Smartthings sensors
269 * @return [void] None.
271 public void newReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
273 System.out.println("DEBUG: Sensor reading value: " + _value);
275 System.out.println("DEBUG: Sensor is detecting something: " + _activeValue);
276 senDetectStatus.put(_sensorId, true);
279 //System.out.println("DEBUG: Sensor is not detecting something: " + _activeValue);
280 senDetectStatus.put(_sensorId, false);
284 /** Method to update state data structures for Smartthings actuators
286 * @return [void] None.
288 public void newActuatorReadingAvailable(int _sensorId, int _value, boolean _activeValue) {
290 System.out.println("DEBUG: Actuator reading value: " + _value);
292 System.out.println("DEBUG: Actuator is detecting something: " + _activeValue);
293 doorlockStatus.put(_sensorId, true);
296 //System.out.println("DEBUG: Sensor is not detecting something: " + _activeValue);
297 doorlockStatus.put(_sensorId, false);
302 /** Update the status of all devices
304 * @return [void] None.
306 private void updateUniversalStatus() {
308 // Check for motion in rooms and if there is motion then report
309 for (RoomSmart room : roomSet.values()) {
311 // Update status of camera
312 updateCameraStatus(room);
314 // Update status of other devices if any
320 /** Update the status of all devices
322 * @return [void] None.
324 private void updateCameraStatus(RoomSmart room) {
326 HashSet<CameraSmart> cameras = roomCameraRelation.get(room);
327 if (roomDidHaveMotionRecently(room, MOTION_TIME_THRESHOLD)) {
329 // Motion was detected
330 System.out.println("DEBUG: Camera detected something!");
331 for(CameraSmart cam : cameras)
332 camDetectStatus.put(cam, true);
335 // No motion was detected
336 //System.out.println("DEBUG: Camera didn't detect anything!");
337 for(CameraSmart cam : cameras)
338 camDetectStatus.put(cam, false);
342 /** Method to turn on alarms
344 * @return [void] None.
346 private void turnOnAlarms(int zoneId) {
348 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
349 Iterator alarmIt = alarmSet.iterator();
350 AlarmSmart alm = (AlarmSmart) alarmIt.next();
351 alm.setZone(zoneId, true, SECOND_TO_TURN_OFF);
355 /** Method to turn off alarms
357 * @return [void] None.
359 private void turnOffAlarms(int zoneId) {
361 // Get and init the alarm (this single alarm set can serve multiple zones / rooms)
362 Iterator alarmIt = alarmSet.iterator();
363 AlarmSmart alm = (AlarmSmart) alarmIt.next();
364 // Turn this alarm off indefinitely
365 alm.setZone(zoneId, false, SECOND_TO_TURN_ON);
369 /** Method to lock doors
371 * @return [void] None.
373 private void lockDoors() {
375 // Get and lock the doorlocks (we only assume 1 main doorlock)
376 Set<SmartthingsActuatorSmart> doorlocks = doorlockSet.values();
377 for (SmartthingsActuatorSmart doorlock : doorlocks) {
379 doorlock.actuate(LOCK_DOOR);
380 System.out.println("DEBUG: Lock doorlock! ID: " + doorlock.getId());
385 /** Check status of devices and turn on alarm accordingly
387 * Simple rule is whenever any sensor or camera detect something unusual
388 * (sensor/camera becomes active) then we sound the corresponding alarm.
389 * This means we send the signal to the right zone in the EspAlarm
391 * @return [void] None.
393 private void decideToTurnOnAlarm() {
397 // Check for motion in rooms and if there is motion then report
398 for (RoomSmart room : roomSet.values()) {
400 // Loop through all the cameras in the room
401 for (CameraSmart cam : roomCameraRelation.get(room)) {
403 // Get the right camera and the right detection status (true or false)
404 if (camDetectStatus.get(cam)) {
405 zoneId = room.getRoomID();
406 turnOnAlarms(zoneId);
407 System.out.println("DETECTION: Camera active in room: " + zoneId);
412 // Loop through all the cameras in the room
413 for (SmartthingsSensorSmart sensor : roomSensorRelation.get(room)) {
415 // Get the right sensor and the right detection status (true or false)
416 //System.out.println("ABOUT TO DETECT: Reading sensor: " + sensor.getId());
417 if (senDetectStatus.get(sensor.getId())) {
418 zoneId = room.getRoomID();
419 turnOnAlarms(zoneId);
420 System.out.println("DETECTION: Sensor active in room: " + zoneId);
421 System.out.println("DETECTION: Detection by sensor: " + sensor.getId());
429 /** Check status of devices and turn off alarm accordingly
431 * If everything (camera and sensors) is set back to normal
432 * then the system will turn off the alarm
434 * @return [void] None.
436 // TODO: Need to fix this part later
437 // TODO: Perhaps we should use a phone app to turn off the alarm
438 /*private void decideToTurnOffAlarm() {
440 // Check for motion in rooms and if there is motion then report
441 for (RoomSmart room : roomSet.values()) {
443 int zoneId = room.getRoomID();
444 // Loop through all the cameras in the room
445 for (CameraSmart cam : roomCameraRelation.get(room)) {
447 // Get the right camera and the right detection status (true or false)
448 if (camDetectStatus.get(cam)) // Camera still active so alarm is still on, so false for off-alarm status
450 alarmStatus.put(zoneId, false);
454 alarmStatus.put(zoneId, true);
458 // Loop through all the cameras in the room
459 for (SmartthingsSensor sensor : roomSensorRelation.get(room)) {
461 // Get the right sensor and the right detection status (true or false)
462 if (senDetectStatus.get(sensor.getId())) // Sensor still active so alarm is still on, so false for off-alarm status
464 alarmStatus.put(zoneId, false);
468 alarmStatus.put(zoneId, true);
472 // Turn on alarm in the right zone
473 for (Map.Entry<Integer, Boolean> alEnt : alarmStatus.entrySet()) {
474 if (alEnt.getValue()) // if this zone is true, that means we need to turn off its alarm
475 turnOffAlarms(alEnt.getKey());
480 /*******************************************************************************************************************************************
484 *******************************************************************************************************************************************/
486 /** Initialization method, called by the runtime (effectively the main of the controller)
487 * This method runs a continuous loop and is blocking
489 * @return [void] None;
493 // Iterate over the set of rooms
494 for (RoomSmart rm : roomSet.values()) {
496 // Init all Smartthings sensors
497 initSmartthingsSensors(rm);
502 // Init all doorlocks
510 System.out.println("DEBUG: Initialized all devices! Now starting detection loop!");
512 // Run the main loop that will keep checking the sensors and cameras periodically
515 // Run this code every <specified time>
516 long currentTimeSeconds = (new Date()).getTime() / 1000;
517 if ((currentTimeSeconds - lastTimeChecked) > CHECK_TIME_WAIT) {
518 lastTimeChecked = currentTimeSeconds;
520 // Update the status of all devices
521 updateUniversalStatus();
523 // Decide to turn on alarm if any of the sensor/camera detects something unusual
524 decideToTurnOnAlarm();
526 // Decide to turn off alarm if every sensor/camera goes back to normal
527 //decideToTurnOffAlarm();
533 Thread.sleep(CHECK_TIME_WAIT * 100); // sleep for a tenth of the time
535 } catch (Exception e) {