Lights, Doc, Db

- Now lights works with DialogFlow
- Database automatically update data from user's fitbit
- Doc is written well (well.. it is ok)
- Added logging here and there
- Removed unused code
- Improved code reading and code quality
This commit is contained in:
2018-09-02 00:04:29 +02:00
parent d891e53c8e
commit f7942abae2
13 changed files with 395 additions and 328 deletions

1
.gitignore vendored
View File

@@ -29,6 +29,7 @@ hs_err_pid*
.idea/*.xml .idea/*.xml
.gradle/ .gradle/
.iml .iml
.db
# eclipse things # # eclipse things #
.classpath .classpath

View File

@@ -7,10 +7,12 @@ import com.google.gson.Gson;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import spark.Spark;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static spark.Spark.get;
import static spark.Spark.post; import static spark.Spark.post;
/** /**
@@ -21,7 +23,12 @@ public class DialogFlowWebHook {
/** /**
* Un logger per vedere le cose piu' easy * Un logger per vedere le cose piu' easy
*/ */
private final Logger log = LoggerFactory.getLogger(this.getClass()); private static final Logger LOG = LoggerFactory.getLogger(DialogFlowWebHook.class);
/**
* Stringa che viene usata se l'azione esiste ma lancia un qualche tipo di errore
*/
public static final String ACTION_ERROR = "Purtroppo chi mi ha programmato e' un pirla, non posso fare cio' che hai chiesto";
/** /**
* Errore che viene mostrato all'utente se l'azione inviata non corrisponde a nessuna di quelle inserite * Errore che viene mostrato all'utente se l'azione inviata non corrisponde a nessuna di quelle inserite
@@ -33,67 +40,89 @@ public class DialogFlowWebHook {
*/ */
public final String path; public final String path;
/**
* La porta in cui il server ascoltera'
*/
public final int port;
/** /**
* Mappa che contiene tutte le azioni e il loro ID * Mappa che contiene tutte le azioni e il loro ID
*/ */
private final Map<String, Action> actions; private final Map<String, Action> actions;
/** /**
* Crea una classe vuota per un server che risponde lle chiamate di Dialog-Flow. * Crea una classe vuota per un server che risponde alle chiamate di Dialog-Flow.
* La path viene impostata di default a "/" * La path viene impostata di default a "/" e la porta a 4567
*/ */
public DialogFlowWebHook() { public DialogFlowWebHook() { this("/", 4567); }
this("/");
}
/** /**
* Crea una classe vuota per un server che risponde lle chiamate di Dialog-Flow. * Crea una classe vuota per un server che risponde alle chiamate di Dialog-Flow.
* @param path il percorso dopo l'url inidicato nel WebHook di Dialog-Flow * @param path il percorso dopo l'url inidicato nel WebHook di Dialog-Flow
* @param port la porta da cui il Webhoook ascolta (se inserito numero negativo ascolta di default sulla porta 4567)
*/ */
public DialogFlowWebHook(String path) { public DialogFlowWebHook(String path, int port) {
this.path = path; this.path = path;
this.port = port>0? port:4567;
this.actions = new HashMap<>(); this.actions = new HashMap<>();
} }
/** /**
* Aggiunge un'azione ad una specifica richiesta di Dialog-Flow * Aggiunge un'azione ad una specifica richiesta di Dialog-Flow
* @param actionId il nome dell'azione che viene passata da Dialog-Flow * @param actionId il nome dell'azione che viene passata da Dialog-Flow
* @param action l'azione da fare (usare lambda) * @param action l'azione da fare (e' consigliato usare le espressioni lambda)
*/ */
public void addOnAction(String actionId, Action action) { public void addOnAction(String actionId, Action action) { this.actions.put(actionId, action); }
this.actions.put(actionId, action);
}
/** /**
* Fa partire il server per accettare richieste da Dialog-Flow. * Fa partire il server per accettare richieste da Dialog-Flow ascoltando connessioni in post.<br>
* Ogni richiesta viene esaminata e fatta coincidere con una azione specificata precedentemente. * Ogni richiesta viene esaminata e fatta coincidere con un'azione specificata precedentemente.<br>
* Se nessuna azione viene riscontrata, viene inviato un errore, rimuovendo i messaggi * Se nessuna azione viene riscontrata, viene inviato un errore, rimuovendo i messaggi<br>
* Inoltre aggiunge un'interfaccia in get che riguarda un iframe di dialogflow
*/ */
public void startServer() { // todo add param port? Spark.port(num); public void startServer() {
Spark.port(this.port);
Gson gson = GsonFactory.getDefaultFactory().getGson(); Gson gson = GsonFactory.getDefaultFactory().getGson();
post(this.path, (request, response) -> { post(this.path, (request, response) -> {
Fulfillment output = new Fulfillment(); Fulfillment output = new Fulfillment();
AIResponse input = gson.fromJson(request.body(), AIResponse.class); AIResponse input = gson.fromJson(request.body(), AIResponse.class);
String text = null; String inputAction = input.getResult().getAction();
Map<String, JsonElement> inputParam = input.getResult().getParameters();
String text;
try { try {
log.info("AZIONE: "+input.getResult().getAction()); LOG.debug("AZIONE: "+ inputAction + ", PARAMS: " + inputParam);
Action action = actions.get(input.getResult().getAction()); Action action = actions.get(inputAction);
text = action.doAction(input.getResult().getParameters()); try {
text = action.doAction(inputParam);
} catch (NullPointerException e) {
LOG.warn("AZIONE FALLITA: "+ inputAction);
text = ACTION_ERROR;
}
} catch (NullPointerException e) { } catch (NullPointerException e) {
log.info("NESSUNA AZIONE TROVATA"); LOG.error("NESSUNA AZIONE TROVATA: "+ inputAction);
text = ERROR; text = ERROR;
} }
if(text != null) { if(text == null)
log.info(text); text = input.getResult().getFulfillment().getSpeech();
output.setDisplayText(text);
output.setSpeech(text); LOG.debug("RISPOSTA: " + text);
} output.setDisplayText(text);
output.setSpeech(text);
response.type("application/json"); response.type("application/json");
return output; return output;
}, gson::toJson); }, gson::toJson);
get(this.path, (request, response) -> {
return "<iframe\n" +
" allow=\"microphone;\"\n" +
" width=\"350\"\n" +
" height=\"430\"\n" +
" src=\"https://console.dialogflow.com/api-client/demo/embedded/SeniorAssistant\">\n" +
"</iframe>";
});
} }
/** /**
@@ -104,7 +133,7 @@ public class DialogFlowWebHook {
* Fai l'azione desiderata. * Fai l'azione desiderata.
* Se ritorna una stringa allora il testo viene cambiato. Se ritorna null non cambia il testo * Se ritorna una stringa allora il testo viene cambiato. Se ritorna null non cambia il testo
* *
* @param params a map containing all the parameters passed form the request * @param params una mappa contenente tutti i parametri impostati da dialogflow
* @return Una stringa che verra' usata come messaggio o null se non si vuole * @return Una stringa che verra' usata come messaggio o null se non si vuole
*/ */
String doAction(Map<String, JsonElement> params); String doAction(Map<String, JsonElement> params);

View File

@@ -20,25 +20,29 @@ public class Fitbit {
* Url da dove si possono prendere i dati dai vari dispositivi fitbit * Url da dove si possono prendere i dati dai vari dispositivi fitbit
*/ */
public static final String BASIC_URL = "https://api.fitbit.com/"; public static final String BASIC_URL = "https://api.fitbit.com/";
/** /**
* Utente del fitbit<br> * Utente del fitbit<br>
* In questo caso e' universale e prende l'utente che e' attualmente loggato * In questo caso e' universale e prende l'utente che e' attualmente loggato
*/ */
public static final String USER = "/user/-/"; public static final String USER = "/user/-/";
/** /**
* Un minuto in millisecondi * Un minuto in millisecondi
*/ */
private static final long MINUTE = 60000; /* 5 minutes in millisec */ private static final long MINUTE = 60000;
/** /**
* L'oauth per l'account fitbit * L'oauth per l'account fitbit
*/ */
private final AuthFitbit auth; private final AuthFitbit auth;
/** /**
* Una mappa contenente le ultime classi usate nelle richieste effettuate<br> * Una mappa contenente le ultime classi usate nelle richieste effettuate<br>
* Una sorta di cache * Una sorta di cache
*/ */
private final Map<Class<?>, Long> latestRequest = new HashMap<>(); private final Map<Class<?>, Long> latestRequest = new HashMap<>();
/** /**
* Un calendario in modo da sapere la data per i dati * Un calendario in modo da sapere la data per i dati
*/ */
@@ -48,26 +52,25 @@ public class Fitbit {
* La classe per sapere i dati sul battito cardiaco * La classe per sapere i dati sul battito cardiaco
*/ */
private HeartRate heart = null; private HeartRate heart = null;
/** /**
* La classe per sapere i dati sul sonno * La classe per sapere i dati sul sonno
*/ */
private Sleep sleep = null; private Sleep sleep = null;
/** /**
* La classe per sapere i dati sui passi effettuati * La classe per sapere i dati sui passi effettuati
*/ */
private Steps steps = null; private Steps steps = null;
/** /**
* Crea una istanza di fitbit<br> * Crea un'istanza di fitbit<br>
* Se l'utente non ha ancora accettato i dati richiesti o l'utente non e' * Se l'utente non ha ancora accettato i dati richiesti o l'utente non e'
* loggato,<br> * loggato, verra' aperto il browser in modo che si possano inserire i dati
* verra' aperto il browser in modo che si possano inserire i dati
* *
* @throws Exception Nel caso qualunque cosa andasse storta (vedi messaggio) * @throws Exception Nel caso qualunque cosa andasse storta (vedi messaggio)
*/ */
public Fitbit() throws Exception { public Fitbit() throws Exception { this.auth = new AuthFitbit(); }
this.auth = new AuthFitbit();
}
/** /**
* Ricevi i passi che l'utente ha effettuato nell'ultimo giorno * Ricevi i passi che l'utente ha effettuato nell'ultimo giorno
@@ -87,15 +90,13 @@ public class Fitbit {
* @return un intero rappresentante la media del battito cardiaco degli ultimi 15 minuti * @return un intero rappresentante la media del battito cardiaco degli ultimi 15 minuti
* @throws IOException nel caso la richiesta non vada a buon fine * @throws IOException nel caso la richiesta non vada a buon fine
*/ */
public synchronized double getHeartRate() throws IOException { public synchronized double getHeartRate() throws IOException { return getHeartRate(15); }
return getHeartRate(15);
}
/** /**
* Ricevi il battito cardiaco dell'utente<br> * Ricevi il battito cardiaco dell'utente<br>
* Il risultato e' una media del battito che l'utente ha avuto negli ultimi minuti * Il risultato e' una media del battito che l'utente ha avuto negli ultimi minuti
* *
* @param lastMinutes fino a quanti minuti bisogna tenere conto (positivi se no ritorno -1) * @param lastMinutes fino a quanti minuti bisogna tenere conto (positivi e !=0 se no ritorno -1)
* @return un intero rappresentante la media del battito cardiaco degli ultimi minuti specificati * @return un intero rappresentante la media del battito cardiaco degli ultimi minuti specificati
* @throws IOException nel caso la richiesta non vada a buon fine * @throws IOException nel caso la richiesta non vada a buon fine
*/ */
@@ -141,7 +142,7 @@ public class Fitbit {
} }
/** /**
* Semplice funzione che controlla che si possa fare l'update o meno di una specifica classe<br> * Semplice funzione che controlla che si possa fare l'update o meno di una specifica classe.<br>
* Se e' possibile fare l'update viene mandata una run all'url selezionato e viene ritornata la variabile aggiornata<br> * Se e' possibile fare l'update viene mandata una run all'url selezionato e viene ritornata la variabile aggiornata<br>
* Altrimenti viene ritornata la variabile passata * Altrimenti viene ritornata la variabile passata
* *
@@ -150,7 +151,7 @@ public class Fitbit {
* @param url l'url da cui prende i dati aggiornati * @param url l'url da cui prende i dati aggiornati
* @return la variabile aggiornata * @return la variabile aggiornata
*/ */
private <T> T update(Class<T> varClass, T variable, String url) throws IOException { private synchronized <T> T update(Class<T> varClass, T variable, String url) throws IOException {
try { try {
long current = System.currentTimeMillis(); long current = System.currentTimeMillis();
long latest = latestRequest.get(varClass); long latest = latestRequest.get(varClass);

View File

@@ -22,36 +22,36 @@ public class Hue {
*/ */
public static final Map<String, Double[]> COLORS = new HashMap<>(); public static final Map<String, Double[]> COLORS = new HashMap<>();
// todo set right colors
static {
COLORS.put("giallo", new Double[]{0.55, 0.45});
COLORS.put("rosso", new Double[]{0.7, 0.25});
COLORS.put("verde", new Double[]{0.15, 0.65});
COLORS.put("blu", new Double[]{0.0, 0.0});
COLORS.put("rosa", new Double[]{0.45, 0.15});
COLORS.put("viola", new Double[]{0.25, .1});
COLORS.put("azzurro", new Double[]{0.15, 0.25});
COLORS.put("arancione", new Double[]{0.63, 0.35});
//COLORS.put("nero", new Double[]{1.0, 1.0});
COLORS.put("bianco", new Double[]{0.35, 0.3});
}
//private String baseURL = "192.168.0.2";
//private String username = "C0vPwqjJZo5Jt9Oe5HgO6sBFFMxgoR532IxFoGmx";
/** /**
* L'url in cui si possono trovare le luci * L'url in cui si possono trovare le luci
*/ */
private String lightsURL; // = baseURL+"/api/"+username+"/lights/"; private final String lightsURL;
/** /**
* Tutte le luci che sono state registrate dall'url * Tutte le luci che sono state registrate dall'url
*/ */
private Map<String, ?> allLights; private final Map<String, Map<String, Object>> allLights;
/** /**
* L'ultima luminosita' impostata * L'ultima luminosita' impostata
*/ */
private int brightness = 0; private double brightness = 0;
/**
* Riempimento della mappa con i vari colori
*/
static { // todo set right colors (in the simulation they are off, maybe in reality are ok)
COLORS.put("giall[oae]", new Double[]{0.475, 0.475});
COLORS.put("ross[oae]", new Double[]{0.7, 0.25});
COLORS.put("verd[ei]", new Double[]{0.1, 0.55});
COLORS.put("blu", new Double[]{0.15, 0.175});
COLORS.put("rosa", new Double[]{0.45, 0.275});
COLORS.put("viola", new Double[]{0.25, 0.1});
COLORS.put("azzurr[oae]", new Double[]{0.15, 0.25});
COLORS.put("arancio(ne|ni)?", new Double[]{0.6, 0.35});
//COLORS.put("nero", new Double[]{1.0, 1.0});
COLORS.put("bianc(o|a|he)", new Double[]{0.3, 0.25});
}
/** /**
* Cerca le luci Philips Hue a ll'indirizzo <a href="http://172.30.1.138/api/C0vPwqjJZo5Jt9Oe5HgO6sBFFMxgoR532IxFoGmx/lights/">http://172.30.1.138/api/C0vPwqjJZo5Jt9Oe5HgO6sBFFMxgoR532IxFoGmx/lights/</a> * Cerca le luci Philips Hue a ll'indirizzo <a href="http://172.30.1.138/api/C0vPwqjJZo5Jt9Oe5HgO6sBFFMxgoR532IxFoGmx/lights/">http://172.30.1.138/api/C0vPwqjJZo5Jt9Oe5HgO6sBFFMxgoR532IxFoGmx/lights/</a>
@@ -61,23 +61,31 @@ public class Hue {
} }
/** /**
* Cerca le luci Philips Hue nell'indirizzo specificato e con l'utente specificato * Cerca le luci Philips Hue nell'indirizzo specificato e con l'utente specificato.<br>
* @param ip l'indirizzo IP * Una volta trovate le luci le setta tutte alla stessa luminosita' e allo stesso colore<br>
* (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 * @param user l'utente
*/ */
public Hue(String ip, String user) { public Hue(String ip, String user) {
this("http://" + ip + "/api/" + user + "/lights/"); lightsURL = "http://" + ip + "/api/" + user + "/lights/";
} allLights = (Map<String, Map<String, Object>>)Rest.get(lightsURL);
/** if(allLights.size() != 0) {
* Inizializza la classe cercando tutte le luci all'indirizzo url specificato double bri = 0;
* double hue = 0;
* @param url l'indirizzo da inserire delle luci Hue for (String name: allLights.keySet()) {
*/ Map<String, Object> state = (Map<String, Object>)allLights.get(name).get("state");
public Hue (String url) { bri += (Double) state.get("bri");
lightsURL = url; hue += (Double) state.get("hue");
allLights = Rest.get(lightsURL); }
// Todo brightness initial, maybe by default 50% or 75% of the total bri = bri/allLights.size();
hue = hue/allLights.size();
setState("bri", String.valueOf(bri));
setState("hue", String.valueOf(hue));
brightness = (bri*MAX_BRIGHTNESS)/100;
}
} }
/** /**
@@ -85,9 +93,7 @@ public class Hue {
* *
* @return l'insieme dei nomi delle luci * @return l'insieme dei nomi delle luci
*/ */
public Set<String> getNameLights() { public Set<String> getNameLights() { return allLights.keySet(); }
return allLights.keySet();
}
/** /**
* Rimuove dal controllo tutte le luci che hanno il nome uguale ad uno contenuto nell'insieme passato * Rimuove dal controllo tutte le luci che hanno il nome uguale ad uno contenuto nell'insieme passato
@@ -102,95 +108,90 @@ public class Hue {
/** /**
* Accende tutte le luci controllate * Accende tutte le luci controllate
*/ */
public void turnOn() { public void turnOn() { setState("on", "true"); }
setState("on", "true");
}
/** /**
* Spegne tutte le luci controllate * Spegne tutte le luci controllate
*/ */
public void turnOff() { public void turnOff() { setState("on", "false"); }
setState("on", "false");
}
/** /**
* Ritorna la liminosita' attuale delle luci controllate * Ritorna la liminosita' attuale delle luci controllate
* @return * @return
*/ */
public int getCurrentBrightness() { public double getCurrentBrightness() { return brightness; }
return brightness;
}
/** /**
* Modifica la luminosita' delle luci a seconda del valore inserito * Modifica la luminosita' delle luci a seconda del valore inserito
* @param num la luminosita' che si vuole * @param num la luminosita' che si vuole da (0 a 100)
*/ */
public void setBrightness(int num) { public void setBrightness(double num) {
if (num<0) if (num<0)
num=0; num=0;
setState("bri", String.valueOf(num)); else if (num>100)
num=100;
setState("bri", String.valueOf( (num*MAX_BRIGHTNESS)/100) );
brightness = num; brightness = num;
} }
/** /**
* Dinuisce la luminosita' delle luci controllate della percentuale che viene passata * Aumenta la luminosita' delle luci controllate della percentuale che viene passata
* @param percentage la percentuale di diminuzione * @param percentage la percentuale di aumento della luminosita'
*/ */
public void increaseBrightness(int percentage) { public void increaseBrightness(double percentage) {
if (percentage<0) if (percentage<0)
percentage = 0; percentage = 0;
else if (percentage>100) else if (percentage>100)
percentage = 100; percentage = 100;
setBrightness(brightness + (percentage*MAX_BRIGHTNESS)/100); setBrightness(brightness + percentage);
} }
/** /**
* Aumenta la luminosita' delle luci controllate del 10% * Aumenta la luminosita' delle luci controllate del 15%
*/ */
public void increaseBrightness() { public void increaseBrightness() { increaseBrightness(15); }
increaseBrightness(10);
}
/** /**
* Dinuisce la luminosita' delle luci controllate della percentuale che viene passata * Dinuisce la luminosita' delle luci controllate della percentuale che viene passata
* @param percentage la percentuale di diminuzione * @param percentage la percentuale di diminuzione della luminosita'
*/ */
public void decreaseBrightness(int percentage) { public void decreaseBrightness(int percentage) {
if (percentage<0) if (percentage<0)
percentage = 0; percentage = 0;
else if (percentage>100) else if (percentage>100)
percentage = 100; percentage = 100;
setBrightness(brightness - (percentage*MAX_BRIGHTNESS)/100); setBrightness(brightness - percentage);
} }
/** /**
* Dinuisce la luminosita' delle luci controllate del 10% * Dinuisce la luminosita' delle luci controllate del 15%
*/ */
public void decreaseBrightness() { public void decreaseBrightness() { decreaseBrightness(15); }
decreaseBrightness(10);
}
public void changeColor(String colorName) { public void changeColor(String colorName) {
String hueColor = GsonFactory.getDefaultFactory().getGson().toJson(COLORS.get(colorName)); for (String regex: COLORS.keySet())
setState("xy", hueColor); if(colorName.matches("(?i)" + regex))
setState("xy", GsonFactory.getDefaultFactory().getGson().toJson(COLORS.get(regex)));
} }
/** /**
* Modifica il colore delle luci in modo da fare un bel effetto arcobaleno continuo * Modifica il colore delle luci in modo da fare un bel effetto arcobaleno continuo
*/ */
public void colorLoop() { public void colorLoop() { setState("effect", "colorloop"); }
setState("effect", "colorloop");
}
/** /**
* Funzione generale per poter utilizzare qualunque valore, ma non funziona<br> * Funzione generale per poter utilizzare qualunque valore, ma non funziona<br>
* Da testare, visto che mi sembra strano che non funzi... * Da testare, visto che mi sembra strano che non funzi...<br>
* e invece funziona. * e invece funziona.
*/ */
public void setState(String attribute, String value){ public void setState(String attribute, String value){
for (String light : allLights.keySet()) for (String light : allLights.keySet()) {
Map<String, Object> state = (Map<String, Object>)allLights.get(light).get("state");
Rest.put(lightsURL + light + "/state", Rest.put(lightsURL + light + "/state",
"{ \"" + attribute + "\" : " + value + " }", "{ \"" + attribute + "\" : " + value + ", \"transitiontime\": 10 }", // todo check transition
"application/json"); "application/json");
state.put(attribute, value);
}
} }
} }

View File

@@ -11,10 +11,6 @@ import support.ZWaySimpleCallback;
*/ */
public class Sensor { public class Sensor {
/* todo ma serve il LOGGER?
private Logger logger = LoggerFactory.getLogger(Sensor.class);
*/
/** /**
* IP del sensore a cui ci si vuole agganciare * IP del sensore a cui ci si vuole agganciare
*/ */
@@ -100,7 +96,8 @@ public class Sensor {
device.update(); device.update();
wait(timeout); wait(timeout);
} }
/*public boolean IsLowLuminescence(int Luminescence) { /*
public boolean IsLowLuminescence(int Luminescence) {
if (dev.getProbeType().equalsIgnoreCase("Luminescence")) if (dev.getProbeType().equalsIgnoreCase("Luminescence"))
if (Integer.parseInt(dev.getMetrics().getLevel()) < Luminescence) if (Integer.parseInt(dev.getMetrics().getLevel()) < Luminescence)
return true; return true;

View File

@@ -9,17 +9,9 @@ import java.util.Map;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class HeartRate { public class HeartRate {
private String dateTime;
private double average; private double average;
public double getAverage() { public double getAverage() { return average; }
return average;
}
@JsonProperty("activities-heart")
public void quelloCheVoglio(Map<String, Object>[] activities){
dateTime = (String) activities[0].get("dateTime");
}
@JsonProperty("activities-heart-intraday") @JsonProperty("activities-heart-intraday")
public void setAverage(Map<String, Object> map) { public void setAverage(Map<String, Object> map) {

View File

@@ -3,6 +3,7 @@ package device.fitbitdata;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.sql.Timestamp;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
@@ -48,14 +49,14 @@ public class Sleep {
} }
public class SleepData { public class SleepData {
public final Date start_date; public final Timestamp start_date;
public final long duration; public final long duration;
public final Date end_date; public final Timestamp end_date;
public SleepData(Date start_date, long duration) { public SleepData(Date start_date, long duration) {
this.start_date = start_date; this.start_date = new Timestamp(start_date.getTime());
this.duration = duration; this.duration = duration;
this.end_date = start_date!=null? new Date(start_date.getTime() + duration):null; this.end_date = start_date!=null? new Timestamp(start_date.getTime() + duration):null;
} }
} }
} }

View File

@@ -23,8 +23,6 @@ public class Steps {
steps = Integer.parseInt(map.get("value")); steps = Integer.parseInt(map.get("value"));
} }
public int getSteps() { public int getSteps() { return steps; }
return steps;
}
} }

View File

@@ -1,14 +1,10 @@
package main; package main;
import ai.api.GsonFactory;
import device.*; import device.*;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import support.Database; import support.Database;
import java.io.IOException;
// todo docs
/** /**
* Created by 20015159 on 28/08/2018. * Created by 20015159 on 28/08/2018.
*/ */
@@ -23,23 +19,22 @@ public class Main {
private static Fitbit fitbit; private static Fitbit fitbit;
private static Sensor sensor; private static Sensor sensor;
private static Database database;
/** /**
* Funzione principale, qui si creano tutte le classi che verranno utilizzate. * Funzione principale, qui si creano tutte le classi che verranno utilizzate.
* @param args per ora nulla, ma forse in futuro si potrebbe mettere roba * @param args per ora nulla, ma forse in futuro si potrebbe mettere roba
*/ */
public static void main(String[] args) { public static void main(String[] args) {
// todo setting log level -> useful if you dont need to see a spam of things but not working
// System.setProperty(SimpleLogger.DEFAULT_LOG_LEVEL_KEY , SimpleLogger.S);
LOG.info("Connecting to hue lights"); LOG.info("Connecting to hue lights");
lights = new Hue("localhost:8090", "newdeveloper"); lights = new Hue("192.168.1.7:8090", "newdeveloper");
LOG.info("Connecting to the sensors"); LOG.info("Connecting to the sensors");
sensor = new Sensor(); sensor = new Sensor();
try { try {
LOG.info("Connecting to Database");
database = Database.getInstance();
LOG.info("Connecting to Fitbit"); LOG.info("Connecting to Fitbit");
fitbit = new Fitbit(); fitbit = new Fitbit();
@@ -56,13 +51,27 @@ public class Main {
* Fa partire il server Webhook per DialogFlow e continua l'esecuzione * Fa partire il server Webhook per DialogFlow e continua l'esecuzione
*/ */
private static void startWebhook() { private static void startWebhook() {
LOG.info("Adding actions to Webhook");
DialogFlowWebHook df = new DialogFlowWebHook(); DialogFlowWebHook df = new DialogFlowWebHook();
df.addOnAction("LightsON", (params) -> {lights.turnOn(); return null;}); df.addOnAction("LightsON", (params) -> {lights.turnOn(); return null;});
df.addOnAction("LightsOFF", (params) -> {lights.turnOff(); return null;}); df.addOnAction("LightsOFF", (params) -> {lights.turnOff(); return null;});
df.addOnAction("ColorLoop", (params) -> {lights.colorLoop(); return null;}); df.addOnAction("ColorLoop", (params) -> {lights.colorLoop(); return null;});
df.addOnAction("ChangeColor", (params) -> {lights.changeColor(params.get("color").getAsString()); return null;}); df.addOnAction("ChangeColor", (params) -> {lights.changeColor(params.get("color").getAsString()); return null;});
df.addOnAction("SetLights", (params) -> {lights.setBrightness(params.get("intensity").getAsInt()); return null;});
df.addOnAction("LightsDOWN", (params) -> {
if(params.get("intensity").getAsString().equals(""))
lights.decreaseBrightness();
else
lights.decreaseBrightness(params.get("intensity").getAsInt());
return null;
});
df.addOnAction("LightsUP", (params) -> {
if(params.get("intensity").getAsString().equals(""))
lights.increaseBrightness();
else
lights.increaseBrightness(params.get("intensity").getAsInt());
return null;
});
LOG.info("Starting Webhook"); LOG.info("Starting Webhook");
df.startServer(); df.startServer();
@@ -72,47 +81,36 @@ public class Main {
* Gestione DB in modo che si aggiorni ogni ora * Gestione DB in modo che si aggiorni ogni ora
*/ */
private static void startInsertData() { private static void startInsertData() {
LOG.info("Connecting to DB to write fitbit data periodically");
try { try {
fitbit.getHoursSleep();
Database database = Database.getInstance(); Database database = Database.getInstance();
Thread hourlyData = new Thread(database.insertHourlyData(fitbit), "updating-hour-data"); Thread hourlyData = new Thread(database.insertHourlyData(fitbit), "updating-hour-data");
Thread dailyData = new Thread(database.insertDailyData(fitbit), "updating-day-data"); Thread dailyData = new Thread(database.insertDailyData(fitbit), "updating-day-data");
LOG.info("Starting threads for automatic update");
hourlyData.start(); hourlyData.start();
dailyData.start(); dailyData.start();
LOG.info("Threads started for updating database");
} catch (IOException e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
/* /*
public static void main(String[] args) throws Exception { TODO AUTOMATIC: {B}, {C}, {D}, {E}
Fitbit fitbit = new Fitbit();
Sensor sensor = new Sensor();
Hue hue = new Hue();
while(true) { XXX Gestione DB in modo che si aggiorni ogni ora
double heart = fitbit.getHeartRate(); /B/ Gestione luci in modo che la luminosità sia sempre la stessa
int brightness = sensor.getBrightnessLevel(); /C/ Gestione luci a seconda del battito cardiaco
/D/ Ad una certa ora guarda i passi e se sono pochi dillo
/E/ Se i battiti sono troppo bassi/alti avvisare il tizio
// AUTOMATIC TODO USER-INTERACTION {A}, {C}
/X/ Gestione DB in modo che si aggiorni ogni ora
// Gestione luci in modo che la luminosità sia sempre la stessa
// Gestione luci a seconda del battito cardiaco
// Ad una certa ora guarda i passi e se sono pochi dillo
// Se i battiti sono troppo bassi/alti avvisare il tizio
// USER-INTERACTION /A/ Dati del sonno/battito/passi che l'utente puo' richiedere
// Dati del sonno/battito/passi che l'utente puo' richiedere XXX Gestione luci secondo le esigenze dell'utente ( settare Dialogflow e server + risolvere bug )
/X/ Gestione luci secondo le esigenze dell'utente ( settare Dialogflow e server + risolvere bug ) /C/ EXTRA Gestione musica tramite comando vocale
// EXTRA Gestione musica tramite comando vocale
// Randomly at night heavy metal start // Randomly at night heavy metal start
}
}
*/ */
} }

View File

@@ -3,7 +3,6 @@ package oauth;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import ai.api.GsonFactory;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.api.client.auth.oauth2.AuthorizationCodeFlow; import com.google.api.client.auth.oauth2.AuthorizationCodeFlow;
import com.google.api.client.auth.oauth2.BearerToken; import com.google.api.client.auth.oauth2.BearerToken;
@@ -16,7 +15,6 @@ import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse; import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport; import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.GenericJson;
import com.google.api.client.json.JsonFactory; import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.JsonObjectParser; import com.google.api.client.json.JsonObjectParser;
import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.json.jackson2.JacksonFactory;
@@ -25,14 +23,74 @@ import com.google.api.client.util.store.FileDataStoreFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/**
//todo add docs * Classe piu' importante per la connessione al fitbit
*/
public class AuthFitbit { public class AuthFitbit {
/**
* Un logger per rendere le cose semplici in caso di casini
*/
private static final Logger LOG = LoggerFactory.getLogger("Fitbit Response"); private static final Logger LOG = LoggerFactory.getLogger("Fitbit Response");
/**
* Un mapper per trasformare i json in mappe.
*/
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* Directory dove vengono messi i dati utente (Token)<br>
* <br>
* Throw a Warning when change permission: they said it's a google bug 'cause is meant to run in linux/unix<br>
* https://stackoverflow.com/questions/30634827/warning-unable-to-change-permissions-for-everybody<br>
* https://github.com/google/google-http-java-client/issues/315<br>
*/
private static final java.io.File DATA_STORE_DIR = new java.io.File(System.getProperty("user.home"), ".store/seniorAssistant");
/**
* OAuth 2 scope.<br>
* Nel nostro caso sono le varie categorie dove si trovano le informazioni di cui abbiamo bisogno
*/
private static final String SCOPE[] = new String[]{"activity","heartrate","sleep","settings"};
/**
* Instanza globale di HttpTranspot necessaria per l'autorizzazione e per le richieste
*/
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
/**
* Istanza globale di una Json Factory
*/
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
/**
* Url dove e' possiblie richiedere il token
*/
private static final String TOKEN_SERVER_URL = " https://api.fitbit.com/oauth2/token";
/**
* Pagina dove si richiede l'autorizzazione a tutti i campi richiesti
*/
private static final String AUTHORIZATION_SERVER_URL = "https://www.fitbit.com/oauth2/authorize";
/**
* Istanza globale del {@link DataStoreFactory}. Il miglior metodo e' creare una singola
* istanza globale condivisa attraverso tutta l'applicazione.
*/
private static FileDataStoreFactory DATA_STORE_FACTORY;
/**
* Un HttpRequestFactory che serve per creare una richiesta http
*/
private final HttpRequestFactory requestFactory; private final HttpRequestFactory requestFactory;
/**
* Prova a connettersi al sito di Fitbit per controllare l'autorizzazione<br>
* Se la richiesta inviata e' sbagliata lancia una eccezione.<br>
* Se l'utente non ha ancora autorizzato l'applicazioe, allora una pagina sul browser (o un link in console) appare<br>
* ci si logga e si lascia l'autorizzazione a questa applicazione.
* @throws Exception per qualunque cosa apparentemente (e noi ci siamo riusciti)
*/
public AuthFitbit() throws Exception { public AuthFitbit() throws Exception {
DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR); DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
final Credential credential = authorize(); final Credential credential = authorize();
@@ -43,34 +101,9 @@ public class AuthFitbit {
}); });
} }
private static ObjectMapper mapper = new ObjectMapper();
/** Directory to store user credentials. */
/* Throw a Warning when change permission: they said it's a google bug 'cause is meant to run in linux/unix
*
* https://stackoverflow.com/questions/30634827/warning-unable-to-change-permissions-for-everybody
* https://github.com/google/google-http-java-client/issues/315
*/
private static final java.io.File DATA_STORE_DIR =
new java.io.File(System.getProperty("user.home"), ".store/seniorAssistant");
/** /**
* Global instance of the {@link DataStoreFactory}. The best practice is to make it a single * Autorizza l'applicazione ad accedere ai dati utente richiesti
* globally shared instance across your application.
*/ */
private static FileDataStoreFactory DATA_STORE_FACTORY;
/** OAuth 2 scope. */
private static final String SCOPE[] = new String[]{"activity","heartrate","sleep","settings"};
/** Global instance of the HTTP transport. */
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
/** Global instance of the JSON factory. */
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
private static final String TOKEN_SERVER_URL = " https://api.fitbit.com/oauth2/token";
private static final String AUTHORIZATION_SERVER_URL = "https://www.fitbit.com/oauth2/authorize";
/** Authorizes the installed application to access user's protected data. */
private Credential authorize() throws Exception { private Credential authorize() throws Exception {
// set up authorization code flow // set up authorization code flow
AuthorizationCodeFlow flow = new AuthorizationCodeFlow.Builder(BearerToken AuthorizationCodeFlow flow = new AuthorizationCodeFlow.Builder(BearerToken
@@ -90,38 +123,39 @@ public class AuthFitbit {
return new AuthorizationCodeInstalledApp(flow, receiver).authorize( "user" ); return new AuthorizationCodeInstalledApp(flow, receiver).authorize( "user" );
} }
public <O> O run(String url, Class<O> returnClass) throws IOException { /**
return run(url, returnClass, false); * Effettua una chiamata al server fitbit richiedendo i dati indicati dall'url
} *
* @param url l'url in cui effettuare la richiesta
public <O> O run(String url, Class<O> returnClass, boolean useAsParse) throws IOException { * @return una stringa in formato Json, che e' il risultato
FITBITUrl fitbitUrl = new FITBITUrl(url); * @throws IOException nel caso ci sia un errore con la richiesta
fitbitUrl.setFields(""); */
public String run(String url) throws IOException {
HttpRequest request = requestFactory.buildGetRequest(fitbitUrl); GenericUrl genericUrl = new GenericUrl(url);
HttpRequest request = requestFactory.buildGetRequest(genericUrl);
HttpResponse response = request.execute(); HttpResponse response = request.execute();
/* String content = response.parseAsString();
GenericJson json = response.parseAs(GenericJson.class);
response.disconnect(); response.disconnect();
LOG.debug("Recived: " + content);
System.out.println(returnClass.getSimpleName()); return content;
System.out.println(url); }
System.out.println(json.toPrettyString());
return mapper.readValue(json.toString(), returnClass); /**
*/ * Fa una chiamata al server fitbit richiedendo i dati indicati dall'url<br>
* La classe e' richiesta se si vuole fare il parsing diretto e ricevere la classe parsificata con un mapper<br>
/**/ * in questo modo non tutti i campi devono esistere
O ret = ( useAsParse ? *
response.parseAs(returnClass) : * @param url l'url in cui effettuare la richiesta
mapper.readValue(response.parseAs(GenericJson.class).toString(), returnClass) * @param returnClass la classe da ritornare
); * @param <O> la classe che ritorna
* @return un oggetto rappresentante la richiesta fatta all'url
response.disconnect(); * @throws IOException nel caso ci sia un errore con la richiesta o con il parsing di quest'ultima
*/
// todo remove this, it's only useful if you need to see the request public <O> O run(String url, Class<O> returnClass) throws IOException {
LOG.info(GsonFactory.getDefaultFactory().getGson().toJson(ret)); O ret = MAPPER.readValue(this.run(url), returnClass);
LOG.debug("Saved in class: " + JSON_FACTORY.toString(ret));
return ret; return ret;
/**/ /**/

View File

@@ -1,29 +0,0 @@
package oauth;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.util.Key;
// todo add docs
public class FITBITUrl extends GenericUrl {
@Key
private String fields;
public FITBITUrl(String encodedUrl) {
super(encodedUrl);
}
/**
* @return the fields
*/
public String getFields() {
return fields;
}
/**
* @param fields the fields to set
*/
public void setFields(String fields) {
this.fields = fields;
}
}

View File

@@ -1,18 +1,28 @@
package oauth; package oauth;
// todo add docs /**
* Classe supporto per l'oauth contenente tutti i dati dell'applicazione
*/
public class OAuth2ClientCredentials { public class OAuth2ClientCredentials {
/** Value of the "API Key". */ /**
* Il valore dell' "API Key".
*/
public static final String API_KEY = "22CSTL"; //maybe togliere le virgolette public static final String API_KEY = "22CSTL"; //maybe togliere le virgolette
/** Value of the "API Secret". */ /**
* Il valore dell' "API Secret"
*/
public static final String API_SECRET = "ea2452013abd35609940ce5601960a08"; //maybe togliere le virgolette public static final String API_SECRET = "ea2452013abd35609940ce5601960a08"; //maybe togliere le virgolette
/** Port in the "Callback URL". */ /**
* La porta per il "Callback URL".
*/
public static final int PORT = 8080; public static final int PORT = 8080;
/** Domain name in the "Callback URL". */ /**
* Il dominio del "Callback URL"
*/
public static final String DOMAIN = "127.0.0.1"; public static final String DOMAIN = "127.0.0.1";
} }

View File

@@ -5,30 +5,48 @@ import device.fitbitdata.Sleep;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.sql.Connection; import java.sql.*;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date;
import java.util.List; import java.util.List;
// todo add docs
/** /**
* Handle the connection to the SQLite database that stores our tasks. * Classe che si connette al database e permette di aggiornare i dati in modo automatico tramite dei runnable
* @author <a href="mailto:luigi.derussis@uniupo.it">Luigi De Russis</a>
* @version 1.1 (06/05/2018)
*/ */
public class Database { public class Database {
public static final String DB_LOCATION = "jdbc:sqlite:src/main/resources/"; /**
public static final String DB_NAME = "user_data.db"; * Un logger per scrivere a console eventuali errori o informazioni
*/
private static final Calendar CALENDAR = Calendar.getInstance();
private static final Logger LOG = LoggerFactory.getLogger(Database.class); private static final Logger LOG = LoggerFactory.getLogger(Database.class);
/**
* Il percorso di dove trovare il database, strutturato in: &lt;interfaccia&gt;:&lt;implementazione&gt;:&lt;percorso vero e proprio&gt;
*/
public static final String DB_LOCATION = "jdbc:sqlite:";
/**
* Il nome del database (aka il nome del file)
*/
public static final String DB_NAME = "user_data.db";
/**
* Un calendario, visto che ci serve sapere quando inseriamo i dati
*/
private static final Calendar CALENDAR = Calendar.getInstance();
/**
* Una costante che indica quanti millisecondi ci sono in un minuto (utile per le conversioni)
*/
private static final int MINUTES_TO_MILLISEC = 60000;
/**
* L'unica istanza del database (dato che e' una classe Singleton)
*/
private static Database instance; private static Database instance;
/**
* La connessione al database
*/
private final Connection conn; private final Connection conn;
/** /**
@@ -46,7 +64,7 @@ public class Database {
conn = DriverManager.getConnection(DB_LOCATION + DB_NAME); conn = DriverManager.getConnection(DB_LOCATION + DB_NAME);
Statement statement = conn.createStatement(); Statement statement = conn.createStatement();
statement.execute("CREATE TABLE IF NOT EXISTS total (day DATE PRIMARY KEY, sleep_time INTEGER, heart_rate DOUBLE, steps INTEGER)"); statement.execute("CREATE TABLE IF NOT EXISTS total (day DATE PRIMARY KEY, sleep_time INTEGER, heart_rate DOUBLE, steps INTEGER)");
statement.execute("CREATE TABLE IF NOT EXISTS heart (day_hour DATE PRIMARY KEY, heart_rate DOUBLE)"); statement.execute("CREATE TABLE IF NOT EXISTS heart (day_hour DATE PRIMARY KEY, rate DOUBLE)");
statement.execute("CREATE TABLE IF NOT EXISTS sleep (sleep_start DATE PRIMARY KEY, sleep_end DATE, duration INTEGER)"); statement.execute("CREATE TABLE IF NOT EXISTS sleep (sleep_start DATE PRIMARY KEY, sleep_end DATE, duration INTEGER)");
} }
@@ -58,8 +76,10 @@ public class Database {
* <li>'heart' battito + orario</li> * <li>'heart' battito + orario</li>
* <li>'sleep' inizio + fine + durata</li> * <li>'sleep' inizio + fine + durata</li>
* </ul> * </ul>
* <br>
* L'implementazione e' synchronized (si spera thread safe)
*/ */
public static Database getInstance() { public synchronized static Database getInstance() {
try { try {
instance = instance==null? new Database():instance; instance = instance==null? new Database():instance;
} catch (SQLException e) { } catch (SQLException e) {
@@ -69,82 +89,96 @@ public class Database {
} }
/** /**
* Prendi il Runnable che automaticamente gestisce l'inserimento dei dati orari (per ora solo il battito cardiaco) * Prendi il Runnable che automaticamente gestisce l'inserimento dei dati orari (per ora solo il battito cardiaco)<br>
* <br>
* Il runnable e' synchronized (si spera thread safe)
* @param fitbit la classe che contiene i dati aggiornati * @param fitbit la classe che contiene i dati aggiornati
* @return un Runnable * @return un Runnable
*/ */
public Runnable insertHourlyData(Fitbit fitbit) { public Runnable insertHourlyData(Fitbit fitbit) {
Runnable runnable = () -> { return new Runnable() {
boolean notInterrupted = true; @Override
boolean retry = false; public synchronized void run() {
double heartRate = 0; boolean notInterrupted = true;
Date now = null; boolean retry = false;
double heartRate = 0;
Timestamp now = null;
while(notInterrupted) { while (notInterrupted) {
try { CALENDAR.setTimeInMillis(System.currentTimeMillis());
wait((retry? 1:59-CALENDAR.get(Calendar.MINUTE)) * 60000); try {
if (retry == false) { wait((retry ? 1:58-CALENDAR.get(Calendar.MINUTE)) * MINUTES_TO_MILLISEC);
heartRate = fitbit.getHeartRate(60); CALENDAR.setTimeInMillis(System.currentTimeMillis());
now = CALENDAR.getTime(); if (retry == false) {
now = new Timestamp(CALENDAR.getTimeInMillis());
heartRate = fitbit.getHeartRate(60);
}
conn.createStatement().execute("INSERT INTO heart (day_hour, rate) VALUES ( ' " + now + " ', '" + heartRate + "')");
LOG.info(CALENDAR.getTime() + " > Ho inserito i dati orari "+now);
retry = false;
wait(MINUTES_TO_MILLISEC);
} catch (InterruptedException e) {
e.printStackTrace();
notInterrupted = false;
} catch (Exception e) {
LOG.error(CALENDAR.getTime() + " " + e.getLocalizedMessage() + " > Non e' stato possibile aggingere i dati orari al database, riprovo fra un minuto "+now);
retry = true;
} }
conn.createStatement().execute("INSERT INTO heart (day_hour, rate) VALUE ( ' " + now + " ', '" + heartRate + "')");
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
notInterrupted = false;
} catch (Exception e) {
LOG.error("Non e' stato possibile aggingere i dati orari al database, riprovo fra un minuto");
retry = true;
} }
} }
}; };
return runnable;
} }
/** /**
* Prendi il Runnable che automaticamente gestisce l'inserimento dei dati giornalieri * Prendi il Runnable che automaticamente gestisce l'inserimento dei dati giornalieri<br>
* <br>
* Il runnable e' synchronized (si spera thread safe)
* @param fitbit la classe che contiene i dati aggiornati * @param fitbit la classe che contiene i dati aggiornati
* @return un Runnable * @return un Runnable
*/ */
public Runnable insertDailyData(Fitbit fitbit) { public Runnable insertDailyData(Fitbit fitbit) {
Runnable runnable = () -> { return new Runnable() {
boolean notInterrupted = true; @Override
boolean retry = false; public synchronized void run() {
double heartRate = 0; boolean notInterrupted = true;
long sleepTime = 0; boolean retry = false;
long steps = 0; double heartRate = 0;
List<Sleep.SleepData> sleepDatas = null; long sleepTime = 0;
Date now = null; long steps = 0;
List<Sleep.SleepData> sleepDatas = null;
Timestamp now = null;
while (notInterrupted) { while (notInterrupted) {
int hourToWait = 23 - CALENDAR.get(Calendar.HOUR); CALENDAR.setTimeInMillis(System.currentTimeMillis());
try {
wait((retry? 1:(22-CALENDAR.get(Calendar.HOUR))*60) * MINUTES_TO_MILLISEC);
CALENDAR.setTimeInMillis(System.currentTimeMillis());
if (retry == false) {
heartRate = fitbit.getHeartRate(60 * 24);
sleepTime = fitbit.getHoursSleep();
sleepDatas = fitbit.getDetailedSleep();
steps = fitbit.getSteps();
now = new Timestamp(CALENDAR.getTimeInMillis());
}
try { conn.createStatement().execute("INSERT INTO total (day, sleep_time, heart_rate, steps) VALUES ( '" + now + "', '" + sleepTime + "', '" + heartRate + "', '" + steps + "' )");
wait(hourToWait * 3600000); for (Sleep.SleepData data : sleepDatas)
if (retry == false) { conn.createStatement().execute("INSERT INTO total (sleep_start, sleep_end, duration) VALUES ( '" + data.start_date + "', '" + data.end_date + "', '" + data.duration + "' )");
heartRate = fitbit.getHeartRate(60 * 24); LOG.info(CALENDAR.getTime() + " > Ho inserito i dati giornalieri");
sleepTime = fitbit.getHoursSleep(); retry = false;
sleepDatas = fitbit.getDetailedSleep(); wait(MINUTES_TO_MILLISEC);
steps = fitbit.getSteps();
now = CALENDAR.getTime(); } catch (InterruptedException e) {
e.printStackTrace();
notInterrupted = false;
} catch (Exception e) {
LOG.error(CALENDAR.getTime() + " " + e.getLocalizedMessage() + " > Non e' stato possibile aggingere i dati della giornata al database, riprovo fra un minuto");
retry = true;
} }
conn.createStatement().execute("INSERT INTO total (day, sleep_time, heart_rate, steps) VALUE ( '" + now + "', '" + sleepTime + "', '" + heartRate + "', '" + steps + "' )");
for (Sleep.SleepData data : sleepDatas)
conn.createStatement().execute("INSERT INTO total (sleep_start, sleep_end, duration) VALUE ( '" + data.start_date + "', '" + data.end_date + "', '" + data.duration + "' )");
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
notInterrupted = false;
} catch (Exception e) {
LOG.error("Non e' stato possibile aggingere i dati orari al database, riprovo fra un minuto");
retry = true;
} }
} }
}; };
return runnable;
} }
} }