package org.bensteele.jirrigate;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;
import java.util.TreeSet;
import jline.console.ConsoleReader;
import jline.console.completer.Completer;
import jline.console.completer.StringsCompleter;
import org.apache.commons.lang.ArrayUtils;
import org.bensteele.jirrigate.controller.Controller;
import org.bensteele.jirrigate.controller.IrrigationResult;
import org.bensteele.jirrigate.controller.zone.Zone;
import org.bensteele.jirrigate.weather.WeatherStation;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
/**
* A simple class to provide an interactive console/shell to jirrigate using jline2.
*
* @author Ben Steele (ben@bensteele.org)
*/
public class Console {
private final Irrigator irrigator;
private final Set<String> commands = new TreeSet<String>();
private final ConsoleReader reader;
private Completer completer = new StringsCompleter();
public Console(Irrigator irrigator) throws IOException {
this.irrigator = irrigator;
this.reader = new ConsoleReader();
}
private void activateController(String input) {
for (Controller c : irrigator.getControllers()) {
if (input.toLowerCase().contains(c.getName().toLowerCase())) {
System.out.print("Activating " + c.getName() + ": ");
c.setActive(true);
System.out.println("OK");
}
}
}
private void activateWeatherStation(String input) {
for (WeatherStation ws : irrigator.getWeatherStations()) {
if (input.toLowerCase().contains(ws.getName().toLowerCase())) {
System.out.print("Activating " + ws.getName() + ": ");
ws.setActive(true);
System.out.println("OK");
}
}
}
private void deactivateController(String input) {
for (Controller c : irrigator.getControllers()) {
if (input.toLowerCase().contains(c.getName().toLowerCase())) {
System.out.print("Deactivating " + c.getName() + ": ");
c.setActive(false);
System.out.println("OK");
}
}
}
private void deactivateWeatherStation(String input) {
for (WeatherStation ws : irrigator.getWeatherStations()) {
if (input.toLowerCase().contains(ws.getName().toLowerCase())) {
System.out.print("Deactivating " + ws.getName() + ": ");
ws.setActive(false);
System.out.println("OK");
}
}
}
private void printControllerInfo(String input) {
System.out.format("%-25s%-20s%-20s%-10s%-15s%-15s%-10s\n", "Controller", "Type", "IP", "Port",
"Username", "Password", "# Zones");
for (Controller c : irrigator.getControllers()) {
if (input.toLowerCase().contains(c.getName().toLowerCase())) {
System.out.format("%-25s%-20s%-20s%-10s%-15s%-15s%-10s\n", c.getName(),
c.getControllerType(), c.getIpAddress().getHostAddress(), c.getPort(), c.getUsername(),
c.getPassword(), c.getZones().size());
}
}
}
private void printControllerInfoAll(String input) {
System.out.format("%-25s%-20s%-20s%-10s%-15s%-15s%-10s\n", "Controller", "Type", "IP", "Port",
"Username", "Password", "# Zones");
for (Controller c : irrigator.getControllers()) {
System.out.format("%-25s%-20s%-20s%-10s%-15s%-15s%-10s\n", c.getName(),
c.getControllerType(), c.getIpAddress().getHostAddress(), c.getPort(), c.getUsername(),
c.getPassword(), c.getZones().size());
}
}
private void printControllerStatus(String input) {
DateTimeFormatter statusFormatter = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm");
System.out.format("%-25s%-20s%-25s%-10s%-15s%-28s\n", "Controller", "Status",
"Currently Irrigating", "Active", "# Irrigations", "Next Irrigation Due");
for (Controller c : irrigator.getControllers()) {
if (input.toLowerCase().contains(c.getName().toLowerCase())) {
System.out.format("%-25s%-20s%-25s%-10s%-15s%-28s\n", c.getName(), c.getStatus(),
c.isIrrigating(), c.isActive(), c.getIrrigationResults().size(),
statusFormatter.print(irrigator.nextIrrigationAt()));
}
}
}
private void printControllerStatusAll(String input) {
DateTimeFormatter statusFormatter = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm");
System.out.format("%-25s%-20s%-25s%-10s%-15s%-28s\n", "Controller", "Status",
"Currently Irrigating", "Active", "# Irrigations", "Next Irrigation Due");
for (Controller c : irrigator.getControllers()) {
System.out.format("%-25s%-20s%-25s%-10s%-15s%-28s\n", c.getName(), c.getStatus(),
c.isIrrigating(), c.isActive(), c.getIrrigationResults().size(),
statusFormatter.print(irrigator.nextIrrigationAt()));
}
}
private void printControllerZones(String input) {
for (Controller c : irrigator.getControllers()) {
if (input.toLowerCase().contains(c.getName().toLowerCase())) {
System.out.format("%-30s%-6s%-6s\n", "Name", "Id", "Duration");
for (Zone z : c.getZones()) {
String durationAmount = "";
if (z.getDuration() < 60) {
durationAmount = z.getDuration() + "s";
} else {
durationAmount = (z.getDuration() / 60) + "m";
}
System.out.format("%-30s%-6s%-6s\n", z.getName(), z.getId(), durationAmount);
}
}
}
}
private void printHelpMenu() {
System.out.println("Available commands are:\n");
for (String command : commands) {
System.out.println(command);
}
}
private void printIrrigationResults(String input) {
for (Controller c : irrigator.getControllers()) {
if (input.toLowerCase().contains(c.getName().toLowerCase())) {
String[] amountString = input.split("results last ");
int duration = Integer.parseInt(amountString[1]);
IrrigationResult[] results = (IrrigationResult[]) c.getIrrigationResults().toArray();
ArrayUtils.reverse(results);
for (int i = 0; i < duration; i++) {
// Don't let the array go out of bounds.
if (i == results.length) {
break;
}
System.out.println("\n" + results[i]);
}
}
}
}
private void printVersion() {
System.out.println("v" + irrigator.getVersion() + " by Ben Steele.\n");
}
private void printWeatherStationInfo(String input) {
DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm");
System.out.format("%-25s%-15s%-10s%-12s%-20s%-20s\n", "Station", "Type", "Active", "# Records",
"Oldest Record", "Newest Record");
for (WeatherStation ws : irrigator.getWeatherStations()) {
if (input.toLowerCase().contains(ws.getName().toLowerCase())) {
System.out.format("%-25s%-15s%-10s%-12s%-20s%-20s\n", ws.getName(), ws.getType(),
ws.isActive(), ws.getNumberOfRecords(), formatter.print(ws.getOldestRecordTime()),
formatter.print(ws.getNewestRecordTime()));
}
}
}
private void printWeatherMultiplier(String input) {
System.out.println("Multiplier Value: " + irrigator.getWeatherMultiplierValue());
System.out.println("Multiplier Max Temp: " + irrigator.getWeatherMultiplierMaxTemp() + "C");
System.out.println("Multiplier Days To Look Ahead: "
+ irrigator.getWeatherMultiplierDaysToLookAhead());
System.out.println("Currently Triggered?: "
+ ((irrigator.getWeatherMultiplier() != 1.0) ? true : false));
}
private void printWeatherStationStatus(String input) {
for (WeatherStation ws : irrigator.getWeatherStations()) {
if (input.toLowerCase().contains(ws.getName().toLowerCase())) {
System.out.println("Station: " + ws.getName());
System.out.println("Record: " + ws.getStatus());
System.out.println("Current Temp (C): " + ws.getCurrentTemperatureCelcius());
System.out.println("Today's Max Temp (C): " + ws.getTodaysMaxTemperatureCelcius());
System.out.println("Today's Min Temp (C): " + ws.getTodaysMinTemperatureCelcius());
System.out.println("Current Temp (F): " + ws.getCurrentTemperatureFahrenheit());
System.out.println("Today's Max Temp (F): " + ws.getTodaysMaxTemperatureFahrenheit());
System.out.println("Today's Min Temp (F): " + ws.getTodaysMinTemperatureFahrenheit());
System.out.println("Avg 7-day Temp (C): " + ws.getLastXDaysAvgTemperatureCelcius(7));
System.out.println("Avg 7-day Temp (F): " + ws.getLastXDaysAvgTemperatureFahrenheit(7));
System.out.println("Today's Rainfall (mm): " + ws.getTodaysRainfallMilliLitres());
System.out.println("Today's Rainfall (in): " + ws.getTodaysRainfallInches());
System.out.println("Last 7-day Rainfall (mm): " + ws.getLastXDaysRainfallMilliLitres(7));
System.out.println("Last 7-day Rainfall (in): " + ws.getLastXDaysRainfallInches(7));
System.out.println("Humidity (%): " + ws.getCurrentRelativeHumidityPercentage());
System.out.println("Current Wind (kph): " + ws.getCurrentWindspeedKiloMetresPerHour());
System.out.println("Current Wind (mph): " + ws.getCurrentWindspeedMilesPerHour());
System.out.println("PoP 1-day (%): " + ws.getNextXDaysPercentageOfPrecipitation(1));
System.out.println("PoP 3-day (%): " + ws.getNextXDaysPercentageOfPrecipitation(3));
System.out.println("PoP 7-day (%): " + ws.getNextXDaysPercentageOfPrecipitation(7));
}
}
}
private void processConsoleInput(String input) {
input = input.trim();
if (input.matches("activate controller.*")) {
activateController(input);
}
else if (input.matches("deactivate controller.*")) {
deactivateController(input);
}
else if (input.matches("activate weatherstation.*")) {
activateWeatherStation(input);
}
else if (input.matches("deactivate weatherstation.*")) {
deactivateWeatherStation(input);
}
else if (input.matches("reload controller configuration")) {
try {
irrigator.reloadConfigurationFromFile();
irrigator.processControllerConfiguration();
} catch (Exception e) {
System.out.println("Error re-loading configuration due to: " + e.getMessage());
}
addAutoCompleteCommands(reader);
System.out.println("Configuration has been reloaded.");
}
else if (input.matches("reload watering day/time configuration")) {
try {
irrigator.reloadConfigurationFromFile();
irrigator.processWateringDaysConfiguration();
irrigator.processWateringStartTimeConfiguration();
} catch (Exception e) {
System.out.println("Error re-loading configuration due to: " + e.getMessage());
}
addAutoCompleteCommands(reader);
System.out.println("Configuration has been reloaded.");
}
else if (input.matches("reload weatherstation configuration")) {
try {
irrigator.reloadConfigurationFromFile();
irrigator.processWeatherStationConfiguration();
} catch (Exception e) {
System.out.println("Error re-loading configuration due to: " + e.getMessage());
}
addAutoCompleteCommands(reader);
System.out.println("Configuration has been reloaded.");
}
else if (input.matches("reload weather multiplier configuration")) {
try {
irrigator.reloadConfigurationFromFile();
irrigator.processWeatherMultiplierConfiguration();
} catch (Exception e) {
System.out.println("Error re-loading configuration due to: " + e.getMessage());
}
addAutoCompleteCommands(reader);
System.out.println("Configuration has been reloaded.");
}
else if (input.matches("reload weather threshold configuration")) {
try {
irrigator.reloadConfigurationFromFile();
irrigator.processWeatherThresholdConfiguration();
} catch (Exception e) {
System.out.println("Error re-loading configuration due to: " + e.getMessage());
}
addAutoCompleteCommands(reader);
System.out.println("Configuration has been reloaded.");
}
else if (input.matches("show controller all info")) {
printControllerInfoAll(input);
}
else if (input.matches("show controller.*info")) {
printControllerInfo(input);
}
else if (input.matches("show controller all status")) {
printControllerStatusAll(input);
}
else if (input.matches("show controller.*results last \\d")) {
printIrrigationResults(input);
}
else if (input.matches("show controller.*status")) {
printControllerStatus(input);
}
else if (input.matches("show controller.*zones")) {
printControllerZones(input);
}
else if (input.matches("show license")) {
printLicense();
}
else if (input.matches("show version")) {
printVersion();
}
else if (input.matches("show weather multiplier")) {
printWeatherMultiplier(input);
}
else if (input.matches("show weatherstation.*info")) {
printWeatherStationInfo(input);
}
else if (input.matches("show weatherstation.*status")) {
printWeatherStationStatus(input);
}
else if (input.matches("start irrigation all")) {
startAllIrrigation();
}
else if (input.matches("stop irrigation all")) {
stopAllIrrigation();
}
else if (input.matches("test controller.*zone.*duration \\d+")) {
testZone(input);
}
else {
printHelpMenu();
}
}
private void printLicense() {
String gnuV3License = "Jirrigate - A Java based irrigation control system.\n"
+ "Copyright (C) 2013 Ben Steele\n" + "https://github.com/bensteele/jirrigate\n\n"
+ "This program is free software: you can redistribute it and/or modify\n"
+ "it under the terms of the GNU General Public License as published by\n"
+ "the Free Software Foundation, either version 3 of the License, or\n"
+ "(at your option) any later version.\n" + "\n"
+ "This program is distributed in the hope that it will be useful,\n"
+ "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
+ "GNU General Public License for more details.\n" + "\n"
+ "You should have received a copy of the GNU General Public License\n"
+ "along with this program. If not, see [http://www.gnu.org/licenses/].";
System.out.println(gnuV3License);
}
private void startAllIrrigation() {
for (Controller c : irrigator.getControllers()) {
System.out.print("Starting default irrigation for " + c.getName() + ": ");
c.irrigationRequest(c.generateDefaultIrrigationRequest());
System.out.println("OK");
}
}
private void addAutoCompleteCommands(ConsoleReader reader) {
// Clear any old auto-complete commands.
commands.clear();
reader.removeCompleter(completer);
// Add the controllers to the auto-complete
for (Controller c : irrigator.getControllers()) {
commands.add("show controller " + c.getName() + " info");
commands.add("show controller " + c.getName() + " results last <x>");
commands.add("show controller " + c.getName() + " status");
commands.add("show controller " + c.getName() + " zones");
commands.add("deactivate controller " + c.getName());
commands.add("activate controller " + c.getName());
}
commands.add("show controller all info");
commands.add("show controller all status");
// Add zone testing
for (Controller c : irrigator.getControllers()) {
for (Zone z : c.getZones()) {
commands.add("test controller " + c.getName() + " zone " + z.getName()
+ " duration <seconds> ");
}
}
// Add the weather stations to the auto-complete
for (WeatherStation ws : irrigator.getWeatherStations()) {
commands.add("show weatherstation " + ws.getName() + " info");
commands.add("show weatherstation " + ws.getName() + " status");
commands.add("deactivate weatherstation " + ws.getName());
commands.add("activate weatherstation " + ws.getName());
}
// Add weather multiplier commands to the auto-complete
commands.add("show weather multiplier");
// Add irrigator specific commands to the auto-complete
commands.add("reload controller configuration");
commands.add("reload watering day/time configuration");
commands.add("reload weatherstation configuration");
commands.add("reload weather multiplier configuration");
commands.add("reload weather threshold configuration");
commands.add("stop irrigation all");
commands.add("start irrigation all");
commands.add("show license");
commands.add("show version");
completer = new StringsCompleter(commands);
reader.addCompleter(completer);
}
public void startConsole() throws IOException {
reader.setPrompt("jirrigate> ");
addAutoCompleteCommands(reader);
String line;
PrintWriter out = new PrintWriter(reader.getOutput(), true);
while ((line = reader.readLine()) != null) {
if (line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("exit")) {
out.println("Goodbye!");
System.exit(0);
}
processConsoleInput(line);
}
}
private void stopAllIrrigation() {
for (Controller c : irrigator.getControllers()) {
System.out.print("Stopping " + c.getName() + ": ");
if (c.stopIrrigation()) {
System.out.println("OK");
} else {
System.out.println("FAIL");
}
}
}
private void testZone(String input) {
for (Controller c : irrigator.getControllers()) {
if (input.toLowerCase().contains(c.getName().toLowerCase())) {
for (Zone z : c.getZones()) {
if (input.toLowerCase().contains(z.getName().toLowerCase())) {
String[] durationString = input.split("duration ");
long duration = Long.parseLong(durationString[1]);
System.out.println("Testing " + c.getName() + " zone " + z.getName() + " for "
+ duration + " seconds");
c.testZone(z, duration);
}
}
}
}
}
}