diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF new file mode 100644 index 0000000..c8d40b5 --- /dev/null +++ b/META-INF/MANIFEST.MF @@ -0,0 +1,19 @@ +Manifest-Version: 1.0 +Class-Path: jackson-annotations-2.9.0.jar jetty-server-9.4.8.v20171121 + .jar google-oauth-client-jetty-1.23.0.jar httpcore-4.4.10.jar google- + http-client-1.23.0.jar jetty-xml-9.4.8.v20171121.jar jetty-client-9.4 + .8.v20171121.jar commons-logging-1.2.jar jetty-util-9.4.8.v20171121.j + ar google-http-client-jackson2-1.23.0.jar google-oauth-client-java6-1 + .23.0.jar jackson-databind-2.9.5.jar zway-lib-0.2.9-SNAPSHOT.jar jett + y-security-9.4.8.v20171121.jar guava-jdk5-17.0.jar gson-2.8.1.jar slf + 4j-simple-1.7.25.jar javax.servlet-api-3.1.0.jar jetty-webapp-9.4.8.v + 20171121.jar jsr305-1.3.9.jar jetty-io-9.4.8.v20171121.jar websocket- + client-9.4.8.v20171121.jar sqlite-jdbc-3.21.0.1.jar websocket-api-9.4 + .8.v20171121.jar spark-core-2.7.2.jar httpclient-4.5.6.jar libai-1.6. + 12.jar google-oauth-client-1.23.0.jar websocket-server-9.4.8.v2017112 + 1.jar jetty-servlet-9.4.8.v20171121.jar google-api-client-1.23.0.jar + jetty-http-9.4.8.v20171121.jar websocket-common-9.4.8.v20171121.jar w + ebsocket-servlet-9.4.8.v20171121.jar commons-codec-1.10.jar jackson-c + ore-2.9.5.jar slf4j-api-1.7.25.jar +Main-Class: main.Main + diff --git a/src/main/java/device/Fitbit.java b/src/main/java/device/Fitbit.java index 6bde0e6..8a67048 100644 --- a/src/main/java/device/Fitbit.java +++ b/src/main/java/device/Fitbit.java @@ -119,7 +119,7 @@ public class Fitbit { if (now.compareTo(ago) < 0) ago = "00:00"; - heart = update(HeartRate.class, heart,"1" + USER + "activities/heart/date/today/1d/1sec/time/" + ago + "/" + now + ".json"); + heart = update(HeartRate.class, null,"1" + USER + "activities/heart/date/today/1d/1sec/time/" + ago + "/" + now + ".json"); return heart.getAverage(); } @@ -154,7 +154,7 @@ public class Fitbit { * Altrimenti viene ritornata la variabile passata * * @param varClass la classe della variabile passata - * @param variable la variabile che vede fare l'update + * @param variable la variabile che deve fare l'update (passando null si forza la richiesta) * @param url l'url da cui prende i dati aggiornati * @return la variabile aggiornata */ @@ -164,7 +164,7 @@ public class Fitbit { long latest = latestRequest.get(varClass); // don't update - if(current - latest < MINUTE * 5) + if( (variable!=null) && (current - latest < MINUTE * 5) ) return variable; } catch (NullPointerException e) { // do nothing and update diff --git a/src/main/java/device/Hue.java b/src/main/java/device/Hue.java index 647ec99..5e1148f 100644 --- a/src/main/java/device/Hue.java +++ b/src/main/java/device/Hue.java @@ -62,10 +62,9 @@ public class Hue { /** * Cerca le luci Philips Hue a ll'indirizzo http://172.30.1.138/api/C0vPwqjJZo5Jt9Oe5HgO6sBFFMxgoR532IxFoGmx/lights/ + * @throws NullPointerException se non trova nessun bridge */ - public Hue () { - this("172.30.1.138", "C0vPwqjJZo5Jt9Oe5HgO6sBFFMxgoR532IxFoGmx"); - } + public Hue () throws NullPointerException { this("172.30.1.138", "C0vPwqjJZo5Jt9Oe5HgO6sBFFMxgoR532IxFoGmx"); } /** * Cerca le luci Philips Hue nell'indirizzo specificato e con l'utente specificato.
@@ -73,11 +72,15 @@ public class Hue { * (per ora fa una media e poi assegna il valore risultante a tutte) * @param ip l'indirizzo IP (seguito dalla porta se e' diversa dalla solita 8000) * @param user l'utente + * @throws NullPointerException se non trova nessun bridge */ - public Hue(String ip, String user) { + public Hue(String ip, String user) throws NullPointerException { lightsURL = "http://" + ip + "/api/" + user + "/lights/"; allLights = (Map>)Rest.get(lightsURL); + if(allLights.isEmpty()) + throw new NullPointerException("Non e' stato possibile connettersi alle luci"); + if(allLights.size() != 0) { double bri = 0; double hue = 0; @@ -88,8 +91,11 @@ public class Hue { } bri = bri/allLights.size(); hue = hue/allLights.size(); - setState("bri", (int) bri ); - setState("hue", (int) hue ); + + Map map = new HashMap<>(); + map.put("bri", (int)bri); + map.put("hue", (int)hue); + setState(map); brightness = (bri*MAX_BRIGHTNESS)/100; } @@ -112,15 +118,21 @@ public class Hue { allLights.remove(string); } + /** + * Accende o spegne le luci controllate + * @param on vero se si vuole le luci accese, false per spegnerle + */ + public void on(boolean on) { setState("on", on, false); } + /** * Accende tutte le luci controllate */ - public void turnOn() { setState("on", true, false); } + public void turnOn() { on(true); } /** * Spegne tutte le luci controllate */ - public void turnOff() { setState("on", false, false); } + public void turnOff() { on(false); } /** * Ritorna la liminosita' attuale delle luci controllate diff --git a/src/main/java/device/Sensor.java b/src/main/java/device/Sensor.java index a2dc6af..c672b26 100644 --- a/src/main/java/device/Sensor.java +++ b/src/main/java/device/Sensor.java @@ -42,22 +42,25 @@ public class Sensor { /** * Crea un sensore contenente tutti i nodi + * @throws NullPointerException se non trova nessun sensore */ - public Sensor() { - this(null); - } + public Sensor() throws NullPointerException { this(null); } /** * Si connette ad un sensore che ha il nodeId selezioniato * @param nodeId nodo che viene selezionato + * @throws NullPointerException se non trova nessun sensore */ - public Sensor (Integer nodeId) { + public Sensor (Integer nodeId) throws NullPointerException { // create an instance of the Z-Way library; all the params are mandatory (we are not going to use the remote service/id) IZWayApi zwayApi = new ZWayApiHttp(IP_ADDRESS, PORT, "http", USERNAME, PASSWORD, 0, false, new ZWaySimpleCallback()); // get all the Z-Wave devices allZWaveDevices = zwayApi.getDevices(); + if(allZWaveDevices == null) + throw new NullPointerException("I sensori non sono stati trovati"); + if(nodeId != null) useNode(nodeId); else @@ -94,7 +97,10 @@ public class Sensor { synchronized public void update(int timeout) throws InterruptedException { wait(timeout); for (Device device : devices.getAllDevices()) - device.update(); + try { + device.update(); + } catch (Exception e) {} + wait(timeout); } /* diff --git a/src/main/java/main/Main.java b/src/main/java/main/Main.java index 504db51..9dec698 100644 --- a/src/main/java/main/Main.java +++ b/src/main/java/main/Main.java @@ -5,6 +5,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import support.Database; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * Created by 20015159 on 28/08/2018. */ @@ -15,34 +20,56 @@ public class Main { */ private static final Logger LOG = LoggerFactory.getLogger("SeniorAssistant"); - private static Hue lights; - private static Fitbit fitbit; - private static Sensor sensor; + private static Hue lights = null; + private static Fitbit fitbit = null; + private static Sensor sensor = null; /** * Funzione principale, qui si creano tutte le classi che verranno utilizzate. * @param args per ora nulla, ma forse in futuro si potrebbe mettere roba */ public static void main(String[] args) { + Map arguments = getArgsMap(args); - LOG.info("Connecting to hue lights"); - lights = new Hue(); - - LOG.info("Connecting to the sensors"); - sensor = new Sensor(2); + // list of arguments to use in the classes + String hueAddress = arguments.get("hueAddress"); + String hueUser = arguments.get("hueUser"); + Integer sensorLog = getInt(arguments.get("sensorLog")); + Integer sensorNode = getInt(arguments.get("sensorNode")); try { - LOG.info("Connecting to Fitbit"); - fitbit = new Fitbit(); + LOG.info("Connessione alle Philips Hue..."); + lights = new Hue(hueAddress, hueUser); - startInsertData(); - // add here functions associated with fitbit - } catch (Exception e) { // in this way the program will continue even without fitbit - e.printStackTrace(); + try { + LOG.info("Connessione ai sensori..."); + sensor = new Sensor(sensorNode); + + if(sensorLog>0) + startSensorLog(sensorLog); + } catch (Exception e) { + LOG.warn(e.getMessage()); + } + + try { + LOG.info("Connessione al Fitbit, ignorare eventuale errore per setPermissionsToOwnerOnly..."); + fitbit = new Fitbit(); + + startInsertData(); + startCheckSteps(); + startHueControlledByHeartBeat(); + startWarnIfHeartBeatDrops(); + } catch (Exception e) { + LOG.warn("Non e' stato possibile collegarsi al fitbit"); + e.printStackTrace(); + } + + startHueAutoBrightness(); + startWebhook(); + } catch (Exception e) { + LOG.error(e.getMessage()); } - - startSensorLog(10); - startWebhook(); + LOG.info("FINE MAIN"); } /** @@ -117,6 +144,42 @@ public class Main { thread.start(); } + // TODO AUTO:{B} Gestione luci in modo che la luminosità sia sempre la stessa + private static void startHueAutoBrightness() { + // controllare la luminosita' arrivata dal sensore + // trovare un valore di default per ogni ora + // se troppo bassa alzare la luci di poco + // se troppo alta abbassare le luci + // se l'utente modifica la luminosita' delle luci allora non fare nulla per almeno 20/30 minuti o di piu + } + + // TODO AUTO:{C} Gestione luci a seconda del battito cardiaco + private static void startHueControlledByHeartBeat() { + // ad ogni X minuti: + // prendere la media di battiti di quell'ora dal DB + // prendere dal fitbit i valori degli ultimi X minuti + // controllare che non differiscano di un valore Delta + // se differiscono di almeno Delta modificare le luci abbassandole o alzandole secondo le esigenze + // (nel caso modificare anche il colore e renderlo meno intenso o di piu) + } + + // TODO AUTO:{D} Ad una certa ora guarda i passi e se sono pochi dillo + private static void startCheckSteps() { + // trovare un orario (magari inserirlo tramite arg) + // a quell'ora controllare i passi fatti durante la giornata + // se pochi mandare un avviso tramite dialogFlow(?) + // (secondo me si puo' evitare) + } + + // TODO AUTO:{E} Se i battiti sono troppo bassi/alti avvisare il tizio + private static void startWarnIfHeartBeatDrops() { + // controllare gli ultimi X minuti del fitbit + // tenersi una lista degli ultimi Y risultati + // controllare l'andamento dei battiti + // se troppo bassi o alti secondo un Delta, allora inviare un messaggio (DialogFlow?) + // (e' possibile integrarlo con la gestione delle luci tramite il battito) + } + /* TODO AUTOMATIC: {B}, {C}, {D}, {E} @@ -134,4 +197,37 @@ public class Main { // Randomly at night heavy metal start */ + + /* ------------------------------------------------------------------------------------ + Le funzioni qui sotto servono solamente per gli argomenti passati al main + ------------------------------------------------------------------------------------ */ + + /** + * Prende gli argomenti nel formato "-(.+)::(.+)" e li inserisce in una mappa. + * Se l'argomento non e' nel formato giusto lo ignora. + * @param args un'array di stringhe contente i vari argomenti + * @return una mappa con key il nome dell'argomento (la parte prima del :: e dopo il meno) e come valore il valore di esso (la parte dopo ::) + */ + private static Map getArgsMap(String[] args) { + Map map = new HashMap<>(); + Pattern pattern = Pattern.compile("-(.+)::(.+)"); + + for (String arg: args) { + Matcher matcher = pattern.matcher(arg); + if (matcher.find()) + map.put(matcher.group(1), matcher.group(2)); + } + return map; + } + + /** + * Funzione creata per gli argomenti che vengono passati in modo da evitare millemila try and catch + * @param num la stringa da trasformare in numero + * @return il numero trasformato, null se fallisce + */ + private static Integer getInt(String num) { + Integer returnNum = null; + try { returnNum = Integer.parseInt(num); } catch (Exception e) {} + return returnNum; + } } diff --git a/src/main/java/support/Rest.java b/src/main/java/support/Rest.java index fc9b03d..80f8966 100644 --- a/src/main/java/support/Rest.java +++ b/src/main/java/support/Rest.java @@ -3,6 +3,7 @@ package support; import java.io.IOException; import java.util.HashMap; import java.util.Map; + import com.google.gson.Gson; import org.apache.http.HttpResponse; @@ -13,15 +14,22 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** - * A generic class to perform HTTP calls and parse the resulting JSON. - * - * @author Luigi De Russis - * @version 1.0 (18/05/2017) + * Una classe generica che invia delle richieste Rest e le parsifica nel JSON corrispondente */ public class Rest { - // init gson + /** + * Un logger per vedere quando le richieste falliscono + */ + private static final Logger LOG = LoggerFactory.getLogger("Rest"); + + /** + * Un GSON utile per il parsing della risposta + */ private static final Gson gson = new Gson(); /** @@ -31,14 +39,12 @@ public class Rest { * @return the response, parsed from JSON */ public static Map get(String URL) { - // init Map response = new HashMap<>(); CloseableHttpClient httpclient = HttpClients.createDefault(); HttpGet request = new HttpGet(URL); - CloseableHttpResponse result = null; - + CloseableHttpResponse result; try { result = httpclient.execute(request); String json = EntityUtils.toString(result.getEntity()); @@ -48,7 +54,7 @@ public class Rest { result.close(); httpclient.close(); } catch (IOException e) { - e.printStackTrace(); + LOG.error("GET: " + e.getMessage()); } return response; @@ -62,11 +68,10 @@ public class Rest { * @param contentType the content type of the request */ public static void put(String URL, String contentBody, String contentType) { - // init CloseableHttpClient httpclient = HttpClients.createDefault(); HttpPut request = new HttpPut(URL); - StringEntity params = null; + StringEntity params; try { params = new StringEntity(contentBody); request.addHeader("content-type", contentType); @@ -76,7 +81,7 @@ public class Rest { // should be in finally... httpclient.close(); } catch (Exception e) { - e.printStackTrace(); + LOG.error("PUT: " + e.getMessage()); } }