package com.sb.elsinore; import ca.strangebrew.recipe.Recipe; import com.sb.common.CollectionsUtil; import com.sb.elsinore.annotations.Parameter; import com.sb.elsinore.html.*; import com.sb.elsinore.notificiations.Notifications; import jGPIO.InvalidGPIOException; import java.io.*; import java.math.BigDecimal; import java.nio.file.Files; import java.util.*; import java.util.Map.Entry; import java.util.logging.Level; import org.apache.tika.Tika; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.rendersnake.HtmlCanvas; import org.rendersnake.tools.PrettyWriter; import com.sb.elsinore.NanoHTTPD.Response; import com.sb.elsinore.NanoHTTPD.Response.Status; import com.sb.elsinore.annotations.UrlEndpoint; import com.sb.elsinore.inputs.PhSensor; import com.sb.elsinore.recipes.BeerXMLReader; import com.sb.elsinore.triggers.TriggerInterface; import javax.xml.xpath.XPathException; import static org.rendersnake.HtmlAttributesFactory.id; @SuppressWarnings("unchecked") public class UrlEndpoints { public File rootDir; public Map<String, String> parameters = null; public Map<String, String> files = null; public Map<String, String> header = null; public static final Map<String, String> MIME_TYPES = new HashMap<String, String>() { /** * The Serial UID. */ public static final long serialVersionUID = 1L; { put("css", "text/css"); put("htm", "text/html"); put("html", "text/html"); put("xml", "text/xml"); put("axml", "application/xml"); put("txt", "text/plain"); put("asc", "text/plain"); put("gif", "image/gif"); put("jpg", "image/jpeg"); put("jpeg", "image/jpeg"); put("png", "image/png"); put("mp3", "audio/mpeg"); put("m3u", "audio/mpeg-url"); put("mp4", "video/mp4"); put("ogv", "video/ogg"); put("flv", "video/x-flv"); put("mov", "video/quicktime"); put("swf", "application/x-shockwave-flash"); put("js", "application/javascript"); put("pdf", "application/pdf"); put("doc", "application/msword"); put("ogg", "application/x-ogg"); put("zip", "application/octet-stream"); put("exe", "application/octet-stream"); put("class", "application/octet-stream"); put("json", "application/json"); } }; public static final String MIME_HTML = "text/html"; public Map<String, String> getParameters() { return this.parameters; } public Map<String, String> ParseParams() { return this.ParseParams(this.getParameters()); } /** * Convert a JSON parameter set into a hashmap. * @param params The incoming parameter listing * @return The converted parameters */ public Map<String, String> ParseParams(Map<String, String> params) { Set<Entry<String, String>> incomingParams = params.entrySet(); Map<String, String> parms; Iterator<Entry<String, String>> it = incomingParams.iterator(); Entry<String, String> param; JSONObject incomingData = null; JSONParser parser = new JSONParser(); String inputUnit = null; // Try to Parse JSON Data while (it.hasNext()) { param = it.next(); BrewServer.LOG.info("Key: " + param.getKey()); BrewServer.LOG.info("Entry: " + param.getValue()); try { Object parsedData = parser.parse(param.getValue()); if (parsedData instanceof JSONArray) { incomingData = (JSONObject) ((JSONArray) parsedData).get(0); } else { incomingData = (JSONObject) parsedData; inputUnit = param.getKey(); } } catch (Exception e) { BrewServer.LOG.info("couldn't read " + param.getValue() + " as a JSON Value " + e.getMessage()); } } if (incomingData != null) { // Use the JSON Data BrewServer.LOG.info("Found valid data for " + inputUnit); parms = (Map<String, String>) incomingData; } else { inputUnit = params.get("form"); parms = params; } if (inputUnit != null) { parms.put("inputunit", inputUnit); } return parms; } @UrlEndpoint(url = "/addsystem", help = "Enable the system temperature probe", parameters = {}) public final Response addSystemTempProbe() { LaunchControl.addSystemTemp(); return new NanoHTTPD.Response(Status.OK, MIME_HTML, "Added system temperature"); } @UrlEndpoint(url = "/clearStatus", help = "Clear the current status message", parameters = {}) public final Response clearStatus() { LaunchControl.setMessage(""); return new NanoHTTPD.Response(Status.OK, MIME_HTML, "Status Cleared"); } @UrlEndpoint(url = "/delsystem", help = "Disable the system temperature probe", parameters = {}) public final Response delSystemStempProbe() { LaunchControl.delSystemTemp(); return new NanoHTTPD.Response(Status.OK, MIME_HTML, "Deleted system temperature"); } @UrlEndpoint(url = "/getstatus", help = "Get the current status JSON", parameters = {}) public final Response getStatus() { return new NanoHTTPD.Response(Status.OK, MIME_TYPES.get("json"), LaunchControl.getJSONStatus()); } @UrlEndpoint(url = "/getsystemsettings", help = "Get the current system settings", parameters = {}) public final Response getSystemSettings() { return new NanoHTTPD.Response(Status.OK, MIME_TYPES.get("json"), LaunchControl.getSystemStatus()); } @UrlEndpoint( url = "/graph", help = "Get the current Graph file", parameters = {}) public final Response getGraph() { return BrewServer.serveFile("/templates/static/graph/graph.html", header, rootDir); } @UrlEndpoint(url = "/checkgit", help = "Check for updates from GIT", parameters = {}) public final Response checkForUpdates() { LaunchControl.checkForUpdates(); return new NanoHTTPD.Response(Status.OK, MIME_TYPES.get("json"), "{Status:'OK'}"); } @UrlEndpoint(url = "/restartupdate", help = "Update from GIT and restart", parameters = {}) public final Response updateFromGit() { LaunchControl.updateFromGit(); return new NanoHTTPD.Response(Status.OK, MIME_TYPES.get("json"), "{Status:'OK'}"); } @UrlEndpoint(url = "/settheme", help = "Set the current theme name", parameters = {@Parameter(name = "name", value = "The name of the theme to set")}) public final Response setTheme() { String newTheme = parameters.get("name"); if (newTheme == null) { return new NanoHTTPD.Response(Status.BAD_REQUEST, MIME_TYPES.get("json"), "{Status:'No name provided'}"); } String fileName = "/logos/" + newTheme + ".ico"; if (!(new File(rootDir, fileName).exists())) { // It doesn't exist LaunchControl.setMessage("Favicon for the new theme: " + newTheme + ", doesn't exist." + " Please add: " + fileName + " and try again"); return new NanoHTTPD.Response(Status.BAD_REQUEST, MIME_TYPES.get("json"), "{Status:'Favicon doesn\'t exist'}"); } fileName = "/logos/" + newTheme + ".gif"; if (!(new File(rootDir, fileName).exists())) { // It doesn't exist LaunchControl.setMessage("Brewry image for the new theme: " + newTheme + ", doesn't exist." + " Please add: " + fileName + " and try again"); return new NanoHTTPD.Response(Status.BAD_REQUEST, MIME_TYPES.get("json"), "{Status:'Brewery Image doesn\'t exist'}"); } LaunchControl.theme = newTheme; return new NanoHTTPD.Response(Status.OK, MIME_TYPES.get("json"), "{Status:'OK'}"); } /** * Parse the parameters to update the MashProfile. * @return True if the profile is updated successfully. */ @UrlEndpoint(url = "/triggerprofile", help = "Get the trigger profile for a temperature probe", parameters = {@Parameter(name = "tempprobe", value = "The Temperature probe to trigger the profile for.")}) public final Response updateTriggerProfile() { String tempProbe = parameters.get("tempprobe"); if (tempProbe == null) { LaunchControl.setMessage("Could not trigger profile," + " no tempProbe provided"); return new NanoHTTPD.Response(Status.OK, MIME_HTML, "FAILED"); } Temp tProbe = LaunchControl.findTemp(tempProbe); if (tProbe == null) { LaunchControl.setMessage("Could not trigger profile," + " no tempProbe provided"); return new NanoHTTPD.Response(Status.OK, MIME_HTML, "FAILED"); } TriggerControl triggerControl = tProbe.getTriggerControl(); if (triggerControl.triggerCount() > 0) { triggerControl.activateTrigger(0); if (triggerControl.isActive()) { triggerControl.deactivate(); } } return new NanoHTTPD.Response(Status.OK, MIME_HTML, "Updated MashProfile"); } /** * Reorder the triggers steps. * @return The Response object. */ @UrlEndpoint(url = "/reordertriggers", help = "Re-order the triggers", parameters = {@Parameter(name = "tempprobe", value = "The temperature probe to re-order the triggers for"), @Parameter(name = "<old position>", value="<new position>")} ) public final Response reorderMashProfile() { Map<String, String> params = this.parameters; JSONObject usage = new JSONObject(); usage.put("Usage", "Set the order for the mash profile."); usage.put("tempprobe", "The name of the TempProbe to change the mash profile order on."); usage.put(":old=:new", "The old position and the new position"); // Resort the mash profile for the PID, then sort the rest String tempProbe = params.get("tempprobe"); // Do we have a PID coming in? if (tempProbe == null) { LaunchControl.setMessage( "No Temp Probe supplied for trigger profile order sort"); return new Response(Status.BAD_REQUEST, MIME_TYPES.get("json"), usage.toJSONString()); } // Prevent any errors from the PID not being a number. params.remove("tempprobe"); // Do we have a mash control for the PID? TriggerControl mControl = LaunchControl.findTriggerControl(tempProbe); if (mControl == null) { return null; } // Should be good to go, iterate and update! for (Map.Entry<String, String> mEntry: params.entrySet()) { if (mEntry.getKey().equals("NanoHttpd.QUERY_STRING")) { continue; } try { int oldPos = Integer.parseInt(mEntry.getKey()); int newPos = Integer.parseInt(mEntry.getValue()); mControl.getTrigger(oldPos).setPosition(newPos); } catch (NumberFormatException nfe) { LaunchControl.setMessage( "Failed to parse Trigger reorder value," + " things may get weird: " + mEntry.getKey() + ": " + mEntry.getValue()); } catch (IndexOutOfBoundsException ie) { LaunchControl.setMessage( "Invalid trigger position, things may get weird"); } } LaunchControl.saveSettings(); return new Response(Status.OK, MIME_TYPES.get("json"), usage.toJSONString()); } /** * Delete the specified trigger step. * @return The Response. */ @UrlEndpoint(url = "/deltriggerstep", help = "Delete the trigger step at a specific position", parameters = {@Parameter(name = "device", value = "The name of the temperature probe to delete the trigger step from"), @Parameter(name = "position", value = "The integer position of the trigger to delete from the profile")}) public Response delTriggerStep() { Map<String, String> params = this.parameters; // Parse the response // Temp unit, PID, duration, temp, method, type, step number // Default to the existing temperature scale JSONObject usage = new JSONObject(); usage.put("Usage", "Add a new mashstep to the specified PID"); usage.put("device", "The PID to delete the mash step from"); usage.put("position", "The mash step to delete"); Status status = Status.OK; try { String tempProbe = params.get("device"); if (tempProbe == null) { BrewServer.LOG.warning("No device parameter supplied to delete trigger."); } int position = Integer.parseInt(params.get("position")); Temp temp = LaunchControl.findTemp(tempProbe); if (temp == null) { return null; } TriggerControl mControl = temp.getTriggerControl(); if (mControl != null) { mControl.delTriggerStep(position); } else { return null; } } catch (NumberFormatException nfe) { nfe.printStackTrace(); LaunchControl.setMessage( "Couldn't parse the position to delete: " + params); status = Status.BAD_REQUEST; } catch (NullPointerException ne) { ne.printStackTrace(); LaunchControl.setMessage( "Couldn't parse the mash data to delete: " + params); status = Status.BAD_REQUEST; } catch (IndexOutOfBoundsException ie) { LaunchControl.setMessage( "Invalid Mash step position to delete: " + ie.getMessage()); status = Status.BAD_REQUEST; } return new Response(status, MIME_TYPES.get("json"), usage.toJSONString()); } /** * Toggle the state of the mash profile on/off. * @return True if set, false if there's an error */ @UrlEndpoint(url = "/toggleTrigger", help = "Toggle the trigger profile on or off for a temp probe", parameters = {@Parameter(name = "tempprobe", value = "The temperature probe to toggle the trigger profile for"), @Parameter(name = "status", value = "\"activate\" or \"deactivate\" to enable or disable the profile"), @Parameter(name = "position", value = "The integer position of the step to activate or deactivate (starting from 0)")}) public Response toggleTriggerProfile() { String tempProbe; if (parameters.containsKey("tempprobe")) { tempProbe = parameters.get("tempprobe"); } else { BrewServer.LOG.warning("No Temp provided to toggle Trigger Profile"); return new NanoHTTPD.Response(Status.BAD_REQUEST, MIME_HTML, "Failed to toggle mash profile"); } boolean activate = false; if (parameters.containsKey("status")) { if (parameters.get("status").equalsIgnoreCase("activate")) { activate = true; } } else { BrewServer.LOG.warning("No Status provided to toggle Profile"); return new NanoHTTPD.Response(Status.BAD_REQUEST, MIME_HTML, "Failed to toggle mash profile"); } int position = -1; if (parameters.containsKey("position")) { try { position = Integer.parseInt(parameters.get("position")); } catch (NumberFormatException e) { BrewServer.LOG.warning("Couldn't parse positional argument: " + parameters.get("position")); return new NanoHTTPD.Response(Status.BAD_REQUEST, MIME_HTML, "Failed to toggle mash profile"); } } Temp temp = LaunchControl.findTemp(tempProbe); if (temp == null) { return null; } TriggerControl mObj = temp.getTriggerControl(); TriggerInterface triggerEntry = mObj.getCurrentTrigger(); int stepToUse; // We have a mash step position if (position >= 0) { BrewServer.LOG.warning("Using mash step for " + tempProbe + " at position " + position); } if (!activate) { // We're de-activating everything stepToUse = -1; } else if (triggerEntry == null && mObj.triggerCount() > 0) { stepToUse = 0; } else if (triggerEntry != null) { stepToUse = triggerEntry.getPosition(); } else { stepToUse = -1; } if (stepToUse >= 0 && activate) { mObj.activateTrigger(stepToUse); BrewServer.LOG.warning( "Activated " + tempProbe + " step at " + stepToUse); } else { mObj.deactivateTrigger(stepToUse, true); BrewServer.LOG.warning("Deactivated " + tempProbe + " step at " + stepToUse); } LaunchControl.startMashControl(tempProbe); return new NanoHTTPD.Response(Status.OK, MIME_HTML, "Toggled profile"); } /** * Read the incoming parameters and edit the vessel as appropriate. * @return True is success, false if failure. */ @SuppressWarnings("ConstantConditions") @UrlEndpoint(url = "/editdevice", help = "Edit the main settings for a device", parameters = {@Parameter(name = "form", value="The Address of the temperature probe to update"), @Parameter(name = "new_name", value="The new name of the probe"), @Parameter(name = "new_heat_gpio", value="The Heat GPIO"), @Parameter(name = "new_cool_gpio", value="The Cool GPIO"), @Parameter(name = "heat_invert", value="\"on\" to enable inverted outputs for the heating pin"), @Parameter(name = "cool_invert", value="\"on\" to enable inverted outputs for the cooling pin"), @Parameter(name = "aux_gpio", value="A pin to use for auxilliary output"), @Parameter(name = "cutoff", value="A temperature value at which to shutdown Elsinore as a safety measure"), @Parameter(name = "cutoff_enabled", value="On to use the cutoff temperature"), @Parameter(name = "calibration", value="An offset for calibration")}) public Response editVessel() { final Map<String, String> params = this.parameters; String auxpin, newName, heatgpio; String inputUnit = null, cutoff, coolgpio; String calibration; boolean heatInvert, coolInvert, auxInvert; int size = -1; Set<Entry<String, String>> incomingParams = params.entrySet(); Map<String, String> parms; Iterator<Entry<String, String>> it = incomingParams.iterator(); Entry<String, String> param; JSONObject incomingData = null; JSONParser parser = new JSONParser(); // Try to Parse JSON Data while (it.hasNext()) { param = it.next(); BrewServer.LOG.info("Key: " + param.getKey()); BrewServer.LOG.info("Entry: " + param.getValue()); try { Object parsedData = parser.parse(param.getValue()); if (parsedData instanceof JSONArray) { incomingData = (JSONObject) ((JSONArray) parsedData).get(0); } else { incomingData = (JSONObject) parsedData; inputUnit = param.getKey(); } } catch (Exception e) { BrewServer.LOG.info("couldn't read " + param.getValue() + " as a JSON Value " + e.getMessage()); } } if (incomingData != null && inputUnit != null) { // Use the JSON Data BrewServer.LOG.info("Found valid data for " + inputUnit); parms = (Map<String, String>) incomingData; } else { inputUnit = params.get("form"); parms = params; } // Fall back to the old style newName = parms.get("new_name"); heatgpio = parms.get("new_heat_gpio"); coolgpio = parms.get("new_cool_gpio"); heatInvert = parms.get("heat_invert") != null && Boolean.parseBoolean(params.get("heat_invert")); coolInvert = parms.get("cool_invert") != null && Boolean.parseBoolean(params.get("cool_invert")); auxpin = parms.get("aux_gpio"); auxInvert = parms.get("aux_invert") != null && Boolean.parseBoolean(params.get("aux_invert")); cutoff = parms.get("cutoff"); boolean cutoffEnabled = parms.get("cutoff_enabled") != null && Boolean.parseBoolean(parms.get("cutoff_enabled")); calibration = parms.get("calibration"); try { size = Integer.parseInt(parms.get("size")); } catch (NumberFormatException nfe) { BrewServer.LOG.warning("Couldn't parse: " + parms.get("size") + " as an int."); } if (inputUnit == null || inputUnit.equals("")) { inputUnit = parms.get("address"); if (inputUnit == null) { BrewServer.LOG.warning("No Valid input unit"); } } inputUnit = inputUnit.replaceAll("_","\\."); Temp tProbe = LaunchControl.findTemp(inputUnit); PID tPID = LaunchControl.findPID(inputUnit); if (tProbe == null) { inputUnit = inputUnit.replace("_", " "); tProbe = LaunchControl.findTemp(inputUnit); tPID = LaunchControl.findPID(inputUnit); } if (tProbe == null) { LaunchControl.setMessage("Couldn't find PID: " + inputUnit); return null; } if (tProbe != null && !newName.equals("")) { tProbe.setName(newName); BrewServer.LOG.warning("Updated temp name " + newName); } tProbe.cutoffEnabled = cutoffEnabled; if (!cutoff.equals("")) { tProbe.setCutoffTemp(cutoff); } if (calibration != null) { tProbe.setCalibration(calibration); } tProbe.setSize(size); if (tPID != null && !newName.equals("")) { tPID.getTemp().setName(newName); BrewServer.LOG.warning("Updated PID Name" + newName); } if (newName.equals("")) { newName = inputUnit; } if (tPID == null) { // No PID already, create one. tPID = new PID(tProbe, newName); } // HEATING GPIO if (heatgpio == null || heatgpio.equals("")) { tPID.delHeatGPIO(); } if (coolgpio == null || coolgpio.equals("")) { tPID.delCoolGPIO(); } tPID.setAux(auxpin, auxInvert); if (!heatgpio.equals("") || !coolgpio.equals("")) { if (!heatgpio.equals(tPID.getHeatGPIO())) { // We have a PID, set it to the new value tPID.setHeatGPIO(heatgpio); } tPID.setHeatInverted(heatInvert); if (!coolgpio.equals(tPID.getCoolGPIO())) { // We have a PID, set it to the new value tPID.setCoolGPIO(coolgpio); } tPID.setCoolInverted(coolInvert); LaunchControl.addPID(tPID); } if (heatgpio.equals("") && coolgpio.equals("")) { LaunchControl.deletePID(tPID); } // Update the heat settings if (params.containsKey("heat_p")) { tPID.setHeatP(new BigDecimal(params.get("heat_p"))); } if (params.containsKey("heat_i")) { tPID.setHeatI(new BigDecimal(params.get("heat_i"))); } if (params.containsKey("heat_d")) { tPID.setHeatD(new BigDecimal(params.get("heat_d"))); } if (params.containsKey("heat_cycletime")) { tPID.setHeatCycle(new BigDecimal(params.get("heat_cycletime"))); } // update the cool settings if (params.containsKey("cool_p")) { tPID.setCoolP(new BigDecimal(params.get("cool_p"))); } if (params.containsKey("cool_i")) { tPID.setCoolI(new BigDecimal(params.get("cool_i"))); } if (params.containsKey("cool_d")) { tPID.setCoolD(new BigDecimal(params.get("cool_d"))); } if (params.containsKey("cool_cycletime")) { tPID.setCoolCycle(new BigDecimal(params.get("cool_cycletime"))); } LaunchControl.saveConfigFile(); return new Response(Status.OK, MIME_TYPES.get("txt"), "PID Updated"); } /** * Add a new timer to the brewery. * * @return True if added ok */ @UrlEndpoint(url = "/addtimer", help = "Add a new timer to the system", parameters = {@Parameter(name = "name", value = "The Name of the timer to create"), @Parameter(name = "duration", value = "The duration of the timer"), @Parameter(name="invert", value="True to invert the timer (count down), false to count up")}) public final Response addTimer() { String newName; String inputUnit = ""; Set<Entry<String, String>> incomingParams = parameters.entrySet(); Map<String, String> parms; Iterator<Entry<String, String>> it = incomingParams.iterator(); Entry<String, String> param; JSONObject incomingData = null; JSONParser parser = new JSONParser(); // Try to Parse JSON Data while (it.hasNext()) { param = it.next(); BrewServer.LOG.info("Key: " + param.getKey()); BrewServer.LOG.info("Entry: " + param.getValue()); try { Object parsedData = parser.parse(param.getValue()); if (parsedData instanceof JSONArray) { incomingData = (JSONObject) ((JSONArray) parsedData).get(0); } else { incomingData = (JSONObject) parsedData; inputUnit = param.getKey(); } } catch (Exception e) { BrewServer.LOG.info("couldn't read " + param.getValue() + " as a JSON Value " + e.getMessage()); } } if (incomingData != null) { // Use the JSON Data BrewServer.LOG.info("Found valid data for " + inputUnit); parms = (Map<String, String>) incomingData; } else { parms = parameters; } // Fall back to the old style newName = parms.get("name"); if (newName == null || newName.trim().length() == 0) { return new Response(Status.BAD_REQUEST, MIME_TYPES.get("txt"), "No name provided for the timer"); } String duration = parms.get("duration"); boolean inverted = Boolean.parseBoolean(parms.get("invert")); Timer timer = LaunchControl.findTimer(newName); if (timer == null) { if (!LaunchControl.addTimer(newName, duration)) { return new Response(Status.BAD_REQUEST, MIME_TYPES.get("txt"), "Failed to create timer"); } timer = LaunchControl.findTimer(newName); if (timer == null) { return new Response(Status.BAD_REQUEST, MIME_TYPES.get("txt"), "Failed to find timer"); } timer.setInverted(inverted); return new Response(Status.OK, MIME_TYPES.get("txt"), "Timer created"); } else { timer.setInverted(inverted); timer.setTarget(duration); return new Response(Status.OK, MIME_TYPES.get("txt"), "Timer Updated"); } } /** * Add a new switch to the brewery. * * @return True if added ok */ @UrlEndpoint(url = "/addswitch", help = "Add a new switch to the brewery", parameters = {@Parameter(name = "name", value = "The name of the switch to add"), @Parameter(name = "gpio", value = "The GPIO to control with this switch"), @Parameter(name = "invert", value = "\"on\" to enable inversion of the outputs, anything else for normal output.")}) public Response addSwitch() { String newName = "", gpio = "", originalName = ""; String inputUnit = ""; boolean invert = false; Set<Entry<String, String>> incomingParams = this.parameters.entrySet(); Map<String, String> parms; Iterator<Entry<String, String>> it = incomingParams.iterator(); Entry<String, String> param; JSONObject incomingData = null; JSONParser parser = new JSONParser(); // Try to Parse JSON Data while (it.hasNext()) { param = it.next(); BrewServer.LOG.info("Key: " + param.getKey()); BrewServer.LOG.info("Entry: " + param.getValue()); try { Object parsedData = parser.parse(param.getValue()); if (parsedData instanceof JSONArray) { incomingData = (JSONObject) ((JSONArray) parsedData).get(0); } else { incomingData = (JSONObject) parsedData; inputUnit = param.getKey(); } } catch (Exception e) { BrewServer.LOG.info("couldn't read " + param.getValue() + " as a JSON Value " + e.getMessage()); } } if (incomingData != null) { // Use the JSON Data BrewServer.LOG.info("Found valid data for " + inputUnit); parms = (Map<String, String>) incomingData; } else { parms = this.parameters; } // Fall back to the old style if (parms.containsKey("originalname")) { originalName = parms.get("originalname"); } if (parms.containsKey("name")) { newName = parms.get("name"); } if (parms.containsKey("gpio")) { gpio = parms.get("gpio"); } if (parms.containsKey("invert")) { String i = parms.get("invert"); if (i.equalsIgnoreCase("on")) { invert = true; } else if (i.equalsIgnoreCase("off")) { invert = false; } else { invert = Boolean.parseBoolean(parms.get("invert")); } } Switch aSwitch = null; if (originalName != null && originalName.length() > 0) { aSwitch = LaunchControl.findSwitch(originalName); if (aSwitch != null) { aSwitch.setName(newName); try { aSwitch.setGPIO(gpio); } catch (InvalidGPIOException e) { } } else { aSwitch = LaunchControl.addSwitch(newName, gpio); } } else { aSwitch = LaunchControl.addSwitch(newName, gpio); } if (aSwitch != null) { aSwitch.setInverted(invert); LaunchControl.saveSettings(); return new Response(Status.OK, MIME_TYPES.get("txt"), "Switch Added"); } else { LaunchControl.setMessage( "Could not add switch " + newName + ": " + gpio); } JSONObject usage = new JSONObject(); usage.put("Usage", "Add a new switch to the system"); usage.put("new_name", "The name of the switch to add"); usage.put("new_gpio", "The GPIO for the switch to work on"); usage.put("Error", "Invalid parameters passed " + this.parameters.toString()); return new Response(Status.BAD_REQUEST, MIME_TYPES.get("json"), usage.toJSONString()); } /** * Read the incoming parameters and update the PID as appropriate. * * @return True if success, false if failure */ @SuppressWarnings("unchecked") @UrlEndpoint(url = "/updatepid", help = "The big one, update the PID parameters", parameters = {@Parameter(name = "inputunit", value = "The PID to change settings for"), @Parameter(name = "dutycycle", value = "The Duty cycle to set (between -100 and +100)"), @Parameter(name = "cycletime", value = "The manual cycle time to use (in seconds)"), @Parameter(name = "setpoint", value = "The target temperature for automatic mode."), @Parameter(name = "heatcycletime", value = "The cycle time for the heating output"), @Parameter(name = "heatp", value = "The proportional value for the heating PID"), @Parameter(name = "heati", value = "The integral value for the heating PID"), @Parameter(name = "heatd", value = "The differential value for the heating PID"), @Parameter(name = "coolcycletime", value = "The cycle time for the cooling output"), @Parameter(name = "coolp", value = "The proportional value for the cooling PID"), @Parameter(name = "cooli", value = "The integral value for the cooling PID"), @Parameter(name = "coold", value = "The differential value for the cooling PID"), @Parameter(name = "mode", value = "The mode to use \"off\" \"auto\" \"manual\" \"hysteria\""), @Parameter(name = "min", value = "The minimum temperature to turn on the heating output, or turn off the cooling output in hysteria mode"), @Parameter(name = "max", value = "The maximum temperature to turn on the cooling output, or turn off the heating output in hysteria mode"), @Parameter(name = "time", value = "The minimum amount of time to turn the cooling/heating output on or off for")}) public Response updatePID() { String temp, mode = "off"; BigDecimal dTemp, duty = null, heatcycle = null, setpoint = null, heatp = null, heati = null, heatd = null, min = null, max = null, time = null, coolcycle = null, coolp = null, cooli = null, coold = null,cooldelay = null, cycle = null; JSONObject sub_usage = new JSONObject(); Map<String, String> parms = ParseParams(parameters); String inputUnit = parms.get("inputunit"); boolean errorValue = false; PID tPID = LaunchControl.findPID(inputUnit); if (tPID == null) { BrewServer.LOG.warning("Couldn't find PID: " + inputUnit); LaunchControl.setMessage("Could not find PID: " + inputUnit); LaunchControl.setMessage("Bad inputs when updating." + " Please check the system log"); return new Response(Status.BAD_REQUEST, MIME_TYPES.get("json"), ""); } // Fall back to the old style sub_usage.put("dutycycle", "The new duty cycle % to set"); if (parms.containsKey("dutycycle")) { temp = parms.get("dutycycle"); try { dTemp = new BigDecimal(temp.replace(",", ".")); duty = dTemp; BrewServer.LOG.info("Duty cycle: " + duty); } catch (NumberFormatException nfe) { System.out.print("Bad duty"); errorValue = true; } } sub_usage.put("setpoint", "The new target temperature to set"); if (parms.containsKey("setpoint")) { temp = parms.get("setpoint"); try { dTemp = new BigDecimal(temp.replace(",", ".")); setpoint = dTemp; BrewServer.LOG.info("Set Point: " + setpoint); } catch (NumberFormatException nfe) { BrewServer.LOG.warning("Bad setpoint"); errorValue = true; } } sub_usage.put("heatcycletime", "The new heat cycle time in seconds to set"); if (parms.containsKey("heatcycletime")) { temp = parms.get("heatcycletime"); try { dTemp = new BigDecimal(temp.replace(",", ".")); heatcycle = dTemp; BrewServer.LOG.info("Cycle time: " + heatcycle); } catch (NumberFormatException nfe) { if (tPID.hasValidHeater()) { BrewServer.LOG.warning("Bad cycle"); errorValue = true; } } } sub_usage.put("heatp", "The new proportional value to set"); if (parms.containsKey("heatp")) { temp = parms.get("heatp"); try { dTemp = new BigDecimal(temp.replace(",", ".")); heatp = dTemp; BrewServer.LOG.info("heat P: " + heatp); } catch (NumberFormatException nfe) { if (tPID.hasValidHeater()) { BrewServer.LOG.warning("Bad heat p"); errorValue = true; } } } sub_usage.put("heati", "The new integral value to set"); if (parms.containsKey("heati")) { temp = parms.get("heati"); try { dTemp = new BigDecimal(temp.replace(",", ".")); heati = dTemp; BrewServer.LOG.info("Heat I: " + heati); } catch (NumberFormatException nfe) { if (tPID.hasValidHeater()) { BrewServer.LOG.warning("Bad heat i"); errorValue = true; } } } sub_usage.put("heatd", "The new head differential value to set"); if (parms.containsKey("heatd")) { temp = parms.get("heatd"); try { dTemp = new BigDecimal(temp.replace(",", ".")); heatd = dTemp; BrewServer.LOG.info("Heat D: " + heatd); } catch (NumberFormatException nfe) { if (tPID.hasValidHeater()) { BrewServer.LOG.warning("Bad heat d"); errorValue = true; } } } sub_usage.put("coolcycletime", "The new cool cycle time in seconds to set"); if (parms.containsKey("coolcycletime")) { temp = parms.get("coolcycletime"); try { dTemp = new BigDecimal(temp.replace(",", ".")); coolcycle = dTemp; BrewServer.LOG.info("Cycle time: " + coolcycle); } catch (NumberFormatException nfe) { if (tPID.hasValidCooler()) { BrewServer.LOG.warning("Bad cycle"); errorValue = true; } } } sub_usage.put("cycletime", "The new manual cycle time in seconds to set"); if (parms.containsKey("cycletime")) { temp = parms.get("cycletime"); try { dTemp = new BigDecimal(temp.replace(",", ".")); cycle = dTemp; BrewServer.LOG.info("Cycle time: " + cycle); } catch (NumberFormatException nfe) { if (tPID.hasValidHeater()) { BrewServer.LOG.warning("Bad cycle"); errorValue = true; } } } sub_usage.put("cooldelay", "The new cool cycle delay in minutes to set"); if (parms.containsKey("cooldelay")) { temp = parms.get("cooldelay"); try { dTemp = new BigDecimal(temp.replace(",", ".")); cooldelay = dTemp; BrewServer.LOG.info("Delay time: " + cooldelay); } catch (NumberFormatException nfe) { if (tPID.hasValidCooler()) { BrewServer.LOG.warning("Bad Cool delay"); errorValue = true; } } } sub_usage.put("coolp", "The new proportional value to set"); if (parms.containsKey("coolp")) { temp = parms.get("coolp"); try { dTemp = new BigDecimal(temp.replace(",", ".")); coolp = dTemp; BrewServer.LOG.info("cool P: " + coolp); } catch (NumberFormatException nfe) { if (tPID.hasValidCooler()) { BrewServer.LOG.warning("Bad cool p"); errorValue = true; } } } sub_usage.put("cooli", "The new integral value to set"); if (parms.containsKey("cooli")) { temp = parms.get("cooli"); try { dTemp = new BigDecimal(temp.replace(",", ".")); cooli = dTemp; BrewServer.LOG.info("Heat I: " + cooli); } catch (NumberFormatException nfe) { if (tPID.hasValidCooler()) { BrewServer.LOG.warning("Bad cool i"); errorValue = true; } } } sub_usage.put("coold", "The new head differential value to set"); if (parms.containsKey("coold")) { temp = parms.get("coold"); try { dTemp = new BigDecimal(temp.replace(",", ".")); coold = dTemp; BrewServer.LOG.info("Heat D: " + coold); } catch (NumberFormatException nfe) { if (tPID.hasValidCooler()) { BrewServer.LOG.warning("Bad cool d"); errorValue = true; } } } sub_usage.put("mode", "The new mode to set"); if (parms.containsKey("mode")) { mode = parms.get("mode"); BrewServer.LOG.info("Mode: " + mode); } sub_usage.put("min", "The minimum temperature to enable the output (HYSTERIA)"); if (parms.containsKey("min")) { try { dTemp = new BigDecimal(parms.get("min").replace(",", ".")); min = dTemp; BrewServer.LOG.info("Min: " + mode); } catch (NumberFormatException nfe) { BrewServer.LOG.warning("Bad minimum"); errorValue = true; } } sub_usage.put("max", "The maximum temperature to disable the output (HYSTERIA)"); if (parms.containsKey("max")) { try { dTemp = new BigDecimal(parms.get("max").replace(",", ".")); max = dTemp; BrewServer.LOG.info("Max: " + mode); } catch (NumberFormatException nfe) { BrewServer.LOG.warning("Bad maximum"); errorValue = true; } } sub_usage.put("time", "The minimum time when enabling the output (HYSTERIA)"); if (parms.containsKey("time")) { try { dTemp = new BigDecimal(parms.get("time").replace(",", ".")); time = dTemp; BrewServer.LOG.info("Time: " + time); } catch (NumberFormatException nfe) { BrewServer.LOG.warning("Bad time"); errorValue = true; } } BrewServer.LOG.info("Form: " + inputUnit); JSONObject usage = new JSONObject(); usage.put(":PIDname", sub_usage); if (errorValue) { LaunchControl.setMessage("Bad inputs when updating. Please check the system log"); return new Response(Status.BAD_REQUEST, MIME_TYPES.get("json"), usage.toJSONString()); } if (mode.equalsIgnoreCase("hysteria")) { tPID.setHysteria(min, max, time); tPID.useHysteria(); } else { BrewServer.LOG.info(mode + ":" + duty + ":" + heatcycle + ":" + setpoint + ":" + heatp + ":" + heati + ":" + heatd); tPID.updateValues(mode, duty, heatcycle, setpoint, heatp, heati, heatd); tPID.setCoolCycle(coolcycle); tPID.setCoolP(coolp); tPID.setCoolI(cooli); tPID.setCoolD(coold); tPID.setCoolDelay(cooldelay); if (mode.equalsIgnoreCase("manual")) { tPID.setManualDuty(duty); tPID.setManualTime(cycle); } } return new Response(Status.OK, MIME_HTML, "PID " + inputUnit + " updated"); } /** * Add a new data point for the volume reading. * @return A NanoHTTPD response */ @UrlEndpoint(url = "/addvolpoint", help = "Add a volume data point to the vessel", parameters = { @Parameter(name = "name", value = "The temperature probe to add a volume data point to"), @Parameter(name = "units", value = "The volume units to use for setup (Only required when setting up"), @Parameter(name = "volume", value = "The volume that is in the vessel at the time of reading"), @Parameter(name = "onewire_address", value = "The OneWire DS2450 Address to use for the readings"), @Parameter(name = "onewire_offset", value = "The OneWire DS2450 offset (A|B|C|D) to use for the readings"), @Parameter(name = "adc_pin", value = "The onboard AIN pin to use"), @Parameter(name = "i2c_address", value = "The I2C Address to use for the Analogue readings"), @Parameter(name = "i2c_device", value = "The I2C Device (integer) to use for the i2c_address"), @Parameter(name = "i2c_channel", value = "The channel on the I2C ADC to use"), @Parameter(name = "i2c_type", value = "The type of the I2C ADC chip."), }) public Response addVolumePoint() { JSONObject usage = new JSONObject(); Map<String, String> parms = ParseParams(this.parameters); usage.put("name", "Temperature probe name to add a volume point to"); usage.put("volume", "Volume that is in the vessel to add a datapoint for"); usage.put("units", "The Volume units to use (only required when setting up)"); usage.put("onewire_address", "The one wire address to be used for analogue reads"); usage.put("onewire_offset", "The one wire offset to be used for analogue reads"); usage.put("adc_pin", "The ADC Pin to be used for analogue reads"); String name = parms.get("name"); String volume = parms.get("volume"); String units = parms.get("units"); String onewire_add = parms.get("onewire_address"); String onewire_offset = parms.get("onewire_offset"); String adc_pin = parms.get("adc_pin"); String i2c_address = parms.get("i2c_address"); String i2c_device = parms.get("i2c_device"); String i2c_channel = parms.get("i2c_channel"); String i2c_type = parms.get("i2c_type"); String error_msg; if (name == null || volume == null) { error_msg = "No name or volume supplied"; usage.put("Error", "Invalid parameters: " + error_msg); return new Response(Response.Status.BAD_REQUEST, MIME_TYPES.get("json"), usage.toJSONString()); } // We have a name and volume, lookup the temp probe Temp t = LaunchControl.findTemp(name); if (t == null) { error_msg = "Invalid temperature probe name supplied (" + name + ")"; usage.put("Error", "Invalid parameters: " + error_msg); return new Response(Response.Status.BAD_REQUEST, MIME_TYPES.get("json"), usage.toJSONString()); } // We have a temp probe, check to see if there's a valid volume input if (!t.hasVolume()) { if (onewire_add == null && onewire_offset == null && adc_pin == null) { error_msg = "No volume input setup, and no valid volume inputs provided"; usage.put("Error", "Invalid parameters: " + error_msg); return new Response(Response.Status.BAD_REQUEST, MIME_TYPES.get("json"), usage.toJSONString()); } // Check for ADC Pin if (adc_pin != null && !adc_pin.equals("")) { int adcpin = Integer.parseInt(adc_pin); if (0 < adcpin || adcpin > 7) { error_msg = "Invalid ADC Pin offset"; usage.put("Error", "Invalid parameters: " + error_msg); return new Response(Response.Status.BAD_REQUEST, MIME_TYPES.get("json"), usage.toJSONString()); } try { if (!t.setupVolumes(adcpin, units)) { error_msg = "Could not setup volumes for " + adcpin + " Units: " + units; usage.put("Error", "Invalid parameters: " + error_msg); return new Response(Response.Status.BAD_REQUEST, MIME_TYPES.get("json"), usage.toJSONString()); } } catch (InvalidGPIOException g) { error_msg = "Invalid GPIO Pin " + g.getMessage(); usage.put("Error", "Invalid parameters: " + error_msg); return new Response(Response.Status.BAD_REQUEST, MIME_TYPES.get("json"), usage.toJSONString()); } } else if (onewire_add != null && onewire_offset != null && !onewire_add.equals("") && !onewire_offset.equals("")) { // Setup Onewire pin if (!t.setupVolumes(onewire_add, onewire_offset, units)) { error_msg = "Could not setup volumes for " + onewire_add + " offset: " + onewire_offset + " Units: " + units; usage.put("Error", "Invalid parameters: " + error_msg); return new Response(Response.Status.BAD_REQUEST, MIME_TYPES.get("json"), usage.toJSONString()); } } else if (i2c_address != null && i2c_channel != null && i2c_device != null && i2c_address.length() > 0 && i2c_channel.length() > 0 && i2c_device.length() > 0 ) { if (t.setupVolumeI2C(i2c_device, i2c_address, i2c_channel, i2c_type, units)) { error_msg = "Could not setup volumes for " + i2c_device + " address: " + i2c_address + " Units: " + units; usage.put("Error", "Invalid parameters: " + error_msg); return new Response(Response.Status.BAD_REQUEST, MIME_TYPES.get("json"), usage.toJSONString()); } } } // Should be good to go now try { BigDecimal actualVol = new BigDecimal(volume.replace(",", ".")); if (t.addVolumeMeasurement(actualVol)) { return new Response(Response.Status.OK, MIME_TYPES.get("json"), "{'Result':\"OK\"}"); } } catch (NumberFormatException nfe) { error_msg = "Could not setup volumes for " + volume + " Units: " + units; LaunchControl.addMessage(error_msg); usage.put("Error", "Invalid parameters: " + error_msg); return new Response(Response.Status.BAD_REQUEST, MIME_TYPES.get("json"), usage.toJSONString()); } return new Response(Response.Status.BAD_REQUEST, MIME_TYPES.get("json"), usage.toJSONString()); } /** * Get the graph data. * @return the JSON Response data */ @UrlEndpoint(url = "/graph-data.zip", help = "Downloading data as a zip", parameters = {}) public NanoHTTPD.Response getGraphDataZipWrapper() { return getGraphData(); } @UrlEndpoint(url = "/graph-data", help = "Get the graph data as JSON", parameters = {}) public NanoHTTPD.Response getGraphDataWrapper() { return getGraphData(); } @UrlEndpoint(url = "/graph-data/", help = "Get the graph data as JSON", parameters = {}) public NanoHTTPD.Response getGraphData() { if (!LaunchControl.recorderEnabled()) { return new NanoHTTPD.Response("Recorder disabled"); } Map<String, String> parms = ParseParams(parameters); return LaunchControl.getRecorder().getData(parms); } /** * Read the incoming parameters and update the name as appropriate. * * @return True if success, false if failure */ @SuppressWarnings("unchecked") @UrlEndpoint(url = "/setbreweryname", help = "Set the brewery name", parameters = {@Parameter(name="name", value = "The name of the brewery to set")}) public Response updateBreweryName() { Map<String, String> parms = ParseParams(this.parameters); String newName = parms.get("name"); if (newName != null && !newName.equals("") && newName.length() > 0) { LaunchControl.setName(newName); return new Response(Response.Status.OK, MIME_TYPES.get("text"), "Updated"); } return null; } /** * Read the incoming parameters and update the name as appropriate. * @return True if success, false if failure */ @SuppressWarnings("unchecked") @UrlEndpoint(url = "/uploadimage", help = "Upload an image for the brewery logo", parameters = {@Parameter(name = "file", value = "Push a file to this EndPoint to upload")}) public Response updateBreweryImage() { final Map<String, String> files = this.files; if (files.size() == 1) { for (Map.Entry<String, String> entry : files.entrySet()) { try { File uploadedFile = new File(entry.getValue()); String fileType = Files.probeContentType( uploadedFile.toPath()); if (fileType.equalsIgnoreCase(MIME_TYPES.get("gif")) || fileType.equalsIgnoreCase(MIME_TYPES.get("jpg")) || fileType.equalsIgnoreCase(MIME_TYPES.get("jpeg")) || fileType.equalsIgnoreCase(MIME_TYPES.get("png"))) { File targetFile = new File(rootDir, "brewerImage.gif"); if (targetFile.exists() && !targetFile.delete()) { BrewServer.LOG.warning("Failed to delete " + targetFile.getCanonicalPath()); } LaunchControl.copyFile(uploadedFile, targetFile); if (!targetFile.setReadable(true, false) || targetFile.setWritable(true, false)) { BrewServer.LOG.warning("Failed to set read write permissions on " + targetFile.getCanonicalPath()); } LaunchControl.setFileOwner(targetFile); return new Response(Response.Status.OK, MIME_TYPES.get("text"), "Updated brewery logo"); } } catch (IOException e) {} } } return null; } /** * Update the switch order. * @return A HTTP Response */ @UrlEndpoint(url = "/updateswitchorder", help = "Reorder the switches", parameters = {@Parameter(name = "<name of the switch>", value = "New integer position of the switch")}) public Response updateSwitchOrder() { Map<String, String> params = ParseParams(this.parameters); Status status; for (Map.Entry<String, String> entry: params.entrySet()) { if (entry.getKey().equals("NanoHttpd.QUERY_STRING")) { continue; } Switch tSwitch = LaunchControl.findSwitch(entry.getKey()); // Make Sure we're aware of this switch if (tSwitch == null) { LaunchControl.setMessage( "Couldn't find Switch: " + entry.getKey()); return null; } try { LaunchControl.switchList.remove(tSwitch); tSwitch.setPosition(Integer.parseInt(entry.getValue())); CollectionsUtil.addInOrder(LaunchControl.switchList, tSwitch); } catch (NumberFormatException nfe) { LaunchControl.setMessage( "Couldn't parse " + entry.getValue() + " as an integer"); return null; } status = Response.Status.OK; return new Response(status, MIME_TYPES.get("text"), "Reordered"); } return null; } /** * Delete a switch. * @return a reponse */ @UrlEndpoint(url = "/deleteswitch", help = "Delete a switch", parameters = {@Parameter(name = "name", value = "The name of the switch to delete")}) public Response deleteSwitch() { Map<String, String> params = ParseParams(this.parameters); Status status = Response.Status.OK; // find the switch String switchName = params.get("name"); if (switchName != null) { LaunchControl.deleteSwitch(switchName); return new Response(status, MIME_TYPES.get("text"), "Deleted"); } return null; } /** * Update the timer order. * @return A HTTP Response */ @UrlEndpoint(url = "/updatetimerorder", help = "Re-order the timers", parameters = {@Parameter(name = "<name of timer>", value = "<Integer position of the timer>")}) public Response updateTimerOrder() { Map<String, String> params = ParseParams(this.parameters); if (params.size() == 0) { return null; } for (Map.Entry<String, String> entry: params.entrySet()) { if (entry.getKey().equals("NanoHttpd.QUERY_STRING")) { continue; } Timer tTimer = LaunchControl.findTimer(entry.getKey()); // Make Sure we're aware of this switch if (tTimer == null) { LaunchControl.setMessage( "Couldn't find Timer: " + entry.getKey()); continue; } try { tTimer.setPosition(Integer.parseInt(entry.getValue())); } catch (NumberFormatException nfe) { LaunchControl.setMessage( "Couldn't parse " + entry.getValue() + " as an integer"); return null; } } LaunchControl.sortTimers(); return new Response(Response.Status.OK, MIME_TYPES.get("text"), "Updated timer order"); } /** * Delete a timer. * @return a response */ @UrlEndpoint(url = "/deletetimer", help = "Delete a timer", parameters = {@Parameter(name = "name", value = "The name of the timer to delete")}) public Response deleteTimer() { Map<String, String> params = ParseParams(this.parameters); Status status = Response.Status.OK; // find the switch String timerName = params.get("name"); if (timerName != null) { LaunchControl.deleteTimer(timerName); } else { return null; } return new Response(status, MIME_TYPES.get("text"), "Deleted timer " + timerName); } @UrlEndpoint(url = "/setscale", help = "Set the temperature scale", parameters = {@Parameter(name = "scale", value = "The new scale \"F\" or \"C\"")}) public Response setScale() { Map<String, String> parms = this.parameters; Map<String, String> params = ParseParams(parms); Status status = Response.Status.OK; // Iterate all the temperature probes and change the scale if (!LaunchControl.setTempScales(params.get("scale"))) { return null; } return new Response(status, MIME_TYPES.get("text"), "Scale set to " + params.get("scale")); } @UrlEndpoint(url = "/toggledevice", help = "Toggle the visibility of a device", parameters = {@Parameter(name = "device", value = "The name of the device to toggle the visibility of")}) public Response toggleDevice() { Map<String, String> parms = this.parameters; Map<String, String> params = ParseParams(parms); Status status = Response.Status.OK; Temp temp = LaunchControl.findTemp(params.get("device")); if (temp != null) { temp.toggleVisibility(); } else { return null; } return new Response(status, MIME_TYPES.get("text"), "Device " + temp.getName() + " is toggled"); } @UrlEndpoint(url = "/updatesystemsettings", help = "Update the system settings", parameters = { @Parameter(name = "recorder", value = "\"on\" to enable the data recorder, \"off\" to disable it"), @Parameter(name = "recorderDiff", value = "Decimal that represents the tolerance to use when recording data, when the temperature varies by this amount from the previous point, record it"), @Parameter(name = "recorderTime", value = "The sample rate in milliseconds"), @Parameter(name = LaunchControl.RESTORE, value = "\"on\" to restore the previous state of Elsinore on startup."), @Parameter(name = "use_owfs", value="Enable or disable OWFS"), @Parameter(name = LaunchControl.OWFS_SERVER, value="OWFS server IP"), @Parameter(name = LaunchControl.OWFS_PORT, value="OWFS port"), @Parameter(name = LaunchControl.SCALE, value="Scale in Celsius or Fahrenheit"), }) public final Response updateSystemSettings() { Map<String, String> params = ParseParams(parameters); if (params.containsKey(LaunchControl.SCALE)) { String scale = params.get(LaunchControl.SCALE); if (scale.equalsIgnoreCase("Celsius") || scale.equalsIgnoreCase("C")) { LaunchControl.setTempScales("C"); } else if (scale.equalsIgnoreCase("Fahrenheit") || scale.equalsIgnoreCase("F")) { LaunchControl.setTempScales("F"); } } if (params.containsKey("recorder")) { boolean recorderOn = params.get("recorder").equals("on"); // If we're on, disable the recorder if (recorderOn) { LaunchControl.enableRecorder(); } else { LaunchControl.disableRecorder(); } } else { LaunchControl.disableRecorder(); } if (params.containsKey("recorderDiff")) { try { StatusRecorder.THRESHOLD = Double.parseDouble(params.get("recorderDiff")); } catch (Exception e) { LaunchControl.setMessage( "Failed to parse Recorder diff as a double\n" + e.getMessage() + LaunchControl.getMessage()); } } if (params.containsKey("recorderTime")) { try { StatusRecorder.SLEEP = Long.parseLong(params.get("recorderTime")); } catch (Exception e) { LaunchControl.setMessage( "Failed to parse Recorder time as a long\n" + e.getMessage() + LaunchControl.getMessage()); } } if (params.containsKey(LaunchControl.RESTORE)) { LaunchControl.setRestore(params.get(LaunchControl.RESTORE).equals("on")); } if (params.containsKey("use_owfs")) { LaunchControl.useOWFS = params.get("use_owfs").equals("on"); } if (params.containsKey(LaunchControl.OWFS_SERVER)) { LaunchControl.owfsServer = params.get(LaunchControl.OWFS_SERVER); } if (params.containsKey(LaunchControl.OWFS_PORT)) { try { LaunchControl.owfsPort = Integer.parseInt(params.get(LaunchControl.OWFS_PORT)); } catch (NumberFormatException nfe) { BrewServer.LOG.log(Level.WARNING, "Failed to parse " + params.get(LaunchControl.OWFS_PORT), nfe); } } LaunchControl.setupOWFS(); LaunchControl.listOWFSTemps(); return new NanoHTTPD.Response(Status.OK, MIME_TYPES.get("text"), "Updated system"); } /** * Set the gravity for the specified device. * @return A response */ @UrlEndpoint(url = "/setgravity", help = "Set the gravity reading for a device to improve the volume calculation", parameters = { @Parameter(name = "device", value = "The Device to set the gravity for"), @Parameter(name = "gravity", value = "The new specific gravity value") }) public final Response setGravity() { Map<String, String> parms = this.parameters; Map<String, String> params = ParseParams(parms); Status status = Status.OK; Temp temp = null; if (params.containsKey("inputunit")) { temp = LaunchControl.findTemp(params.get("inputunit")); if (temp == null) { LaunchControl.addMessage( "Could not find the temperature probe for: " + params.get("inputunit")); status = Status.BAD_REQUEST; } } else { LaunchControl.addMessage( "No device provided when setting the gravity"); status = Status.BAD_REQUEST; } if (params.containsKey("gravity")) { try { BigDecimal gravity = new BigDecimal(params.get("gravity")); if (temp != null) { temp.setGravity(gravity); } } catch (NumberFormatException nfe) { LaunchControl.addMessage( "Could not parse gravity as a decimal: " + params.get("gravity")); } } if (status == Status.BAD_REQUEST) { return null; } return new Response(status, MIME_TYPES.get("text"), "Updated"); } @UrlEndpoint(url="/controller", help = "Get the main controller page HTML", parameters = {}) public Response renderController() { return BrewServer.serveFile("html/index.html", header, rootDir); } @UrlEndpoint(url = "/lockpage", help = "Disable editing of the main controller page", parameters = {}) public Response lockPage() { LaunchControl.pageLock = true; return new NanoHTTPD.Response(Status.OK, BrewServer.MIME_TYPES.get("json"), "{status: \"locked\"}"); } @UrlEndpoint(url = "/unlockpage", help = "Enable editing of the main controller page", parameters = {}) public Response unlockPage() { LaunchControl.listOneWireSys(); LaunchControl.pageLock = false; return new NanoHTTPD.Response(Status.OK, BrewServer.MIME_TYPES.get("json"), "{status: \"unlocked\""); } @UrlEndpoint(url = "/brewerImage.gif", help = "Get the current brewery logo", parameters = {}) public Response getBrewerImage() { String uri = "/brewerImage.gif"; // Has the user uploaded a file? if (new File(rootDir, uri).exists()) { return BrewServer.serveFile(uri, this.header, rootDir); } // Check to see if there's a theme set. if (LaunchControl.theme != null && !LaunchControl.theme.equals("")) { if (new File(rootDir, "/logos/" + LaunchControl.theme + ".gif").exists()) { return BrewServer.serveFile("/logos/" + LaunchControl.theme + ".gif", this.header, rootDir); } } return new NanoHTTPD.Response(Status.BAD_REQUEST, BrewServer.MIME_TYPES.get("json"), "{image: \"unavailable\"}"); } @UrlEndpoint(url = "/updateswitch", help = "Toggle a switch on or off", parameters = {@Parameter(name = "toggle", value = "The name of the switch to toggle")}) public Response updateSwitch() { if (parameters.containsKey("toggle")) { String switchname = parameters.get("toggle"); Switch tempSwitch = LaunchControl.findSwitch(switchname); if (tempSwitch != null) { if (tempSwitch.getStatus()) { tempSwitch.turnOff(); } else { tempSwitch.turnOn(); } return new NanoHTTPD.Response(Status.OK, MIME_HTML, "Updated Switch"); } else { JSONObject usage = new JSONObject(); usage.put("Error", "Invalid name supplied: " + switchname); usage.put("toggle", "The name of the Switch to toggle on/off"); return new Response(Status.BAD_REQUEST, MIME_TYPES.get("json"), usage.toJSONString()); } } return new Response(Status.BAD_REQUEST, MIME_TYPES.get("txt"), "Invalid data" + " provided."); } @UrlEndpoint(url = "/getswitchsettings", help = "Get the settings for a switch", parameters = {@Parameter(name = "name", value = "The name of the switch to get the details for")}) public Response switchSettings() { Status status = Status.BAD_REQUEST; String data = ""; if (parameters.containsKey("name")) { String switchname = parameters.get("name"); Switch tempSwitch = LaunchControl.findSwitch(switchname.replaceAll("_", " ")); if (tempSwitch != null) { JSONObject values = new JSONObject(); values.put("name", tempSwitch.getName()); values.put("gpio", tempSwitch.getGPIO()); values.put("inverted", tempSwitch.getInverted()); data = values.toJSONString(); status = Status.OK; } } return new Response(status, MIME_TYPES.get("json"), data); } @UrlEndpoint(url = "/toggleaux", help = "Toggle the aux pin for a PID", parameters = {@Parameter(name = "toggle", value = "The name of the PID to toggle the pin for")}) public Response toggleAux() { JSONObject usage = new JSONObject(); if (parameters.containsKey("toggle")) { String pidname = parameters.get("toggle"); PID tempPID = LaunchControl.findPID(pidname); if (tempPID != null) { tempPID.toggleAux(); return new NanoHTTPD.Response(Status.OK, MIME_HTML, "Updated Aux for " + pidname); } else { BrewServer.LOG.warning("Invalid PID: " + pidname + " provided."); usage.put("Error", "Invalid name supplied: " + pidname); } } usage.put("toggle", "The name of the PID to toggle the aux output for"); return new Response(usage.toJSONString()); } @UrlEndpoint(url = "/getvolumeform", help = "Get the volume update form for the specified vessel", parameters = { @Parameter(name = "vessel", value = "The name of the vessel to get the Volume form for") }) public Response getVolumeForm() { if (!parameters.containsKey("vessel")) { LaunchControl.setMessage("No Vessel provided"); return null; } // Check to make sure we have a valid vessel Temp temp = LaunchControl.findTemp(parameters.get("vessel")); if (temp == null) { return null; } // Render away VolumeEditForm volEditForm = new VolumeEditForm( temp); HtmlCanvas html = new HtmlCanvas(new PrettyWriter()); String result; try { volEditForm.renderOn(html); result = html.toHtml(); } catch (IOException e) { e.printStackTrace(); result = e.getMessage(); LaunchControl.setMessage(result); } return new Response(Status.OK, MIME_HTML, result); } @UrlEndpoint(url = "/getphsensorform", help = "Get the HTML form for a pH Sensor", parameters ={ @Parameter(name = "sensor", value = "The name of the sensor to get the pH Edit form for") }) public Response getPhSensorForm() { PhSensor phSensor; if (!parameters.containsKey("sensor")) { phSensor = new PhSensor(); } else { phSensor = LaunchControl.findPhSensor(parameters.get("sensor")); } // Render away PhSensorForm phSensorForm = new PhSensorForm(phSensor); HtmlCanvas html = new HtmlCanvas(new PrettyWriter()); String result; try { phSensorForm.renderOn(html); result = html.toHtml(); } catch (IOException e) { e.printStackTrace(); result = e.getMessage(); LaunchControl.setMessage(result); return null; } return new Response(Status.OK, MIME_HTML, result); } @UrlEndpoint(url = "/addphsensor", help = "Add or update a pH Sensor", parameters = { @Parameter(name = "name", value = "The name of the pH Sensor to edit"), @Parameter(name = "dsAddress", value = "The DS2450 Address to use for reading the pH Sensor Analogue value"), @Parameter(name = "dsOffset", value = "The DS2450 Offset to use for reading the pH Sensor Analogue value"), @Parameter(name = "adc_pin", value = "The onboard ADC AIN pin to use for reading the pH Sensor Analogue value"), @Parameter(name = "i2c_model", value = "The I2C ADC model that's used for reading the pH Sensor Analogue value"), @Parameter(name = "i2c_device", value = "The I2C device that's used for reading the pH Sensor Analogue value"), @Parameter(name = "i2c_address", value = "The I2C ADC Address that's used for reading the pH Sensor Analogue value"), @Parameter(name = "i2c_channel", value = "The I2C ADC channel that's used for reading the pH Sensor Analogue value"), @Parameter(name = "ph_model", value = "The pH sensor model that being used"), @Parameter(name = "calibration", value = "The offset for calibration") }) public Response addPhSensor() { PhSensor phSensor = null; String result = ""; Map<String, String> localParams = this.ParseParams(); if (localParams.containsKey("name")) { phSensor = LaunchControl.findPhSensor(localParams.get("name")); } if (phSensor == null) { phSensor = new PhSensor(); phSensor.setName(localParams.get("name")); LaunchControl.phSensorList.add(phSensor); } // Update the pH Sensor String temp = localParams.get("dsAddress"); if (temp != null) { phSensor.setDsAddress(temp); } temp = localParams.get("dsOffset"); if (temp != null) { phSensor.setDsOffset(temp); } temp = localParams.get("adc_pin"); if (temp != null) { int newPin = -1; if (!temp.equals("")) { try { newPin = Integer.parseInt(temp); } catch (NumberFormatException nfe) { LaunchControl.setMessage( "Couldn't parse analog pin " + temp + " as an integer."); } } phSensor.setAinPin(newPin); } String i2cModel = localParams.get("i2c_model"); if (i2cModel != null && i2cModel.length() > 0) { String devNumber = localParams.get("i2c_device"); String devAddress = localParams.get("i2c_address"); String devChannel = localParams.get("i2c_channel"); phSensor.i2cDevice = LaunchControl.getI2CDevice(devNumber, devAddress, i2cModel); phSensor.i2cChannel = Integer.parseInt(devChannel); } temp = localParams.get("ph_model"); if (temp != null) { phSensor.setModel(temp); } // Check for a calibration temp = localParams.get("calibration"); if (temp != null && !temp.equals("")) { try { BigDecimal calibration = new BigDecimal(temp); phSensor.calibrate(calibration); } catch (Exception e) { LaunchControl.setMessage("Could not read calibration: " + temp + ", " + e.getLocalizedMessage()); } } return new Response(Status.OK, MIME_HTML, result); } @UrlEndpoint(url = "/delphsensor", help = "Remove a pH Sensor", parameters = { @Parameter(name = "name", value = "The name of the pH Sensor to delete") }) public Response delPhSensor() { String sensorName = parameters.get("name"); if (sensorName != null && LaunchControl.deletePhSensor(sensorName)) { LaunchControl.setMessage("pH Sensor '" + sensorName + "' deleted."); } else { LaunchControl.setMessage("Could not find pH Sensor with name: " + sensorName); } return new Response(Status.OK, MIME_HTML, ""); } /** * Update the specified pH Sensor reading. * @return a Response with the pH Sensor value. */ @UrlEndpoint(url = "/readphsensor", help = "Get the current value of a pH Sensor", parameters = {@Parameter(name = "name", value = "The name of the pH Sensor to get the value for")}) public final Response readPhSensor() { PhSensor phSensor = LaunchControl.findPhSensor( parameters.get("name").replace(" ", "_")); // CHeck for errors String result; if (phSensor == null) { result = "Couldn't find phSensor"; } else { result = phSensor.calcPhValue().toPlainString(); } // return the value return new Response(Status.OK, MIME_HTML, result); } /** * Get the trigger input form for the trigger type specified. * @return The HTML Representing the trigger form. */ @UrlEndpoint(url = "/getTriggerForm", help = "Get the trigger edit form for the specified trigger type at the position", parameters = { @Parameter(name = "position", value = "The integer position of the trigger to edit/add"), @Parameter(name = "type", value = "The trigger type to get the edit/add form for.") }) public final Response getTriggerForm() { int position = Integer.parseInt(parameters.get("position")); String type = parameters.get("type"); HtmlCanvas result = TriggerControl.getNewTriggerForm(position, type); if (result == null) { return null; } return new Response(Status.OK, MIME_HTML, result.toHtml()); } /** * Get the form representing the new triggers. * @return The Response with the HTML. */ @UrlEndpoint(url = "/getNewTriggers", help = "Get a form representing the new trigger types available for the temperature probe", parameters = {@Parameter(name = "temp", value = "The name of the temperature probe/device to get the new trigger form for")}) public final Response getNewTriggersForm() { Status status = Status.OK; HtmlCanvas htmlCanvas; String probe = parameters.get("temp"); if (probe == null) { LaunchControl.setMessage("No Temperature probe set."); } try { htmlCanvas = TriggerControl.getNewTriggersForm(probe); } catch (IOException ioe) { LaunchControl.setMessage( "Failed to get the new triggers form: " + ioe.getLocalizedMessage()); status = Status.BAD_REQUEST; htmlCanvas = new HtmlCanvas(); } return new Response(status, MIME_HTML, htmlCanvas.toHtml()); } /** * Get the trigger edit form for the specified parameters. * @return The Edit form. */ @UrlEndpoint(url = "/gettriggeredit", help = "Edit the trigger at the specified position for the temp probe", parameters = { @Parameter(name = "position", value = "The position in the trigger control to edit"), @Parameter(name = "tempprobe", value = "The temperature probe to edit.") }) public final Response getTriggerEditForm() { Status status = Status.OK; int position = Integer.parseInt(parameters.get("position")); String tempProbeName = parameters.get("tempprobe"); Temp tempProbe = LaunchControl.findTemp(tempProbeName); if (tempProbe == null) { return null; } TriggerControl triggerControl = tempProbe.getTriggerControl(); HtmlCanvas canvas = triggerControl.getEditTriggerForm( position, new JSONObject(parameters)); if (canvas != null) { return new Response(status, MIME_HTML, canvas.toHtml()); } return new Response(Status.BAD_REQUEST, MIME_HTML, "BAD"); } /** * Add a new Trigger to the incoming tempProbe object. * @return A response for the user. */ @UrlEndpoint(url = "/addtriggertotemp", help = "Add a trigger to the temperature probe trigger control", parameters = { @Parameter(name = "position", value = "The position to add the trigger in"), @Parameter(name = "type", value = "The type of trigger to add"), @Parameter(name = "tempprobe", value = "The name of the temperature probe to add the trigger control to."), @Parameter(name = "<Other Values", value = "The values specified by the trigger edit form.") }) public final Response addNewTrigger() { Status status = Status.OK; int position = Integer.parseInt(parameters.get("position")); String type = parameters.get("type"); String tempProbeName = parameters.get("tempprobe"); Temp tempProbe = LaunchControl.findTemp(tempProbeName); if (tempProbe == null) { return null; } TriggerControl triggerControl = tempProbe.getTriggerControl(); if (triggerControl == null) { return null; } triggerControl.addTrigger(position, type, new JSONObject(parameters)); return new Response(status, MIME_HTML, "OK"); } /** * Update the trigger. * @return The Edit form. */ @UrlEndpoint(url = "/updatetrigger", help = "Update the trigger at the specified position", parameters = { @Parameter(name = "position", value = "The position to edit the trigger at."), @Parameter(name = "tempprobe", value = "The temperature probe to edit the trigger on."), @Parameter(name = "<Other Values>", value = "Other values requested by the trigger type.") }) public final Response updateTrigger() { Status status = Status.OK; int position = Integer.parseInt(parameters.get("position")); String tempProbeName = parameters.get("tempprobe"); Temp tempProbe = LaunchControl.findTemp(tempProbeName); if (tempProbe == null) { return null; } TriggerControl triggerControl = tempProbe.getTriggerControl(); if (triggerControl == null) { return null; } if (!triggerControl.updateTrigger(position, new JSONObject(parameters))) { return null; } return new Response(status, MIME_HTML, "OK"); } /** * Reorder the devices in the UI. * @return A response object. */ @UrlEndpoint(url = "/reorderprobes", help = "Re-order the temperature probes", parameters = {@Parameter(name = "<name of the device", value = "<New integer position of the device>")}) public final Response reorderProbes() { Map<String, String> params = this.parameters; JSONObject usage = new JSONObject(); usage.put("Usage", "Set the order for the probes."); usage.put(":name=:position", "The name and the new position"); // Should be good to go, iterate and update! for (Map.Entry<String, String> mEntry: params.entrySet()) { if (mEntry.getKey().equals("NanoHttpd.QUERY_STRING")) { continue; } try { String tName = mEntry.getKey(); int newPos = Integer.parseInt(mEntry.getValue()); Temp temp = LaunchControl.findTemp(tName); if (temp != null) { temp.setPosition(newPos); } } catch (NumberFormatException nfe) { LaunchControl.setMessage( "Failed to parse device reorder value," + " things may get weird: " + mEntry.getKey() + ": " + mEntry.getValue()); } } LaunchControl.sortDevices(); return new Response(Status.OK, MIME_TYPES.get("json"), usage.toJSONString()); } @SuppressWarnings("unchecked") @UrlEndpoint(url = "/uploadbeerxml", help = "Upload beerXML for parsing", parameters = {@Parameter(name = "<files>", value = "Files to upload")}) public Response uploadBeerXML() { final Map<String, String> files = this.files; Status status = Status.ACCEPTED; if (files.size() == 1) { for (Map.Entry<String, String> entry : files.entrySet()) { try { File uploadedFile = new File(entry.getValue()); String fileType = new Tika().detect( uploadedFile); if (fileType.endsWith("/xml")) { BeerXMLReader.getInstance().readFile(uploadedFile); ArrayList<String> recipeList = BeerXMLReader.getInstance().getListOfRecipes(); BrewServer.setRecipeList(recipeList); if (recipeList == null) { LaunchControl.setMessage("Couldn't read recipe file."); } else if (recipeList.size() == 1) { BrewServer.setCurrentRecipe(BeerXMLReader.getInstance().readRecipe(recipeList.get(0))); LaunchControl.setMessage("A single recipe has been read in and set: " + BrewServer.getCurrentRecipe().getName()); } else { LaunchControl.setMessage(recipeList.size() + " recipes read in."); } } } catch (IOException e) { return null; } catch (XPathException e) { e.printStackTrace(); } } } return new Response(status, MIME_TYPES.get("json"), ""); } @UrlEndpoint(url = "/getrecipelist", help = "Get the list of recipes that Elsinore has read in", parameters = {}) public Response getRecipeList() { HtmlCanvas html = new HtmlCanvas(new PrettyWriter()); try { new RecipeListForm().renderOn(html); } catch (IOException e) { e.printStackTrace(); } return new Response(Status.OK, MIME_HTML, html.toHtml()); } @UrlEndpoint(url = "/showrecipe", help = "Show the recipe HTML for a specified recipe", parameters = {@Parameter(name = "recipeName", value = "The name of the recipe to show")}) public Response showRecipe(){ String recipeName = this.parameters.get("recipeName"); String renderSection = this.parameters.get("subset"); if (recipeName == null && BrewServer.getCurrentRecipe() == null) { return new Response(Status.BAD_REQUEST, MIME_HTML, "No recipe name provided"); } Recipe recipe = null; if (recipeName != null && !recipeName.equals("")) { try { recipe = BeerXMLReader.getInstance().readRecipe(recipeName); } catch (XPathException e) { e.printStackTrace(); } } else { recipe = BrewServer.getCurrentRecipe(); } if (recipe == null) { return new Response(Status.BAD_REQUEST, MIME_HTML, "Could not find recipe: " + recipeName); } HtmlCanvas html = new HtmlCanvas(new PrettyWriter()); try { RecipeViewForm recipeForm = new RecipeViewForm(recipe); if (renderSection != null) { html.macros().stylesheet("/bootstrap-v4/css/bootstrap.min.css"); html.div(id("recipeView").class_("text-center")); switch (renderSection) { case "mash": recipeForm.renderMash(html); break; case "hops": recipeForm.renderHops(html); break; case "fermentation": recipeForm.renderFermentation(html); break; case "dry": recipeForm.renderDryHops(html); break; } html._div(); } else { recipeForm.renderOn(html); } } catch (IOException e) { e.printStackTrace(); } return new Response(Status.OK, MIME_HTML, html.toHtml()); } @UrlEndpoint(url="/setprofile", help = "Set the profile for a PID from the recipe, make sure the current recipe is set first.", parameters = { @Parameter(name = "profile", value = "The name of the profile to set from the recipe."), @Parameter(name = "tempprobe", value = "The name of the temperature probe to set the profile for.") } ) public Response setProfile() { String profile = parameters.get("profile"); if (profile == null || profile.equals("")) { LaunchControl.setMessage("No profile provided"); return null; } String tempProbe = parameters.get("tempprobe"); if (tempProbe == null || tempProbe.equals("")) { LaunchControl.setMessage("No temperature probe provided"); return null; } Temp temp = LaunchControl.findTemp(tempProbe); if (temp == null) { LaunchControl.setMessage("Couldn't find temp probe: " + tempProbe); return null; } if (BrewServer.getCurrentRecipe() == null) { LaunchControl.setMessage("No recipe selected"); return null; } if (profile.equalsIgnoreCase("mash")) { BrewServer.getCurrentRecipe().setMashProfile(temp); } if (profile.equalsIgnoreCase("boil")) { BrewServer.getCurrentRecipe().setBoilHops(temp); } if (profile.equalsIgnoreCase("ferm")) { BrewServer.getCurrentRecipe().setFermProfile(temp); } if (profile.equalsIgnoreCase("dry")) { BrewServer.getCurrentRecipe().setDryHops(temp); } LaunchControl.setMessage("Set " + profile + " profile for " + tempProbe); return new Response(Status.OK, MIME_HTML, "Set " + profile + " profile for " + tempProbe); } /** * Clear the notification. * @return A response object indicating whether the notification is cleared OK. */ @UrlEndpoint(url="/clearnotification", help = "Clear the current notification", parameters = {@Parameter(name = "notification", value = "The notification to clear.")}) public Response clearNotification() { String rInt = parameters.get("notification"); if (rInt == null) { BrewServer.LOG.warning("No notification position supplied to clear"); return null; } try { int position = Integer.parseInt(rInt); if (!Notifications.getInstance().clearNotification(position)) { BrewServer.LOG.warning("Failed to clear notification: " + position); return null; } } catch (NumberFormatException ne) { BrewServer.LOG.warning("No notification position supplied to clear: " + rInt); return null; } return new Response(Status.OK, MIME_HTML, "Cleared notification: " + rInt); } @UrlEndpoint(url="/resetrecorder", help = "Reset the recorder data, store the old data, and create a new start point.", parameters = {}) public Response resetRecorder() { LaunchControl.disableRecorder(); LaunchControl.enableRecorder(); return new Response(Status.OK, MIME_HTML, "Reset recorder."); } @UrlEndpoint(url="/deletegraphdata", help = "Delete the current recorder data.", parameters = {}) public Response deleteGraphData() { if (LaunchControl.recorderEnabled) { StatusRecorder temp = LaunchControl.disableRecorder(); if (temp == null) { return null; } Response response = temp.deleteAllData(); LaunchControl.enableRecorder(); return response; } return new Response("Recorder not enabled"); } @UrlEndpoint(url="/deleteprobe", help = "Delete the PID/Temp probe specified", parameters = {@Parameter(name = "probe", value = "The name of the device to delete")}) public Response deleteTempProbe() { String probeName = parameters.get("probe"); Status status = Status.OK; if (probeName == null) { return null; } else { Temp tempProbe = LaunchControl.findTemp(probeName); if (tempProbe == null) { return null; } else { PID pid = LaunchControl.findPID(probeName); if (pid != null) { LaunchControl.deletePID(pid); } LaunchControl.deleteTemp(tempProbe); } } Response response = new Response(""); response.setStatus(status); return response; } @UrlEndpoint(url="/shutdownSystem", help = "Shutdown Elsinore or turn off the system", parameters = {@Parameter(name = "turnoff", value = "true: turn off the entire system, false: shutdown Elsinore only")}) public Response shutdownSystem() { try { LaunchControl.saveEverything(); boolean shutdownEverything = Boolean.parseBoolean(parameters.get("turnoff")); if (shutdownEverything) { // Shutdown the system using shutdown now Runtime runtime = Runtime.getRuntime(); runtime.exec("shutdown -h now"); } System.exit(0); } catch (Exception e) { e.printStackTrace(); BrewServer.LOG.warning("Failed to shutdown. " + e.getMessage()); } return new Response("Shutdown called"); } @UrlEndpoint(url="/toggletimer", help="Start, stop or reset a timer", parameters = {@Parameter(name="start", value="name of the timer to start/pause"), @Parameter(name="reset", value = "name of the timer to reset")}) public Response toggleTimer() { Status status = Status.BAD_REQUEST; String message = "Bad request"; if (parameters.containsKey("toggle")) { String name = parameters.get("toggle"); Timer timer = LaunchControl.findTimer(name); if (timer != null) { timer.startTimer(); status = Status.OK; message = "Timer started/paused"; } else { status = Status.BAD_REQUEST; message = "Couldn't find the timer " + name; } } if (parameters.containsKey("reset")) { String name = parameters.get("reset"); Timer timer = LaunchControl.findTimer(name); if (timer != null) { timer.resetTimer(); status = Status.OK; message = "Timer reset"; } else { status = Status.BAD_REQUEST; message = "Couldn't find the timer " + name; } } return new Response(status, MIME_TYPES.get("txt"), message); } @UrlEndpoint(url="/getTimerSettings", help="Get the settings for the current timer.", parameters = {@Parameter(name="timer", value="The name of the timer to get the settings for")}) public Response getTimerSettings() { String timerName = this.parameters.get("timer"); if (timerName == null || timerName.length() == 0) { return new Response(Status.BAD_REQUEST, MIME_TYPES.get("txt"), "No timer name provided."); } Timer timer = LaunchControl.findTimer(timerName); JSONObject timerSettings = new JSONObject(); if (timer != null) { timerSettings.put("name", timer.getName()); timerSettings.put("duration", timer.getTarget()); timerSettings.put("inverted", timer.getInverted()); } return new Response(Status.OK, MIME_TYPES.get("json"), timerSettings.toJSONString()); } @UrlEndpoint(url="/clearbeerxml", help="Clear the currently loaded BeerXML", parameters = {}) public Response clearBeerXML() { BrewServer.getRecipeList().clear(); BrewServer.setCurrentRecipe(null); return new Response(Status.OK, MIME_TYPES.get("txt"), "Cleared BeerXML"); } }