diff --git a/.gitignore b/.gitignore index aa2d4c2..6f38751 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ build/* .DS_Store # BlueJ files *.ctxt +ouimeaux-env/ !gradle-wrapper.jar diff --git a/build.gradle b/build.gradle index 5835a51..6539fbe 100644 --- a/build.gradle +++ b/build.gradle @@ -7,9 +7,7 @@ apply plugin: 'java' apply plugin: 'application' -//mainClassName = 'MonitorLauncher' -mainClassName = 'edu.cmu.sei.kalki.IotInterface' -//mainClassName = 'edu.cmu.sei.kalki.Mail.SendMail' +mainClassName = 'edu.cmu.sei.kalki.iotinterface.IotInterface' sourceCompatibility = 1.8 @@ -22,9 +20,6 @@ repositories { } dependencies { -// compile name: 'slf4j-api-1.6.1' -// compile name: 'mail' -// compile name: 'activation' compile group: 'org.eclipse.jetty', name: 'jetty-servlet', version: '9.4.19.v20190610' compile group: 'org.subethamail', name: 'subethasmtp', version: '3.1.3' compile 'org.json:json:20171018' @@ -32,5 +27,5 @@ dependencies { compile name: 'huelocalsdk' compile name: 'huesdkresources' compile 'com.jcraft:jsch:0.1.55' -// compile name: 'tablelayout' + compile 'org.apache.httpcomponents:httpclient:4.5.10' } diff --git a/config.json b/config.json index f4fe9c6..9686508 100644 --- a/config.json +++ b/config.json @@ -1,3 +1,7 @@ { - "MAIL_PORT":25000 + "MAIL_PORT": "25000", + "dlink_notification_email": "camera1@dlink.com", + "iot_interface_api_port": "5050", + "device_controller_api_ip": "10.27.153.3", + "device_controller_api_port": "9090" } diff --git a/run.sh b/run.sh new file mode 100644 index 0000000..1ce5653 --- /dev/null +++ b/run.sh @@ -0,0 +1,5 @@ +export http_proxy="" +export https_proxy="" +export HTTP_PROXY="" +export HTTPS_PROXY="" +./gradlew run diff --git a/src/main/java/edu/cmu/sei/kalki/DeviceMonitor.java b/src/main/java/edu/cmu/sei/kalki/DeviceMonitor.java deleted file mode 100644 index 60eb0fb..0000000 --- a/src/main/java/edu/cmu/sei/kalki/DeviceMonitor.java +++ /dev/null @@ -1,78 +0,0 @@ -package edu.cmu.sei.kalki; - -import edu.cmu.sei.kalki.utils.DeviceControllerApi; -import edu.cmu.sei.ttg.kalki.models.Device; -import edu.cmu.sei.kalki.Monitors.IotMonitor; -import edu.cmu.sei.kalki.Monitors.PollingMonitor; -import edu.cmu.sei.ttg.kalki.models.StageLog; - -import java.util.HashMap; -import java.util.logging.Logger; - -public class DeviceMonitor { - private Logger logger = Logger.getLogger("iot-interface"); - private HashMap monitors; - private String apiUrl; - - public DeviceMonitor(String url) { - apiUrl = url; - monitors = new HashMap(); - } - - public String getApiUrl(){ - return apiUrl; - } - - /** - * Starts a new monitor for the given device - * @param device - */ - public void startMonitor(Device device) { - if(device.getSamplingRate() == 0){ - logger.info("[DeviceMonitor] Sampling rate of 0. Not starting monitor."); - logUpdateMonitor(device, "0 sampling rate"); - } - else { - logger.info("[DeviceMonitor] Starting monitor for device: "+device.getId()); - IotMonitor mon = IotMonitor.fromDevice(device, apiUrl); - monitors.put(device.getId(), mon); - logUpdateMonitor(device, "Monitor started"); - - } - } - - /** - * Updates the sampling rate for the given device - * @param device - */ - public void updateMonitor(Device device) { - IotMonitor mon = monitors.get(device.getId()); - if(mon != null && mon.getPollInterval() != device.getSamplingRate()){ - logger.info("[DeviceMonitor] Updating monitor for device: "+device.getId()); - if(mon.isPollable() && device.getSamplingRate() > 0){ - logger.info("[DeviceMonitor] Found monitor, updating sampling rate"); - mon.setPollInterval(device.getSamplingRate()); - monitors.replace(device.getId(), mon); - logUpdateMonitor(device, "Updated monitor"); - } else { - logUpdateMonitor(device, "Monitor not updated"); - } - - } else { - logger.severe("[DeviceMonitor] No monitor found for given device "+device.getId()+". Starting one..."); - startMonitor(device); - } - } - - /** - * Sends a StageLog to the DeviceControllerApi to record updating a sampling rate - * @param device Device the monitor was updated for - */ - private void logUpdateMonitor(Device device, String info) { - logger.info("[DeviceMonitor] Logging monitor update for device: "+device.getId()); - StageLog log = new StageLog(device.getCurrentState().getId(), StageLog.Action.INCREASE_SAMPLE_RATE, StageLog.Stage.FINISH, info); - DeviceControllerApi.sendLog(log, apiUrl); - } - - -} diff --git a/src/main/java/edu/cmu/sei/kalki/Monitors/DLinkCameraMonitor.java b/src/main/java/edu/cmu/sei/kalki/Monitors/DLinkCameraMonitor.java deleted file mode 100644 index 6ebd4ec..0000000 --- a/src/main/java/edu/cmu/sei/kalki/Monitors/DLinkCameraMonitor.java +++ /dev/null @@ -1,28 +0,0 @@ -package edu.cmu.sei.kalki.Monitors; - -import edu.cmu.sei.kalki.Mail.EventObserver; -import edu.cmu.sei.kalki.Mail.MailServer; -import edu.cmu.sei.ttg.kalki.models.DeviceStatus; - -public class DLinkCameraMonitor extends IotMonitor implements EventObserver { - - private String listenEmail = "camera1@dlink.com"; - - public DLinkCameraMonitor(int deviceId, String ip, int samplingRate, String url){ - this.apiUrl = url; - MailServer.initialize(); - MailServer.registerObserver(this); - isPollable = false; - this.pollInterval = samplingRate; - logger.info("[DLinkCameraMonitor] Monitor started for device: "+deviceId); - this.deviceId = deviceId; - } - public void notify(String message){ - if (message.equals(listenEmail)){ - DeviceStatus status = new DeviceStatus(deviceId); - status.addAttribute("motion_detected", "true"); - sendToDeviceController(status); - } - } - -} diff --git a/src/main/java/edu/cmu/sei/kalki/Monitors/IotMonitor.java b/src/main/java/edu/cmu/sei/kalki/Monitors/IotMonitor.java deleted file mode 100644 index 49a840e..0000000 --- a/src/main/java/edu/cmu/sei/kalki/Monitors/IotMonitor.java +++ /dev/null @@ -1,70 +0,0 @@ -package edu.cmu.sei.kalki.Monitors; - -import java.lang.reflect.Constructor; -import java.util.Timer; -import java.util.logging.Logger; - -import edu.cmu.sei.kalki.utils.DeviceControllerApi; -import edu.cmu.sei.ttg.kalki.models.*; - -public abstract class IotMonitor { - protected String apiUrl; - protected Timer pollTimer = new Timer(); - protected int pollInterval; - protected boolean isPollable; - protected boolean pollingEnabled = true; - protected boolean timerGoing = false; - public int deviceId; - - protected final Logger logger = Logger.getLogger("iot-interface"); - - public IotMonitor(){ } - - public static IotMonitor fromDevice(Device device, String apiUrl){ - Logger logger = Logger.getLogger("iot-interface"); - try { - String classPath = "edu.cmu.sei.kalki.Monitors."+getDeviceTypeMonitorClassName(device.getType().getName()); - Constructor con = Class.forName(classPath).getConstructor(Integer.TYPE, String.class, Integer.TYPE, String.class); - IotMonitor mon = (IotMonitor) con.newInstance(device.getId(), device.getIp(), device.getSamplingRate(), apiUrl); - return mon; - } catch (Exception e2){ - e2.printStackTrace(); - logger.info(e2.getMessage()); - - return null; - } - } - - /** - * Removes spaces from device type's name and append 'Monitor' - * @param devTypeName - * @return device type's monitor class name - */ - private static String getDeviceTypeMonitorClassName(String devTypeName) { - String[] temp = devTypeName.split(" "); - String name = ""; - for(int i=0;i lights = new ArrayList(); - private DeviceStatus status; - - public PhilipsHueLightEmulatorMonitor( int deviceId, String ip, int samplingRate, String url) { - super(); - this.isPollable = true; - logger.info("[PhilipsHueLightEmulatorMonitor] Starting monitor for device: "+deviceId); - this.apiUrl = url; - this.ip = ip; - - PHHueSDK phHueSDK = PHHueSDK.create(); - phHueSDK.getNotificationManager().registerSDKListener(listener); - this.pollInterval = samplingRate; - this.phHueSDK = PHHueSDK.getInstance(); - this.deviceId = deviceId; - this.username = "f450ab20effc384c3298bbcf745272a"; - start(); - } - - @Override - public void start(){ - connectToDevice(); - super.start(); - } - - public void findBridges() { - phHueSDK = PHHueSDK.getInstance(); - PHBridgeSearchManager sm = (PHBridgeSearchManager) phHueSDK.getSDKService(PHHueSDK.SEARCH_BRIDGE); - sm.search(true, true); - } - - private PHSDKListener listener = new PHSDKListener() { - - @Override - public void onAccessPointsFound(List accessPointsList) { - logger.info("Found AccessPoints!"); - - } - - @Override - public void onAuthenticationRequired(PHAccessPoint accessPoint) { - // Start the Pushlink Authentication. - logger.info("AuthenticationRequired"); - phHueSDK.startPushlinkAuthentication(accessPoint); - } - - @Override - public void onBridgeConnected(PHBridge bridge, String username) { - phHueSDK.setSelectedBridge(bridge); - logger.info("Connected to bridge"); - String lastIpAddress = bridge.getResourceCache().getBridgeConfiguration().getIpAddress(); - logger.info("IP is : " + lastIpAddress); - logger.info("device ip is: "+ip); - if(!comparePorts(ip, lastIpAddress)) - return; - logger.info("Username is: " + username); - if(pollingEnabled){ - phHueSDK.disableAllHeartbeat(); - phHueSDK.enableHeartbeat(bridge, getHeartbeatInterval()); - } - setStatuses(bridge); - } - - @Override - public void onCacheUpdated(List arg0, PHBridge arg1) { - } - - @Override - public void onConnectionLost(PHAccessPoint arg0) { - } - - @Override - public void onConnectionResumed(PHBridge arg0) { - } - - @Override - public void onError(int code, final String message) { - - if (code == PHHueError.BRIDGE_NOT_RESPONDING) { - logger.info("Bridge not responding: " + message); - } - else if (code == PHMessageType.PUSHLINK_BUTTON_NOT_PRESSED) { - logger.info("Button not pressed: " + message); - } - else if (code == PHMessageType.PUSHLINK_AUTHENTICATION_FAILED) { - logger.info("Authentication failed: " + message); - } - else if (code == PHMessageType.BRIDGE_NOT_FOUND) { - logger.info("Bridge not found: " + message); - } - } - - @Override - public void onParsingErrors(List parsingErrorsList) { - for (PHHueParsingError parsingError: parsingErrorsList) { - logger.info("ParsingError : " + parsingError.getMessage()); - } - } - }; - - public void randomLights() { - PHBridge bridge = phHueSDK.getSelectedBridge(); - PHBridgeResourcesCache cache = bridge.getResourceCache(); - - List allLights = cache.getAllLights(); - Random rand = new Random(); - - for (PHLight light : allLights) { - PHLightState lightState = new PHLightState(); - lightState.setHue(rand.nextInt(MAX_HUE)); - bridge.updateLightState(light, lightState); // If no bridge response is required then use this simpler form. - } - } - - private boolean comparePorts(String deviceUrl, String bridgeUrl){ - String[] dev = deviceUrl.split(":"); - String[] bridge = bridgeUrl.split(":"); - - if(dev[1].equals(bridge[1])) - return true; - - return false; - } - - public void setStatuses(PHBridge bridge) { - PHBridgeResourcesCache cache = bridge.getResourceCache(); - // And now you can get any resource you want, for example: - List myLights = cache.getAllLights(); - lights = new ArrayList(); - for(PHLight light : myLights){ - PHLightState state = light.getLastKnownLightState(); - String id = light.getUniqueId(); - if (id == null){ - id = UUID.randomUUID().toString(); - light.setUniqueId(id); - } - DeviceStatus newLight = new DeviceStatus(deviceId); - newLight.addAttribute("brightness", state.getBrightness().toString()); - newLight.addAttribute("hue", state.getHue().toString()); - newLight.addAttribute("isOn", state.isOn().toString()); - newLight.addAttribute("lightId", id); - lights.add(newLight); - } - } - - /** - * Connects to bridge using the given ip. - * @param ip address of the bridge. - * @return true if there was sufficient information to attempt a connection. - */ - public boolean connectToAccessPoint(String ip) { - if (username==null || ip == null) { - logger.info("Missing Last Username or Last IP. Last known connection not found."); - return false; - } - PHAccessPoint accessPoint = new PHAccessPoint(); - accessPoint.setIpAddress(ip); - accessPoint.setUsername(username); - phHueSDK.connect(accessPoint); - return true; - } - - @Override - public void startPolling(){ - super.startPolling(); - PHBridge bridge = phHueSDK.getSelectedBridge(); - if (bridge != null){ - phHueSDK.enableHeartbeat(bridge, getHeartbeatInterval()); - } - } - - /** - * Heartbeat interval needs to be slightly less than the pollInterval to guarantee correctness. - * @return - */ - private int getHeartbeatInterval(){ - return Math.max(pollInterval/3, pollInterval-300); - } - - @Override - public void stopPolling() { - super.stopPolling(); - PHBridge bridge = phHueSDK.getSelectedBridge(); - phHueSDK.disableHeartbeat(bridge); - } - - @Override - public void pollDevice() { - PHHueSDK phHueSDK = PHHueSDK.getInstance(); - PHBridge bridge = phHueSDK.getSelectedBridge(); - if (bridge == null){ - logger.severe("Null Bridge"); - return; - } - } - - @Override - public void saveCurrentState() { - for(DeviceStatus light : lights){ - sendToDeviceController(light); - } - } - - public void connectToDevice() { - connectToAccessPoint(ip); - } -} diff --git a/src/main/java/edu/cmu/sei/kalki/Monitors/WeMoInsightMonitor.java b/src/main/java/edu/cmu/sei/kalki/Monitors/WeMoInsightMonitor.java deleted file mode 100644 index 0cc1ff7..0000000 --- a/src/main/java/edu/cmu/sei/kalki/Monitors/WeMoInsightMonitor.java +++ /dev/null @@ -1,89 +0,0 @@ -package edu.cmu.sei.kalki.Monitors; - -import edu.cmu.sei.ttg.kalki.models.DeviceStatus; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.HashMap; -import java.util.Map; -import java.lang.StringBuilder; -import org.json.JSONException; -import org.json.JSONObject; - -public class WeMoInsightMonitor extends PollingMonitor { - - private String deviceIp; - private Boolean isOn; - private int deviceId; - - private Map attributes; - private DeviceStatus status; - - public WeMoInsightMonitor(int deviceId, String ip, int samplingRate, String url){ - this.deviceIp = ip; - this.deviceId = deviceId; - this.pollInterval = samplingRate; - this.isPollable = true; - this.apiUrl = url; - logger.info("[WeMoInsightMonitor] Starting monitor."); - start(); - } - - @Override - public void pollDevice() { - attributes = new HashMap(); - try { - - // run the command - // using the Runtime exec method: - String[] args = new String[]{ - "python", - "wemo.py", - deviceIp, - "status" - }; - Process p = Runtime.getRuntime().exec(args); - - BufferedReader stdInput = new BufferedReader(new - InputStreamReader(p.getInputStream())); - - BufferedReader stdError = new BufferedReader(new - InputStreamReader(p.getErrorStream())); - - // read the output from the command - StringBuilder st = new StringBuilder(); - String s = null; - while ((s = stdInput.readLine()) != null) { - st.append(s); - //s = s.replace("Switch: " + deviceIp, ""); - //isOn = s.contains("on"); - } - logger.info("Output from device: "+st.toString()); - JSONObject json = new JSONObject(st.toString()); - for(Object keyObj : json.keySet()){ - String key = (String) keyObj; - String value = (String) json.get(key).toString(); - attributes.put(key, value); - } - // read any errors from the attempted command - while ((s = stdError.readLine()) != null) { - logger.severe(s); - } - } catch (IOException e) { - logger.severe("Error polling Wemo Insight: " + e.toString()); - } catch (JSONException e) { - logger.severe("Error parsing JSON respons from Wemo Insight: " + deviceId + ". " + e.getMessage()); - e.printStackTrace(); - } - - } - - @Override - public void saveCurrentState() { - logger.info("[WeMoInsightMonitor] Saving current state"); - DeviceStatus wemo = new DeviceStatus(deviceId, attributes); - sendToDeviceController(wemo); - logger.info("[WeMoInsightMonitor] State saved: "+wemo.toString()); - } -} diff --git a/src/main/java/edu/cmu/sei/kalki/commanders/DeviceCommandSender.java b/src/main/java/edu/cmu/sei/kalki/commanders/DeviceCommandSender.java deleted file mode 100644 index 1239a1e..0000000 --- a/src/main/java/edu/cmu/sei/kalki/commanders/DeviceCommandSender.java +++ /dev/null @@ -1,33 +0,0 @@ -package edu.cmu.sei.kalki.commanders; - -import edu.cmu.sei.ttg.kalki.models.Device; -import edu.cmu.sei.ttg.kalki.models.DeviceCommand; -import edu.cmu.sei.ttg.kalki.models.DeviceType; - -import java.util.List; -import java.util.logging.Logger; - -public abstract class DeviceCommandSender { - private static Logger logger = Logger.getLogger("iot-interface"); - - public static void sendCommands(Device device, List commands, String apiUrl){ - DeviceType deviceType = device.getType(); - switch (deviceType.getId()){ - case 1: // DLC - logger.severe("[DeviceCommandSender] Error: there are no commands for a DLink Camera"); - break; - case 2: // UNTS - logger.severe("[DeviceCommandSender] Error: there are no commands for a Udoo Neo with Temperature Sensor"); - break; - case 3: //WeMo - WemoCommandSender.sendCommands(device, commands, apiUrl); - break; - case 4: // PHLE - PhleCommandSender.sendCommands(device, commands, apiUrl); - break; - default: - logger.severe("[DeviceCommandSender] System not configured to send commands to type: " + deviceType.getName()); - } - } - -} diff --git a/src/main/java/edu/cmu/sei/kalki/commanders/PhleCommandSender.java b/src/main/java/edu/cmu/sei/kalki/commanders/PhleCommandSender.java deleted file mode 100644 index bc83d80..0000000 --- a/src/main/java/edu/cmu/sei/kalki/commanders/PhleCommandSender.java +++ /dev/null @@ -1,158 +0,0 @@ -package edu.cmu.sei.kalki.commanders; - -import com.philips.lighting.hue.listener.PHLightListener; -import edu.cmu.sei.kalki.utils.DeviceControllerApi; -import edu.cmu.sei.ttg.kalki.models.Device; -import edu.cmu.sei.ttg.kalki.models.DeviceCommand; - -import com.philips.lighting.hue.sdk.*; -import com.philips.lighting.model.*; -import edu.cmu.sei.ttg.kalki.models.StageLog; -import org.json.JSONObject; - -import java.io.OutputStreamWriter; -import java.net.URL; -import java.net.HttpURLConnection; -import java.util.List; -import java.util.Map; -import java.util.logging.Logger; - -public class PhleCommandSender { - private static PHHueSDK phHueSDK; - private static Logger logger = Logger.getLogger("iot-interface"); - - public static void sendCommands(Device device, List commands, String apiUrl){ - logger.info("[PhleCommandSender] Sending commands to PHLE: "+device.getId()); - connectToHue(device); - - PHBridge bridge = null; - for (int i=0; i < 10 && bridge == null; i++){ - logger.info("Waiting to discover PHLE bridge..."); - try {Thread.sleep(100);}catch (Exception e) { } - bridge = phHueSDK.getSelectedBridge(); - } - if(bridge == null){ - logger.severe("Unable to connect to PHLE bridge & send commands to device: "+device.getId()); - return; - } - PHBridgeResourcesCache cache = bridge.getResourceCache(); - List lights = cache.getAllLights(); - for(int i=0;i< lights.size();i++){ - for(DeviceCommand command: commands){ - switch (command.getName()){ - case "turn-on": - logger.info("[PhleCommandSender] Sending 'turn-on' command to PHLE: " + device.getId()); - sendIsOn(device.getIp(), i+1,"true"); - logSendCommand(device, command.getName(), apiUrl); - break; - case "turn-off": - logger.info("[PhleCommandSender] Sending 'turn-off' command to PHLE: " + device.getId()); - sendIsOn(device.getIp(), i+1,"false"); - logSendCommand(device, command.getName(), apiUrl); - break; - case "set-name": - case "set-brightness": - case "set-color": - case "set-schedule": - case "set-group": - case "set-scene": - default: - logger.severe("[PhleCommandSender] Command: " + command.getName() + " not supported for Phillips Hue Light Emulator."); - } - } - } - - } - - private static void logSendCommand(Device device, String command, String apiUrl) { - logger.info("[PhleCommandSender] Logging that a command was sent to the device."); - StageLog log = new StageLog(device.getCurrentState().getId(), StageLog.Action.SEND_COMMAND, StageLog.Stage.FINISH, "Sent command to device: "+command); - DeviceControllerApi.sendLog(log, apiUrl); - } - - private static void sendIsOn(String ip, int lightId, String isOn) { - try { - URL url = new URL("http://"+ip+"/api/newdeveloper/lights/"+lightId+"/state/"); - HttpURLConnection httpCon = (HttpURLConnection) url.openConnection(); - httpCon.setDoOutput(true); - httpCon.setRequestMethod("PUT"); - OutputStreamWriter out = new OutputStreamWriter(httpCon.getOutputStream()); - JSONObject json = new JSONObject("{\"on\":"+isOn+"}"); - out.write(json.toString()); - out.close(); - httpCon.getInputStream(); - } catch (Exception e) { - logger.severe("[PhleCommandSender] Error sending command to device!"); - logger.severe(e.getMessage()); - } - } - - private static void connectToHue(Device device) { - phHueSDK = PHHueSDK.getInstance(); - phHueSDK.getNotificationManager().registerSDKListener(listener); - - PHAccessPoint accessPoint = new PHAccessPoint(); - accessPoint.setIpAddress(device.getIp()); - accessPoint.setUsername("newdeveloper"); - phHueSDK.connect(accessPoint); - } - - private static PHSDKListener listener = new PHSDKListener() { - - @Override - public void onAccessPointsFound(List accessPointsList) { - logger.info("Found AccessPoints!"); - } - - @Override - public void onAuthenticationRequired(PHAccessPoint accessPoint) { - // Start the Pushlink Authentication. - logger.info("AuthenticationRequired"); - phHueSDK.startPushlinkAuthentication(accessPoint); - } - - @Override - public void onBridgeConnected(PHBridge bridge, String username) { - phHueSDK.setSelectedBridge(bridge); - logger.info("Connected to bridge"); -/* String lastIpAddress = bridge.getResourceCache().getBridgeConfiguration().getIpAddress(); - logger.info("IP is : " + lastIpAddress); - logger.info("Username is: " + username);*/ - } - - @Override - public void onCacheUpdated(List arg0, PHBridge arg1) { - } - - @Override - public void onConnectionLost(PHAccessPoint arg0) { - } - - @Override - public void onConnectionResumed(PHBridge arg0) { - } - - @Override - public void onError(int code, final String message) { - if (code == PHHueError.BRIDGE_NOT_RESPONDING) { - logger.severe("Bridge not responding: " + message); - } - else if (code == PHMessageType.PUSHLINK_BUTTON_NOT_PRESSED) { - logger.severe("Button not pressed: " + message); - } - else if (code == PHMessageType.PUSHLINK_AUTHENTICATION_FAILED) { - logger.severe("Authentication failed: " + message); - } - else if (code == PHMessageType.BRIDGE_NOT_FOUND) { - logger.severe("Bridge not found: " + message); - } - } - - @Override - public void onParsingErrors(List parsingErrorsList) { - for (PHHueParsingError parsingError: parsingErrorsList) { - logger.severe("ParsingError : " + parsingError.getMessage()); - } - } - }; -} diff --git a/src/main/java/edu/cmu/sei/kalki/commanders/WemoCommandSender.java b/src/main/java/edu/cmu/sei/kalki/commanders/WemoCommandSender.java deleted file mode 100644 index 777d3e5..0000000 --- a/src/main/java/edu/cmu/sei/kalki/commanders/WemoCommandSender.java +++ /dev/null @@ -1,74 +0,0 @@ -package edu.cmu.sei.kalki.commanders; - -import edu.cmu.sei.kalki.utils.DeviceControllerApi; -import edu.cmu.sei.ttg.kalki.models.Device; -import edu.cmu.sei.ttg.kalki.models.DeviceCommand; -import edu.cmu.sei.ttg.kalki.models.StageLog; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.List; -import java.util.logging.Logger; - -public class WemoCommandSender { - private static Logger logger = Logger.getLogger("iot-interface"); - private static String[] args = new String[]{ - "python", - "wemo.py", - "device", - "command" - }; - - public static void sendCommands(Device device, List commands, String apiUrl) { - logger.info("[WemoCommandSender] Sending commands to device: "+device.getId()); - - for(DeviceCommand command: commands) { - switch (command.getName()){ - case "turn-on": - case "turn-off": - sendCommand(device, command); - logSendCommand(device, command.getName(), apiUrl); - break; - default: - logger.severe("[WemoCommandSender] Command: " + command.getName() + " is not a valid command for a Wemo Insight"); - } - } - } - - private static void sendCommand(Device device, DeviceCommand command) { - args[2] = device.getIp(); - args[3] = command.getName(); - String s = null; - - try { - Process p = Runtime.getRuntime().exec(args); - - BufferedReader stdInput = new BufferedReader(new - InputStreamReader(p.getInputStream())); - - BufferedReader stdError = new BufferedReader(new - InputStreamReader(p.getErrorStream())); - - // read the output from the command - while ((s = stdInput.readLine()) != null) { - logger.info("[WemoCommandSender] " + s); - } - - // read any errors from the attempted command - while ((s = stdError.readLine()) != null) { - logger.severe("[WemoCommandSender] Error with wemo.py: "+s); - } - } catch (IOException e) { - logger.severe("[WemoCommandSender] Error reading response from " + device.getId() + ") " + device.getName()); - logger.severe(e.getMessage()); - } - } - - private static void logSendCommand(Device device, String command, String apiUrl) { - logger.info("[WemoCommandSender] Logging that a command was sent to the device."); - StageLog log = new StageLog(device.getCurrentState().getId(), StageLog.Action.SEND_COMMAND, StageLog.Stage.FINISH, "Sent command to device: "+command); - DeviceControllerApi.sendLog(log, apiUrl); - } - -} diff --git a/src/main/java/edu/cmu/sei/kalki/iotinterface/CommandManager.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/CommandManager.java new file mode 100644 index 0000000..c9da281 --- /dev/null +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/CommandManager.java @@ -0,0 +1,38 @@ +package edu.cmu.sei.kalki.iotinterface; + +import edu.cmu.sei.kalki.iotinterface.devicetypes.IotCommandSender; +import edu.cmu.sei.ttg.kalki.models.Device; +import edu.cmu.sei.ttg.kalki.models.DeviceCommand; + +import java.lang.reflect.Constructor; +import java.util.List; +import java.util.logging.Logger; + +public class CommandManager { + private static Logger logger = Logger.getLogger("iot-interface"); + + public CommandManager(){} + + /** + * Creates command sender from device's type and sends the list of commands + * @param device The device receiving commands + * @param commands The list of commands to send + */ + public static void processCommands(Device device, List commands){ + try { + // Remove white spaces from device type name + String deviceTypeName = device.getType().getName().replaceAll("\\s+",""); + + // Get command sender constructor via reflection + String classPath = "edu.cmu.sei.kalki.devicetypes."+deviceTypeName+".CommandSender"; + Constructor con = Class.forName(classPath).getConstructor(Device.class, List.class); + + // Create instance and send sommands + IotCommandSender commandSender = (IotCommandSender) con.newInstance(device, commands); + commandSender.sendCommands(); + } catch (Exception e) { // No command sender found for the given device type + logger.severe("[CommandManager] Error: there are no commands for a "+device.getType().getName()); + logger.severe("[CommandManager] Error: "+e); + } + } +} diff --git a/src/main/java/edu/cmu/sei/kalki/IotInterface.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/IotInterface.java similarity index 56% rename from src/main/java/edu/cmu/sei/kalki/IotInterface.java rename to src/main/java/edu/cmu/sei/kalki/iotinterface/IotInterface.java index 4a2e0a8..011fa01 100644 --- a/src/main/java/edu/cmu/sei/kalki/IotInterface.java +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/IotInterface.java @@ -1,28 +1,34 @@ -package edu.cmu.sei.kalki; -import edu.cmu.sei.kalki.api.*; +package edu.cmu.sei.kalki.iotinterface; +import edu.cmu.sei.kalki.iotinterface.api.*; +import edu.cmu.sei.kalki.iotinterface.utils.Config; + +import java.io.IOException; import java.util.logging.Logger; public class IotInterface { private static Logger logger = Logger.getLogger("iot-interface"); public static void main(String[] args) { - String apiUrl = "10.27.153.3:9090"; try { - apiUrl = args[0]; - } catch (ArrayIndexOutOfBoundsException e) { - logger.info("[IotInterface] No alternative API IP+port specified. Defaulting to: "+apiUrl); + Config.load("config.json"); + } catch (IOException e) { + logger.severe("[IotInterface] Error parsing the config.json. Exiting."); + e.printStackTrace(); + System.exit(-1); } - DeviceMonitor monitor = new DeviceMonitor("http://"+apiUrl+"/device-controller-api/"); - logger.info("[IotInterface] DeviceMonitor initialized."); + MonitorManager monitor = new MonitorManager(); + logger.info("[IotInterface] MonitorManager initialized."); + + if(!startApiServer(monitor)){ + logger.info("[IotInterface] APIServerStartup failed. Exiting."); + System.exit(-1); + } - boolean success = startApiServer(monitor); - if(!success) - return; } - private static boolean startApiServer(DeviceMonitor monitor) { + private static boolean startApiServer(MonitorManager monitor) { boolean success = false; int attempts = 0; diff --git a/src/main/java/edu/cmu/sei/kalki/iotinterface/MonitorManager.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/MonitorManager.java new file mode 100644 index 0000000..b9298de --- /dev/null +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/MonitorManager.java @@ -0,0 +1,105 @@ +package edu.cmu.sei.kalki.iotinterface; + +import edu.cmu.sei.kalki.iotinterface.devicetypes.PollingMonitor; +import edu.cmu.sei.kalki.iotinterface.utils.DeviceControllerApi; +import edu.cmu.sei.ttg.kalki.models.Device; +import edu.cmu.sei.kalki.iotinterface.devicetypes.IotMonitor; +import edu.cmu.sei.ttg.kalki.models.StageLog; + +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.logging.Logger; + +public class MonitorManager { + private Logger logger = Logger.getLogger("iot-interface"); + private HashMap monitors; + private static final String className = "[MonitorManager]"; + + public MonitorManager() { + monitors = new HashMap<>(); + } + + /** + * Starts a new monitor for the given device + * @param device + */ + public void startMonitor(Device device) { + if(device.getSamplingRate() == 0){ + logger.info(className + " Sampling rate of 0. Not starting monitor."); + logUpdateMonitor(device, "0 sampling rate"); + } + else { + logger.info(className + " Starting monitor for device: "+device.getId()); + IotMonitor mon = fromDevice(device); + monitors.put(device.getId(), mon); + logUpdateMonitor(device, "Monitor started"); + + } + } + + /** + * Updates the sampling rate for the given device + * @param device + */ + public void updateMonitor(Device device) { + IotMonitor mon = monitors.get(device.getId()); + if(mon != null){ // monitor exists + + if(!mon.isPollable()){ // monitor doesn't have a sampling rate + logger.info((className + "Monitor is not pollable, no sampling rate to update")); + return; + } + + PollingMonitor pollMon = (PollingMonitor) mon; + if(pollMon.getPollInterval() != device.getSamplingRate()) { // the sampling rate has been updated + logger.info(className + " Updating monitor for device: "+device.getId()); + pollMon.setPollInterval(device.getSamplingRate()); + monitors.replace(device.getId(), pollMon); + } else { + logger.info(className + " Not updating monitor for device: "+device.getId()+". Sampling rate hasn't changed."); + } + + } else { + logger.info(className + " No monitor found for given device "+device.getId()+". Starting one..."); + startMonitor(device); + } + } + + /** + * Creates an instance of an IotMonitor for the given device's type + * @param device The device to be monitored + * @return The instance of the device's monitor + */ + public static IotMonitor fromDevice(Device device){ + Logger logger = Logger.getLogger("iot-interface"); + try { + // Remove white spaces from device type name + String deviceTypeName = device.getType().getName().replaceAll("\\s+",""); + + // Get IotMonitor constructor via reflection + String classPath = "edu.cmu.sei.kalki.devicetypes."+deviceTypeName+".Monitor"; + Constructor con = Class.forName(classPath).getConstructor(Integer.TYPE, String.class, Integer.TYPE); + + // Create and return instance of specific IotMonitor + IotMonitor mon = (IotMonitor) con.newInstance(device.getId(), device.getIp(), device.getSamplingRate()); + return mon; + } catch (Exception e2){ + e2.printStackTrace(); + logger.info(e2.getMessage()); + + return null; + } + } + + /** + * Sends a StageLog to the DeviceControllerApi to record updating a sampling rate + * @param device Device the monitor was updated for + */ + private void logUpdateMonitor(Device device, String info) { + logger.info( className + " Logging monitor update for device: "+device.getId()); + StageLog log = new StageLog(device.getCurrentState().getId(), StageLog.Action.INCREASE_SAMPLE_RATE, StageLog.Stage.FINISH, info); + DeviceControllerApi.sendLog(log); + } + + +} diff --git a/src/main/java/edu/cmu/sei/kalki/api/ApiServerStartup.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/api/ApiServerStartup.java similarity index 77% rename from src/main/java/edu/cmu/sei/kalki/api/ApiServerStartup.java rename to src/main/java/edu/cmu/sei/kalki/iotinterface/api/ApiServerStartup.java index 703f70c..d459c92 100644 --- a/src/main/java/edu/cmu/sei/kalki/api/ApiServerStartup.java +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/api/ApiServerStartup.java @@ -1,6 +1,7 @@ -package edu.cmu.sei.kalki.api; +package edu.cmu.sei.kalki.iotinterface.api; -import edu.cmu.sei.kalki.DeviceMonitor; +import edu.cmu.sei.kalki.iotinterface.MonitorManager; +import edu.cmu.sei.kalki.iotinterface.utils.Config; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import java.util.logging.Logger; @@ -8,12 +9,12 @@ public class ApiServerStartup { private static Logger logger = Logger.getLogger("iot-interface"); private static final String API_URL = "/iot-interface-api"; - private static final int SERVER_PORT = 5050; + private static final int SERVER_PORT = Integer.parseInt(Config.data.get("iot_interface_api_port")); /** * Starts a Jetty server, with handler for notifications */ - public static void start(DeviceMonitor monitor) throws Exception { + public static void start(MonitorManager monitor) throws Exception { try { Server httpServer = new Server(SERVER_PORT); ServletContextHandler handler = new ServletContextHandler(httpServer, API_URL); diff --git a/src/main/java/edu/cmu/sei/kalki/api/ApiServlet.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/api/ApiServlet.java similarity index 79% rename from src/main/java/edu/cmu/sei/kalki/api/ApiServlet.java rename to src/main/java/edu/cmu/sei/kalki/iotinterface/api/ApiServlet.java index 6478104..87fca5b 100644 --- a/src/main/java/edu/cmu/sei/kalki/api/ApiServlet.java +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/api/ApiServlet.java @@ -1,7 +1,6 @@ -package edu.cmu.sei.kalki.api; +package edu.cmu.sei.kalki.iotinterface.api; import edu.cmu.sei.ttg.kalki.models.*; -import org.eclipse.jetty.http.HttpStatus; import org.json.JSONException; import org.json.JSONObject; @@ -13,8 +12,18 @@ import java.io.IOException; import java.sql.Timestamp; +/** + * Base class for ApiServlets to handle requests from the DeviceController + */ public class ApiServlet extends HttpServlet { + /** + * Method to extract body of an HTTP request and convert it to a JSON object + * @param request + * @param response + * @return + * @throws ServletException + */ protected JSONObject parseRequestBody(HttpServletRequest request, HttpServletResponse response) throws ServletException { JSONObject requestBody; try { @@ -36,6 +45,12 @@ protected JSONObject parseRequestBody(HttpServletRequest request, HttpServletRes return requestBody; } + /** + * Method to convert a JSONObject to a Device + * @param deviceData + * @return + * @throws JSONException + */ protected Device parseDevice(JSONObject deviceData) throws JSONException { int id = deviceData.getInt("id"); String name = deviceData.getString("name"); @@ -53,18 +68,33 @@ protected Device parseDevice(JSONObject deviceData) throws JSONException { return device; } + /** + * Method to convert a JSONObject to a DeviceType + * @param type + * @return + */ protected DeviceType parseDeviceType(JSONObject type) { int id = type.getInt("id"); String name = type.getString("name"); return new DeviceType(id, name); } + /** + * Method to convert a JSONObject to a Group + * @param group + * @return + */ protected Group parseGroup(JSONObject group) { int id = group.getInt("id"); String name = group.getString("name"); return new Group(id, name); } + /** + * Method to convert a JSONObject to a DeviceSecurityState + * @param state + * @return + */ protected DeviceSecurityState parseSecurityState(JSONObject state) { int id = state.getInt("id"); int deviceId = state.getInt("deviceId"); @@ -74,6 +104,11 @@ protected DeviceSecurityState parseSecurityState(JSONObject state) { return new DeviceSecurityState(id, deviceId, stateId, timestamp, name); } + /** + * Method to convert a JSONObject to an Alert + * @param alert + * @return + */ protected Alert parseAlert(JSONObject alert) { int id = alert.getInt("id"); String name = alert.getString("name"); @@ -82,6 +117,7 @@ protected Alert parseAlert(JSONObject alert) { int deviceId = alert.getInt("deviceId"); Integer deviceStatusId = alert.getInt("deviceStatusId"); int alertTypeId = alert.getInt("alertTypeId"); - return new Alert(id, name, timestamp, alerterId, deviceId, deviceStatusId, alertTypeId); + String info = alert.getString("info"); + return new Alert(id, name, timestamp, alerterId, deviceId, deviceStatusId, alertTypeId, info); } } diff --git a/src/main/java/edu/cmu/sei/kalki/api/NewDeviceServlet.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/api/NewDeviceServlet.java similarity index 76% rename from src/main/java/edu/cmu/sei/kalki/api/NewDeviceServlet.java rename to src/main/java/edu/cmu/sei/kalki/iotinterface/api/NewDeviceServlet.java index 0af0a86..01a2bc2 100644 --- a/src/main/java/edu/cmu/sei/kalki/api/NewDeviceServlet.java +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/api/NewDeviceServlet.java @@ -1,10 +1,10 @@ -package edu.cmu.sei.kalki.api; +package edu.cmu.sei.kalki.iotinterface.api; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import edu.cmu.sei.kalki.DeviceMonitor; +import edu.cmu.sei.kalki.iotinterface.MonitorManager; import edu.cmu.sei.ttg.kalki.models.*; import org.eclipse.jetty.http.HttpStatus; @@ -16,6 +16,12 @@ public class NewDeviceServlet extends ApiServlet { private Logger logger = Logger.getLogger("iot-interface"); + /** + * Extracts the Device from the request body and starts a monitor for it + * @param request + * @param response + * @throws ServletException + */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException { logger.info("[NewDeviceServlet] Handling request."); @@ -35,7 +41,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) logger.info("[NewDeviceServlet] Calling startMonitor for device: "+device.toString()); response.setStatus(HttpStatus.OK_200); - DeviceMonitor monitor = (DeviceMonitor) getServletContext().getAttribute("monitor"); + MonitorManager monitor = (MonitorManager) getServletContext().getAttribute("monitor"); monitor.startMonitor(device); } diff --git a/src/main/java/edu/cmu/sei/kalki/api/SendCommandServlet.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/api/SendCommandServlet.java similarity index 87% rename from src/main/java/edu/cmu/sei/kalki/api/SendCommandServlet.java rename to src/main/java/edu/cmu/sei/kalki/iotinterface/api/SendCommandServlet.java index 94e84d5..fdfb655 100644 --- a/src/main/java/edu/cmu/sei/kalki/api/SendCommandServlet.java +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/api/SendCommandServlet.java @@ -1,11 +1,10 @@ -package edu.cmu.sei.kalki.api; +package edu.cmu.sei.kalki.iotinterface.api; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import edu.cmu.sei.kalki.DeviceMonitor; -import edu.cmu.sei.kalki.commanders.DeviceCommandSender; +import edu.cmu.sei.kalki.iotinterface.CommandManager; import edu.cmu.sei.ttg.kalki.models.Device; import edu.cmu.sei.ttg.kalki.models.DeviceCommand; import org.eclipse.jetty.http.HttpStatus; @@ -21,6 +20,12 @@ public class SendCommandServlet extends ApiServlet { private Logger logger = Logger.getLogger("iot-interface"); + /** + * Extracts the device and commands from the request and initiates command sending + * @param request + * @param response + * @throws ServletException + */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException { logger.info("[SendCommandServlet] Handling request"); @@ -49,8 +54,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) response.setStatus(HttpStatus.OK_200); logger.info("[SendCommandServlet] Sending commands to device: "+device.toString()); - DeviceMonitor monitor = (DeviceMonitor) getServletContext().getAttribute("monitor"); - DeviceCommandSender.sendCommands(device, commandList, monitor.getApiUrl()); + CommandManager.processCommands(device, commandList); } private List parseCommandList(JSONArray commandList) { diff --git a/src/main/java/edu/cmu/sei/kalki/api/UpdateDeviceServlet.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/api/UpdateDeviceServlet.java similarity index 76% rename from src/main/java/edu/cmu/sei/kalki/api/UpdateDeviceServlet.java rename to src/main/java/edu/cmu/sei/kalki/iotinterface/api/UpdateDeviceServlet.java index deab652..36c7d65 100644 --- a/src/main/java/edu/cmu/sei/kalki/api/UpdateDeviceServlet.java +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/api/UpdateDeviceServlet.java @@ -1,10 +1,10 @@ -package edu.cmu.sei.kalki.api; +package edu.cmu.sei.kalki.iotinterface.api; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import edu.cmu.sei.kalki.DeviceMonitor; +import edu.cmu.sei.kalki.iotinterface.MonitorManager; import edu.cmu.sei.ttg.kalki.models.Device; import org.eclipse.jetty.http.HttpStatus; @@ -16,6 +16,12 @@ public class UpdateDeviceServlet extends ApiServlet { private Logger logger = Logger.getLogger("iot-interface"); + /** + * Extracts the device from the request and updates its monitor + * @param request + * @param response + * @throws ServletException + */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException { logger.info("[UpdateDeviceServlet] Handling request."); @@ -34,7 +40,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) logger.info("[UpdateDeviceServlet] Updating monitor for device: "+device.getId()); response.setStatus(HttpStatus.OK_200); - DeviceMonitor monitor = (DeviceMonitor) getServletContext().getAttribute("monitor"); + MonitorManager monitor = (MonitorManager) getServletContext().getAttribute("monitor"); monitor.updateMonitor(device); } } diff --git a/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/DLinkCamera/Monitor.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/DLinkCamera/Monitor.java new file mode 100644 index 0000000..2ec1935 --- /dev/null +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/DLinkCamera/Monitor.java @@ -0,0 +1,34 @@ +package edu.cmu.sei.kalki.iotinterface.devicetypes.DLinkCamera; + +import edu.cmu.sei.kalki.iotinterface.utils.Config; +import edu.cmu.sei.kalki.iotinterface.utils.mail.EventObserver; +import edu.cmu.sei.kalki.iotinterface.utils.mail.MailServer; +import edu.cmu.sei.kalki.iotinterface.devicetypes.IotMonitor; +import edu.cmu.sei.ttg.kalki.models.DeviceStatus; + +public class Monitor extends IotMonitor implements EventObserver { + + private final String listenEmail = Config.data.get("dlink_notification_email"); + private static final String logId = "[DLinkCameraMonitor]"; + + public Monitor(int deviceId, String ip, int samplingRate){ + super(deviceId, ip, false); + MailServer.initialize(); + MailServer.registerObserver(this); + logger.info(logId + " Monitor started for device: "+deviceId); + } + + /** + * The camera is configured to send a notification to listenEmail on motion detected. + * This method sends a status to the DeviceController on an appropriate message + * @param message The notification from the camera + */ + public void notify(String message){ + if (message.equals(listenEmail)){ + DeviceStatus status = new DeviceStatus(deviceId); + status.addAttribute("motion_detected", "true"); + sendToDeviceController(status); + } + } + +} diff --git a/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/IotCommandSender.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/IotCommandSender.java new file mode 100644 index 0000000..f7c4915 --- /dev/null +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/IotCommandSender.java @@ -0,0 +1,43 @@ +package edu.cmu.sei.kalki.iotinterface.devicetypes; + +import edu.cmu.sei.kalki.iotinterface.utils.DeviceControllerApi; +import edu.cmu.sei.ttg.kalki.models.Device; +import edu.cmu.sei.ttg.kalki.models.DeviceCommand; +import edu.cmu.sei.ttg.kalki.models.StageLog; + +import java.util.List; +import java.util.logging.Logger; + +public abstract class IotCommandSender { + private static Logger logger = Logger.getLogger("iot-interface"); + + protected Device device; + protected List commands; + + + public IotCommandSender(Device device, List commands){ + this.device = device; + this.commands = commands; + } + + public void sendCommands() { + for(DeviceCommand command: commands) { + sendCommand(command); + } + } + + /** + * Method to be overwritten by child class. Check if it's a valid command, then send accordingly + */ + protected abstract void sendCommand(DeviceCommand command); + + /** + * Sends StageLog to Device Controller indicating a command was sent to the device + * @param command The name of the command + */ + protected void logSendCommand(String command) { + logger.info("[IotCommandSender] Logging that a command was sent to the device."); + StageLog log = new StageLog(device.getCurrentState().getId(), StageLog.Action.SEND_COMMAND, StageLog.Stage.FINISH, "Sent command to device: "+command); + DeviceControllerApi.sendLog(log); + } +} diff --git a/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/IotMonitor.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/IotMonitor.java new file mode 100644 index 0000000..c5b59c1 --- /dev/null +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/IotMonitor.java @@ -0,0 +1,28 @@ +package edu.cmu.sei.kalki.iotinterface.devicetypes; + +import edu.cmu.sei.kalki.iotinterface.utils.DeviceControllerApi; +import edu.cmu.sei.ttg.kalki.models.*; + +import java.util.logging.Logger; + +public abstract class IotMonitor { + protected int deviceId; + protected String deviceIp; + protected boolean isPollable; + + protected final Logger logger = Logger.getLogger("iot-interface"); + + public IotMonitor(int deviceId, String deviceIp, boolean isPollable){ + this.deviceId = deviceId; + this.deviceIp = deviceIp; + this.isPollable = isPollable; + } + + public boolean isPollable() { + return isPollable; + } + + protected void sendToDeviceController(DeviceStatus status) { + DeviceControllerApi.sendStatus(status); + } +} diff --git a/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/PhilipsHueLightEmulator/CommandSender.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/PhilipsHueLightEmulator/CommandSender.java new file mode 100644 index 0000000..25abea5 --- /dev/null +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/PhilipsHueLightEmulator/CommandSender.java @@ -0,0 +1,94 @@ +package edu.cmu.sei.kalki.iotinterface.devicetypes.PhilipsHueLightEmulator; + +import edu.cmu.sei.kalki.iotinterface.utils.HttpRequest; +import edu.cmu.sei.kalki.iotinterface.devicetypes.IotCommandSender; +import edu.cmu.sei.ttg.kalki.models.Device; +import edu.cmu.sei.ttg.kalki.models.DeviceCommand; +import org.json.JSONObject; + +import java.util.Iterator; +import java.util.List; +import java.util.logging.Logger; + +public class CommandSender extends IotCommandSender { + private static Logger logger = Logger.getLogger("iot-interface"); + private static final String logId = "[PhleCommandSender]"; + private static final String phleBasePath = "/api/newdeveloper/lights/"; + private JSONObject lights; + + public CommandSender(Device device, List commands) { + super(device, commands); + lights = getAllLights(device.getIp()); + } + + /** + * Sends commands to each light associated with the given PHLE bridge + * @param command Currently supports turn-on & turn-off + */ + @Override + protected void sendCommand(DeviceCommand command) { + logger.info(logId + " Sending commands to PHLE: "+device.getId()); + + if(lights== null){ + logger.severe(logId + " Unable to get lights from bridge"); + return; + } + Iterator lightIds = lights.keys(); + while(lightIds.hasNext()){ + int id = Integer.parseInt(lightIds.next()); + switch (command.getName()){ + case "turn-on": + logger.info(logId + " Sending 'turn-on' command to PHLE: " + device.getId()); + sendIsOn(device.getIp(), id,"true"); + logSendCommand(command.getName()); + break; + case "turn-off": + logger.info(logId + " Sending 'turn-off' command to PHLE: " + device.getId()); + sendIsOn(device.getIp(), id,"false"); + logSendCommand(command.getName()); + break; + case "set-name": + case "set-brightness": + case "set-color": + case "set-schedule": + case "set-group": + case "set-scene": + default: + logger.severe(logId + " Command: " + command.getName() + " not supported for Phillips Hue Light Emulator."); + } + } + } + + /** + * Sets the light's state 'isOn' property + * @param ip The ip of the bridge + * @param lightId The id of the light on the bridge + * @param isOn String value of a boolean determining 'isOn' + */ + private static void sendIsOn(String ip, int lightId, String isOn) { + try { + JSONObject body = new JSONObject("{\"on\":"+isOn+"}"); + String apiUrl = "http://"+ip+ phleBasePath +lightId+"/state"; + HttpRequest.putRequest(body, apiUrl); + } catch (Exception e) { + logger.severe(logId + " Error sending command to device!"); + logger.severe(e.getMessage()); + } + } + + /** + * Get all the lights associated with the PHLE bridge + * @param ip The ip of the PHLE bridge + * @return JSON object representing all lights connected to the bridge + */ + private static JSONObject getAllLights(String ip) { + JSONObject json = null; + try { + json = HttpRequest.getRequest("http://"+ip+phleBasePath); + } catch (Exception e) { + logger.severe(logId + " Error getting all lights."); + logger.severe(e.getMessage()); + } + return json; + } +} diff --git a/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/PhilipsHueLightEmulator/Monitor.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/PhilipsHueLightEmulator/Monitor.java new file mode 100644 index 0000000..8ddabe2 --- /dev/null +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/PhilipsHueLightEmulator/Monitor.java @@ -0,0 +1,66 @@ +package edu.cmu.sei.kalki.iotinterface.devicetypes.PhilipsHueLightEmulator; + +import edu.cmu.sei.kalki.iotinterface.utils.HttpRequest; +import edu.cmu.sei.kalki.iotinterface.devicetypes.PollingMonitor; +import edu.cmu.sei.ttg.kalki.models.DeviceStatus; + +import java.util.Set; + +import org.json.JSONException; +import org.json.JSONObject; + +public class Monitor extends PollingMonitor { + private static final String logId = "[PhleMonitor]"; + private String authCode = "newdeveloper"; //Default username works for most GET operations + + public Monitor(int deviceId, String ip, int samplingRate){ + super(deviceId, ip, true, samplingRate); + logger.info(logId + " Starting monitor."); + start(); + } + + + /** + * Makes a get request to the PHLE REST API at the given path + * @param path The endpoint of the api + * @return String representation of the response from the API + */ + public JSONObject issueCommand(String path){ + String targetURL = "http://" + deviceIp + "/api/" + authCode + "/" + path; + try{ + return HttpRequest.getRequest(targetURL); + } catch (Exception e) { + logger.severe(logId + " Error getting a response from the bridge: "+ e); + return new JSONObject("{\"error\": \"Error\"}"); + } + } + + /** + * Gets the state of the lights from the PHLE bridge + * @param status The DeviceStatus to be inserted + */ + @Override + public void pollDevice(DeviceStatus status) { + JSONObject json = issueCommand("lights"); + try { + Set keys = json.keySet(); + + String key = keys.iterator().next(); //Assumes only one light is connected, does not verify + status.addAttribute("lightId", key); + JSONObject lightJson = json.getJSONObject(key); + String name = lightJson.getString("name"); + JSONObject state = lightJson.getJSONObject("state"); + String brightness = Integer.toString(state.getInt("bri")); + String hue = Integer.toString(state.getInt("hue")); + String isOn = Boolean.toString(state.getBoolean("on")); + status.addAttribute("hue", hue); + status.addAttribute("isOn", isOn); + status.addAttribute("brightness", brightness); + status.addAttribute("name", name); + logger.info(logId + " Successfully polled device."); + } catch (JSONException err){ + logger.severe(logId + " Error: " + err.toString()); + } + } + +} diff --git a/src/main/java/edu/cmu/sei/kalki/Monitors/PollingMonitor.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/PollingMonitor.java similarity index 52% rename from src/main/java/edu/cmu/sei/kalki/Monitors/PollingMonitor.java rename to src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/PollingMonitor.java index 0b9bd6b..7bb2793 100644 --- a/src/main/java/edu/cmu/sei/kalki/Monitors/PollingMonitor.java +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/PollingMonitor.java @@ -1,19 +1,34 @@ -package edu.cmu.sei.kalki.Monitors; +package edu.cmu.sei.kalki.iotinterface.devicetypes; + +import edu.cmu.sei.ttg.kalki.models.DeviceStatus; import java.util.Timer; import java.util.TimerTask; public abstract class PollingMonitor extends IotMonitor { + protected int pollInterval; + protected Timer pollTimer; + private boolean timerGoing; + + public PollingMonitor(int deviceId, String deviceIp, boolean isPollable, int pollInterval) { + super(deviceId, deviceIp, isPollable); + this.pollInterval = pollInterval; + this.pollTimer = new Timer(); + this.timerGoing = false; + } /** - * Polls the device for updates. + * Polls the device for updates. Adds all device attributes to status. */ - public abstract void pollDevice(); + public abstract void pollDevice(DeviceStatus Status); /** * Saves the current state of the iot device to the database */ - public abstract void saveCurrentState(); + public void saveCurrentState(DeviceStatus status){ + sendToDeviceController(status); + logger.info("Sent status to device controller:" + status.toString()); + } /** * Connect to the device and begin monitoring. @@ -30,7 +45,7 @@ public void start(){ */ protected void startPolling() { pollTimer = new Timer(); - pollTimer.schedule(new PollTask(), pollInterval, pollInterval); + pollTimer.schedule(new PollTask(deviceId), pollInterval, pollInterval); timerGoing = true; } @@ -49,9 +64,16 @@ protected void stopPolling() { * Started from startPolling */ class PollTask extends TimerTask { + private int deviceId; + + public PollTask(int deviceId){ + this.deviceId = deviceId; + } + public void run() { - pollDevice(); - saveCurrentState(); + DeviceStatus status = new DeviceStatus(this.deviceId); + pollDevice(status); // pollDevice adds attributes to currentStatus + saveCurrentState(status); } } @@ -59,10 +81,14 @@ public void run() { * Sets the interval for polling the device for updates. * @param newInterval new interval, in milliseconds. */ - @Override public void setPollInterval(int newInterval) { pollInterval = newInterval; stopPolling(); startPolling(); } + + public int getPollInterval() { + return pollInterval; + } + } diff --git a/src/main/java/edu/cmu/sei/kalki/Monitors/UdooNeoMonitor.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/UdooNeo/Monitor.java similarity index 66% rename from src/main/java/edu/cmu/sei/kalki/Monitors/UdooNeoMonitor.java rename to src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/UdooNeo/Monitor.java index 8108f8c..9ed159b 100644 --- a/src/main/java/edu/cmu/sei/kalki/Monitors/UdooNeoMonitor.java +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/UdooNeo/Monitor.java @@ -1,5 +1,6 @@ -package edu.cmu.sei.kalki.Monitors; +package edu.cmu.sei.kalki.iotinterface.devicetypes.UdooNeo; +import edu.cmu.sei.kalki.iotinterface.devicetypes.PollingMonitor; import edu.cmu.sei.ttg.kalki.models.DeviceStatus; import java.io.IOException; @@ -17,43 +18,36 @@ import com.jcraft.jsch.Session; import com.jcraft.jsch.JSchException; -public class UdooNeoMonitor extends PollingMonitor { - +public class Monitor extends PollingMonitor { + private static final String logId = "[UdooNeoMonitor]"; private List sensors = new ArrayList(); private String username; private String password; - private String ip; - private int deviceId; - private DeviceStatus status; - - private Map attributes = new HashMap(); - public UdooNeoMonitor(int deviceId, String ip, String username, String password, int samplingRate, String url){ - this(deviceId, ip, samplingRate, url); + public Monitor(int deviceId, String ip, int samplingRate, String username, String password){ + super(deviceId, ip, true, samplingRate); this.username = username; this.password = password; - this.isPollable = true; - } - - public UdooNeoMonitor(int deviceId, String ip, int samplingRate, String url){ - super(); - logger.info("[UdooNeoMonitor] Starting Neo Monitor for device: " + deviceId); - this.ip = ip; - this.deviceId = deviceId; - this.username = "udooer"; - this.password = "udooer"; - this.pollInterval = samplingRate; - this.isPollable = true; - this.apiUrl = url; + logger.info(logId + " Starting Neo Monitor for device: " + deviceId); setSensors(); start(); } + public Monitor(int deviceId, String ip, int samplingRate){ + this(deviceId, ip, samplingRate, "udooer", "udooer"); + } + + /** + * Abstract class for the sensors on an Udoo Neo + */ public abstract class NeoSensor{ public abstract String getCommand(); public abstract Map parseResponse(List response); } + /** + * Class to interface with an Udoo Sensor with X,Y,Z values + */ public class NeoXYZSensor extends NeoSensor { private String name; @@ -71,7 +65,7 @@ public String getCommand(){ public Map parseResponse(List responses) { Map result = new HashMap(); if (responses.size() < 1){ - logger.severe("[UdooNeoMonitor] Missing response from Udoo Neo."); + logger.severe(logId + " Missing response from Udoo Neo."); } else { String response = responses.get(0); @@ -85,6 +79,9 @@ public Map parseResponse(List responses) { } } + /** + * Class to interface with an Udoo Temperature Sensor + */ public class TemperatureSensor extends NeoSensor { List fields = new ArrayList(); @@ -117,6 +114,9 @@ public Map parseResponse(List responses) { } } + /** + * Sets the sensors that are available on the Udoo Neo + */ public void setSensors(){ sensors.add(new NeoXYZSensor("accelerometer")); sensors.add(new NeoXYZSensor("gyroscope")); @@ -124,10 +124,16 @@ public void setSensors(){ sensors.add(new TemperatureSensor()); } + /** + * Connects to device, gets raw sensor readings, and converts to human-readable values + * @param status The DeviceStatus to be sent to the DeviceControllerApi + */ @Override - public void pollDevice() { + public void pollDevice(DeviceStatus status) { try { + Map attributes = new HashMap(); + String command = ""; for(NeoSensor sensor: sensors){ command += sensor.getCommand(); @@ -136,7 +142,7 @@ public void pollDevice() { Properties config = new Properties(); config.put("StrictHostKeyChecking", "no"); JSch jsch = new JSch(); - Session session = jsch.getSession(username, ip, 22); + Session session = jsch.getSession(username, deviceIp, 22); session.setPassword(password); session.setConfig(config); session.connect(); @@ -149,7 +155,7 @@ public void pollDevice() { channel.connect(); InputStream inStream = channel.getInputStream(); - logger.info("[UdooNeoMonitor] Sent commands to device"); + logger.info(logId + " Sent commands to poll device"); List lines = new ArrayList(); int c = inStream.read(); @@ -167,43 +173,53 @@ public void pollDevice() { channel.disconnect(); session.disconnect(); - Map results = new HashMap(); for(NeoSensor sensor: sensors){ - results.putAll(sensor.parseResponse(lines)); + attributes.putAll(sensor.parseResponse(lines)); + } + convertRawReadings(attributes); + for (String key : attributes.keySet()){ + status.addAttribute(key, attributes.get(key)); } - - attributes = results; - convertRawReadings(); } catch (JSchException e1){ - logger.severe("[UdooNeoMonitor] Exception happened - here's what I know: "); + logger.severe(logId + " Exception happened - here's what I know: "); logger.severe(e1.getMessage()); } catch (IOException e) { - logger.severe("[UdooNeoMonitor] Exception happened - here's what I know: "); + logger.severe(logId + " Exception happened - here's what I know: "); logger.severe(e.getMessage()); } return; } - public void convertRawReadings(){ + /** + * Helper method to convert the raw readings of sensors to human-readable values + * @param attributes + */ + public void convertRawReadings(Map attributes){ //convert accelerometer readings to g's double accelCoefficient = 0.000244 / 4; - convertThreeAxisReading("accelerometer", accelCoefficient); + convertThreeAxisReading("accelerometer", accelCoefficient, attributes); //convert gyroscope readings to degrees/second double gyroCoefficient = 0.0625; - convertThreeAxisReading("gyroscope", gyroCoefficient); + convertThreeAxisReading("gyroscope", gyroCoefficient, attributes); //convert magnetometer readings to micro Teslas double magCoefficient = 0.1; - convertThreeAxisReading("magnetometer", magCoefficient); + convertThreeAxisReading("magnetometer", magCoefficient, attributes); //convert temperature readings to celsius double tempCoefficient = 1/1000; - convertTempReading("input", tempCoefficient); - convertTempReading("max", tempCoefficient); - convertTempReading("max_hyst", tempCoefficient); + convertTempReading("input", tempCoefficient, attributes); + convertTempReading("max", tempCoefficient, attributes); + convertTempReading("max_hyst", tempCoefficient, attributes); } - private void convertThreeAxisReading(String sensor, double coefficient){ + /** + * Helper method to replace X,Y,Z values with converted value + * @param sensor The sensor with X,Y,Z values + * @param coefficient The conversion rate + * @param attributes The device status attributes(sensor + X,Y, or Z) + */ + private void convertThreeAxisReading(String sensor, double coefficient, Map attributes){ double xReading = Double.valueOf(attributes.get(sensor+"X")) * coefficient; double yReading = Double.valueOf(attributes.get(sensor+"Y")) * coefficient; double zReading = Double.valueOf(attributes.get(sensor+"Z")) * coefficient; @@ -212,15 +228,15 @@ private void convertThreeAxisReading(String sensor, double coefficient){ attributes.replace(sensor+"Z", String.valueOf(zReading)); } - private void convertTempReading(String suffix, double coefficient) { + /** + * Helper method to replace temperature value with converted value + * @param suffix The different temperature value + * @param coefficient The conversion rate + * @param attributes The device status attributes(sensor + suffix) + */ + private void convertTempReading(String suffix, double coefficient, Map attributes) { double reading = Double.valueOf(attributes.get("temp"+suffix)) * coefficient; attributes.replace("temp"+suffix, String.valueOf(reading)); } - @Override - public void saveCurrentState() { - logger.info("[UdooNeoMonitor] Saving current state"); - status = new DeviceStatus(deviceId, attributes); - sendToDeviceController(status); - } } diff --git a/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/WeMoInsight/CommandSender.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/WeMoInsight/CommandSender.java new file mode 100644 index 0000000..c674345 --- /dev/null +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/WeMoInsight/CommandSender.java @@ -0,0 +1,93 @@ +package edu.cmu.sei.kalki.iotinterface.devicetypes.WeMoInsight; + +import edu.cmu.sei.kalki.iotinterface.devicetypes.IotCommandSender; +import edu.cmu.sei.ttg.kalki.models.Device; +import edu.cmu.sei.ttg.kalki.models.DeviceCommand; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.logging.Logger; +import java.util.List; + +public class CommandSender extends IotCommandSender { + private static Logger logger = Logger.getLogger("iot-interface"); + private static final String logId = "[WemoCommandSender]"; + public CommandSender(Device device, List commands) { + super(device, commands); + } + + + @Override + protected void sendCommand(DeviceCommand command) { + switch (command.getName()){ + case "turn-on": + case "turn-off": + executeScript(command.getName()); + break; + default: + logger.severe(logId + " Command: " + command.getName() + " is not a valid command for a Wemo Insight"); + return; + } + } + + /** + * Configures command line arguments to execute the wemo python script + * @param deviceIp The ip of the WemoInsight device + * @param command The command to be sent. Options: turn-off, turn-on, status (not applicable here) + * @return Array of command line args to execute the script + */ + private String[] setArgs(String deviceIp, String command){ + return new String[]{ + "python", + "wemo.py", + deviceIp, + command + }; + } + + /** + * Executes the wemo python script and process output + * @param command The string that is the command's name + */ + private void executeScript(String command){ + try { + String s = ""; + String[] args = setArgs(device.getIp(), command); + Process p = Runtime.getRuntime().exec(args); + + logger.info(logId + " Python script executed"); + + BufferedReader stdInput = new BufferedReader(new + InputStreamReader(p.getInputStream())); + + logger.info(logId + " Input stream captured"); + + BufferedReader stdError = new BufferedReader(new + InputStreamReader(p.getErrorStream())); + + logger.info(logId + " Error stream captured"); + + logger.info(logId + " Processing input stream"); + // read the output from the command + while ((s = stdInput.readLine()) != null) { + logger.info("[CommandSender] " + s); + } + + logger.info(logId + " Processing error stream"); + StringBuilder error = new StringBuilder(); + // read any errors from the attempted command + while ((s = stdError.readLine()) != null) { + error.append(s+"\n"); + } + if(error.toString().length()>0) + logger.severe(logId + " Error with wemo.py: "+error.toString()); + + logSendCommand(command); + } catch (IOException e) { + logger.severe(logId + " Error reading response from " + device.getId() + ") " + device.getName()); + logger.severe(e.getMessage()); + } + } + +} diff --git a/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/WeMoInsight/Monitor.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/WeMoInsight/Monitor.java new file mode 100644 index 0000000..b523ca2 --- /dev/null +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/devicetypes/WeMoInsight/Monitor.java @@ -0,0 +1,72 @@ +package edu.cmu.sei.kalki.iotinterface.devicetypes.WeMoInsight; + +import edu.cmu.sei.kalki.iotinterface.devicetypes.PollingMonitor; +import edu.cmu.sei.ttg.kalki.models.DeviceStatus; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.StringBuilder; +import org.json.JSONException; +import org.json.JSONObject; + +public class Monitor extends PollingMonitor { + private static final String logId = "[WemoMonitor]"; + + public Monitor(int deviceId, String ip, int samplingRate) { + super(deviceId, ip, true, samplingRate); + logger.info(logId + " Starting monitor."); + start(); + } + + /** + * Executes the wemo python script to get a WemoInsight's status + * @param status The DeviceStatus to be sent to the DeviceControllerApi + */ + @Override + public void pollDevice(DeviceStatus status) { + try { + + // run the command + // using the Runtime exec method: + String[] args = new String[]{ + "python", + "wemo.py", + deviceIp, + "status" + }; + Process p = Runtime.getRuntime().exec(args); + + BufferedReader stdInput = new BufferedReader(new + InputStreamReader(p.getInputStream())); + + BufferedReader stdError = new BufferedReader(new + InputStreamReader(p.getErrorStream())); + + // read the output from the command + StringBuilder st = new StringBuilder(); + String s = null; + while ((s = stdInput.readLine()) != null) { + st.append(s); + } + logger.info("Output from device: "+st.toString()); + JSONObject json = new JSONObject(st.toString()); + for(Object keyObj : json.keySet()){ + String key = (String) keyObj; + String value = (String) json.get(key).toString(); + status.addAttribute(key, value); + } + // read any errors from the attempted command + while ((s = stdError.readLine()) != null) { + logger.severe(s); + } + + } catch (IOException e) { + logger.severe(logId + " Error polling Wemo Insight: " + e.toString()); + } catch (JSONException e) { + logger.severe(logId + " Error parsing JSON response from Wemo Insight: " + deviceId + ". " + e.getMessage()); + e.printStackTrace(); + } + + } +} diff --git a/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/Config.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/Config.java new file mode 100644 index 0000000..af160e9 --- /dev/null +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/Config.java @@ -0,0 +1,34 @@ +package edu.cmu.sei.kalki.iotinterface.utils; + +import org.json.JSONObject; +import org.json.JSONTokener; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Created by sebastianecheverria on 8/2/17. + */ +public class Config { + public static Map data = new HashMap<>(); + + public static void load(String configFilePath) throws IOException { + System.out.println(System.getProperty("user.dir")); + InputStream fs = new FileInputStream(configFilePath); + JSONTokener parser = new JSONTokener(fs); + JSONObject config = new JSONObject(parser); + + Iterator configData = config.keys(); + while(configData.hasNext()) { + String paramName = configData.next(); + String paramValue = config.getString(paramName); + data.put(paramName, paramValue); + } + + fs.close(); + } +} diff --git a/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/DeviceControllerApi.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/DeviceControllerApi.java new file mode 100644 index 0000000..87cbfec --- /dev/null +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/DeviceControllerApi.java @@ -0,0 +1,51 @@ +package edu.cmu.sei.kalki.iotinterface.utils; + +import edu.cmu.sei.ttg.kalki.models.DeviceStatus; +import edu.cmu.sei.ttg.kalki.models.StageLog; +import org.json.JSONObject; + +import java.util.logging.Logger; + +public class DeviceControllerApi { + private static final Logger logger = Logger.getLogger("iot-interface"); + + private static final String apiIp = Config.data.get("device_controller_api_ip"); + private static final String apiPort = Config.data.get("device_controller_api_port"); + private static final String basePath = "/device-controller-api"; + + private static final String apiUrl = "http://"+apiIp+":"+apiPort+basePath; + private static final String statusPath = "/new-status"; + private static final String logPath = "/new-stage-log"; + + /** + * Method to send a device status to the DeviceControllerApi + * @param status + */ + public static void sendStatus(DeviceStatus status) { + JSONObject json = new JSONObject(status.toString()); + sendToApi(json, apiUrl+statusPath); + } + + /** + * Method to send a Stage Log to the DeviceControllerApi + * @param log + */ + public static void sendLog(StageLog log) { + JSONObject json = new JSONObject(log.toString()); + sendToApi(json, apiUrl+logPath); + } + + /** + * Helper method to send a json object to the DeviceControllerApi + * @param object + * @param endpoint + */ + private static void sendToApi(JSONObject object, String endpoint){ + try { + HttpRequest.postRequest(object, endpoint); + } catch (Exception e) { + logger.severe("[DeviceControllerApi] Error sending object to DeviceController: "+object.toString()); + logger.severe(e.getMessage()); + } + } +} diff --git a/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/HttpRequest.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/HttpRequest.java new file mode 100644 index 0000000..a9c4018 --- /dev/null +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/HttpRequest.java @@ -0,0 +1,57 @@ +package edu.cmu.sei.kalki.iotinterface.utils; + +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.URL; + +public class HttpRequest { + + public static void putRequest(JSONObject body, String apiUrl) throws Exception { + sendData(body, apiUrl, "PUT"); + } + + public static void postRequest(JSONObject body, String apiUrl) throws Exception { + sendData(body, apiUrl, "POST"); + } + + private static void sendData(JSONObject body, String apiUrl, String method) throws IOException { + HttpURLConnection con = makeConnection(apiUrl, true, method); + appendBodyToRequest(con, body); + con.getInputStream(); + con.disconnect(); + } + + public static JSONObject getRequest(String apiUrl) throws IOException { + HttpURLConnection con = makeConnection(apiUrl, false, "GET"); + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + + StringBuilder response = new StringBuilder(); + String line = ""; + while((line = in.readLine()) != null) { + response.append(line); + } + + con.disconnect(); + + return new JSONObject(response.toString()); + } + + private static void appendBodyToRequest(HttpURLConnection con, JSONObject body) throws IOException { + OutputStreamWriter out = new OutputStreamWriter(con.getOutputStream()); + out.write(body.toString()); + out.close(); + } + + private static HttpURLConnection makeConnection(String endpoint, boolean output, String method) throws IOException { + URL url = new URL(endpoint); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + con.setDoOutput(output); + con.setRequestMethod(method); + return con; + } +} diff --git a/src/main/java/edu/cmu/sei/kalki/Mail/EventObserver.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/mail/EventObserver.java similarity index 59% rename from src/main/java/edu/cmu/sei/kalki/Mail/EventObserver.java rename to src/main/java/edu/cmu/sei/kalki/iotinterface/utils/mail/EventObserver.java index 7eaf469..b0ebed1 100644 --- a/src/main/java/edu/cmu/sei/kalki/Mail/EventObserver.java +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/mail/EventObserver.java @@ -1,4 +1,4 @@ -package edu.cmu.sei.kalki.Mail; +package edu.cmu.sei.kalki.iotinterface.utils.mail; public interface EventObserver { public void notify(String message); diff --git a/src/main/java/edu/cmu/sei/kalki/Mail/MailServer.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/mail/MailServer.java similarity index 55% rename from src/main/java/edu/cmu/sei/kalki/Mail/MailServer.java rename to src/main/java/edu/cmu/sei/kalki/iotinterface/utils/mail/MailServer.java index ec03a85..8dc840f 100644 --- a/src/main/java/edu/cmu/sei/kalki/Mail/MailServer.java +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/mail/MailServer.java @@ -1,17 +1,12 @@ -package edu.cmu.sei.kalki.Mail; +package edu.cmu.sei.kalki.iotinterface.utils.mail; +import edu.cmu.sei.kalki.iotinterface.utils.Config; import org.subethamail.smtp.server.SMTPServer; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import java.util.List; -import java.util.Properties; import java.util.logging.Logger; -import org.json.JSONObject; -import org.json.JSONTokener; public class MailServer { @@ -43,25 +38,10 @@ public static void notify(String fromEmail){ public static void initialize() { if (mailServer == null){ logger.info("Initializing mail server"); - - try{ - - InputStream fs = new FileInputStream("config.json"); - JSONTokener parser = new JSONTokener(fs); - JSONObject config = new JSONObject(parser); - int port = config.getInt("MAIL_PORT"); - fs.close(); - - mailServer = new MailServer(port); - logger.info("[MailServer] Succesfully initialized mail Server."); - } - catch(IOException e){ - logger.severe("[MailServer] Error intializing mail server."); - System.exit(-1); - } - + mailServer = new MailServer(Integer.valueOf(Config.data.get("MAIL_PORT"))); + logger.info("[MailServer] Successfully initialized mail Server."); } else { - logger.info("[MailServer] Mail Server already initialized"); + logger.info("[MailServer] mail Server already initialized"); } } } diff --git a/src/main/java/edu/cmu/sei/kalki/Mail/MyMessageHandlerFactory.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/mail/MyMessageHandlerFactory.java similarity index 97% rename from src/main/java/edu/cmu/sei/kalki/Mail/MyMessageHandlerFactory.java rename to src/main/java/edu/cmu/sei/kalki/iotinterface/utils/mail/MyMessageHandlerFactory.java index 8de83a1..bd28f4d 100644 --- a/src/main/java/edu/cmu/sei/kalki/Mail/MyMessageHandlerFactory.java +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/mail/MyMessageHandlerFactory.java @@ -1,4 +1,4 @@ -package edu.cmu.sei.kalki.Mail; +package edu.cmu.sei.kalki.iotinterface.utils.mail; import org.subethamail.smtp.*; diff --git a/src/main/java/edu/cmu/sei/kalki/Mail/SendMail.java b/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/mail/SendMail.java similarity index 96% rename from src/main/java/edu/cmu/sei/kalki/Mail/SendMail.java rename to src/main/java/edu/cmu/sei/kalki/iotinterface/utils/mail/SendMail.java index cd87afe..0459224 100644 --- a/src/main/java/edu/cmu/sei/kalki/Mail/SendMail.java +++ b/src/main/java/edu/cmu/sei/kalki/iotinterface/utils/mail/SendMail.java @@ -1,4 +1,4 @@ -package edu.cmu.sei.kalki.Mail; +package edu.cmu.sei.kalki.iotinterface.utils.mail; import java.util.*; import javax.mail.*; diff --git a/src/main/java/edu/cmu/sei/kalki/utils/DeviceControllerApi.java b/src/main/java/edu/cmu/sei/kalki/utils/DeviceControllerApi.java deleted file mode 100644 index 1622e31..0000000 --- a/src/main/java/edu/cmu/sei/kalki/utils/DeviceControllerApi.java +++ /dev/null @@ -1,43 +0,0 @@ -package edu.cmu.sei.kalki.utils; - -import edu.cmu.sei.ttg.kalki.models.DeviceStatus; -import edu.cmu.sei.ttg.kalki.models.StageLog; -import org.json.JSONObject; - -import java.io.OutputStreamWriter; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.logging.Logger; - -public class DeviceControllerApi { - private static final Logger logger = Logger.getLogger("iot-interface"); - - private static final String statusEndpoint = "new-status"; - private static final String logEndpoint = "new-stage-log"; - - public static void sendStatus(DeviceStatus status, String apiUrl) { - JSONObject json = new JSONObject(status.toString()); - sendToApi(json, apiUrl+statusEndpoint); - } - - public static void sendLog(StageLog log, String apiUrl) { - JSONObject json = new JSONObject(log.toString()); - sendToApi(json, apiUrl+logEndpoint); - } - - private static void sendToApi(JSONObject object, String apiUrl){ - try { - URL url = new URL(apiUrl); - HttpURLConnection httpCon = (HttpURLConnection) url.openConnection(); - httpCon.setDoOutput(true); - httpCon.setRequestMethod("POST"); - OutputStreamWriter out = new OutputStreamWriter(httpCon.getOutputStream()); - out.write(object.toString()); - out.close(); - httpCon.getInputStream(); - } catch (Exception e) { - logger.severe("[DeviceControllerApi] Error sending object to DeviceController: "+object.toString()); - logger.severe(e.getMessage()); - } - } -} diff --git a/wemo.py b/wemo.py index e3e1995..6655a18 100644 --- a/wemo.py +++ b/wemo.py @@ -7,23 +7,25 @@ insight_ip = sys.argv[1] command = sys.argv[2] -#try: -device = Insight("http://"+insight_ip+":49153/setup.xml") -if command == "turn-off": - device.off() - print "Insight turned off:", device.name -if command == "turn-on": - device.on() - print "Insight turned on:", device.name -if command == "status": - result = device.insight_params - result['today_kwh'] = device.today_kwh - result['today_standby_time'] = device.today_standby_time - result['lastchange'] = result['lastchange'].strftime("%Y-%m-%d %H:%M:%S") - if(result['state'] == '1'): - result['isOn'] = True +try: + device = Insight("http://"+insight_ip+":49153/setup.xml") + if command == "turn-off": + device.off() + print("Insight turned off: " + device.name) + elif command == "turn-on": + device.on() + print("Insight turned on: " + device.name) + elif command == "status": + result = device.insight_params + result['today_kwh'] = device.today_kwh + result['today_standby_time'] = device.today_standby_time + result['lastchange'] = result['lastchange'].strftime("%Y-%m-%d %H:%M:%S") + if(result['state'] == '1'): + result['isOn'] = True + else: + result['isOn'] = False + print(result) else: - result['isOn'] = False - print(result) -#except: -# print("Unknown Device") + print("Unknown command") +except Exception as e: + print("Exception happened " + str(e))