From eea9dd50e0c6f254c3fc039bc292244d83cf5f5c Mon Sep 17 00:00:00 2001 From: Giacomo Date: Wed, 19 Sep 2018 22:57:11 +0200 Subject: [PATCH] Fix and last TODO - Various fix in code - Fitbit now support steps by minutes - Added data retrieving from WebHook - Added checkSteps --- src/main/java/device/DialogFlowWebHook.java | 9 +- src/main/java/device/Fitbit.java | 60 ++++++--- src/main/java/device/Hue.java | 21 +-- src/main/java/device/fitbitdata/Steps.java | 21 ++- src/main/java/main/SeniorAssistant.java | 19 +-- src/main/java/main/VariousThreads.java | 130 ++++++++++++++++--- src/main/java/oauth/AuthFitbit.java | 6 +- src/main/java/support/audio/AudioFile.java | 2 +- src/main/java/support/database/Database.java | 34 ++--- src/main/java/support/database/LocalDB.java | 24 ++++ src/main/java/support/database/RemoteDB.java | 30 ++++- src/test/java/test/TestFitbit.java | 11 +- src/test/java/test/TestLights.java | 11 +- src/test/java/test/TestMusich.java | 12 ++ 14 files changed, 284 insertions(+), 106 deletions(-) diff --git a/src/main/java/device/DialogFlowWebHook.java b/src/main/java/device/DialogFlowWebHook.java index 06e60e9..3e32fd8 100644 --- a/src/main/java/device/DialogFlowWebHook.java +++ b/src/main/java/device/DialogFlowWebHook.java @@ -102,11 +102,17 @@ public class DialogFlowWebHook { } catch (NullPointerException e) { LOG.error("NESSUNA AZIONE TROVATA: "+ inputAction); text = ERROR; + } catch (Exception e) { + LOG.error("Qualcosa e' andato storto: " + e.getMessage()); + text = ERROR; } if(text == null) text = input.getResult().getFulfillment().getSpeech(); + for(String param: inputParam.keySet()) + text = text.replace("@"+param, inputParam.get(param).getAsString()); + LOG.info("RISPOSTA: " + text); output.setDisplayText(text); output.setSpeech(text); @@ -135,7 +141,8 @@ public class DialogFlowWebHook { * * @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 + * @throws Exception una qualunque eccezione che si vuole lanciare */ - String doAction(Map params); + String doAction(Map params) throws Exception; } } diff --git a/src/main/java/device/Fitbit.java b/src/main/java/device/Fitbit.java index d157f5e..d9efc76 100644 --- a/src/main/java/device/Fitbit.java +++ b/src/main/java/device/Fitbit.java @@ -1,11 +1,5 @@ package device; -import java.io.IOException; -import java.util.Calendar; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import device.fitbitdata.HeartRate; import device.fitbitdata.Sleep; import device.fitbitdata.Steps; @@ -13,6 +7,12 @@ import oauth.AuthFitbit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.util.Calendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * Classe che permette di ricevere i dati di un particolare account FitBit */ @@ -83,19 +83,39 @@ public class Fitbit { * Ricevi i passi che l'utente ha effettuato nell'ultimo giorno * * @return un intero rappresentante i passi effettuati - * @throws IOException nel caso la richiesta non vada a buon fine */ public synchronized int getSteps() { - steps = update(Steps.class, steps, "1" + USER + "activities/steps/date/today/1w.json"); + steps = update(Steps.class, steps, "1" + USER + "activities/steps/date/today/1d/1min.json"); return steps.getSteps(); } + /** + * Ricevi i passi che l'utente ha effettuato negli ultimi minuti richiesti + * + * @param lastMinutes gli ultimi minuti che si vogliono vedere (positivi e !=0 se no ritorno -1) + * @return un intero rappresentante i passi effettuati + */ + public synchronized int getSteps(int lastMinutes) { + if(lastMinutes<=0) + return -1; + + steps = update(Steps.class, steps, "1" + USER + "activities/steps/date/today/1d/1min.json"); + + List> list = steps.getStepsData(); + final int now = list.size()-1; + + int totalSteps = 0; + for(int i=0; i * Il risultato e' una media del battito che l'utente ha avuto negli 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 */ public synchronized double getHeartRate() { return getHeartRate(15); } @@ -105,7 +125,6 @@ public class Fitbit { * * @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 - * @throws IOException nel caso la richiesta non vada a buon fine */ public synchronized double getHeartRate(int lastMinutes) { if(lastMinutes<=0) @@ -127,7 +146,6 @@ public class Fitbit { * Ricevi le ore di sonno che l'utente ha fatto nell'ultimo giorno * * @return un intero rappresentante le ore passate a dormire - * @throws IOException nel caso la richiesta non vada a buon fine */ public synchronized long getHoursSleep() { sleep = update(Sleep.class, sleep,"1.2" + USER + "sleep/date/today.json"); @@ -141,7 +159,6 @@ public class Fitbit { * - la durata del sonno
* - la data di fine
* @return una lista contenente ogni volta che l'utente ha dormito - * @throws IOException */ public synchronized List getDetailedSleep() { sleep = update(Sleep.class, sleep,"1.2" + USER + "sleep/date/today.json"); @@ -169,16 +186,17 @@ public class Fitbit { return variable; } catch (NullPointerException e) { // do nothing and update - } finally { - LOG.info("Updating " + varClass.getSimpleName() + " form " + BASIC_URL + url); - try { - variable = auth.run(BASIC_URL + url, varClass); - latestRequest.put(varClass, System.currentTimeMillis()); - } catch (IOException e) { - LOG.error("Non sono riuscito a prender i dati aggiornati: " + e.getMessage()); - } - return variable; } + + LOG.info("Updating " + varClass.getSimpleName() + " form " + BASIC_URL + url); + try { + variable = auth.run(BASIC_URL + url, varClass); + latestRequest.put(varClass, System.currentTimeMillis()); + } catch (IOException e) { + LOG.error("Non sono riuscito a prender i dati aggiornati: " + e.getMessage()); + e.printStackTrace(); + } + return variable; } /** diff --git a/src/main/java/device/Hue.java b/src/main/java/device/Hue.java index eebad44..d30641a 100644 --- a/src/main/java/device/Hue.java +++ b/src/main/java/device/Hue.java @@ -80,6 +80,7 @@ public class Hue { if(allLights.isEmpty()) throw new NullPointerException("Non e' stato possibile connettersi alle luci"); + // TODO GIOVEDI impostare una luminosita' e un colore di default double bri = 0; double hue = 0; for (String name: allLights.keySet()) { @@ -147,7 +148,7 @@ public class Hue { else if (num>100) num=100; - setState("bri", (int) (num*MAX_BRIGHTNESS)/100 ); + setState("bri", (int) (num*MAX_BRIGHTNESS)/100, true); brightness.set(num); } @@ -196,23 +197,13 @@ public class Hue { public void changeColor(String colorName) { for (String regex: COLORS.keySet()) if(colorName.matches("(?i)" + regex)) - setState("xy", COLORS.get(regex)); + setState("xy", COLORS.get(regex), true); } /** * Modifica il colore delle luci in modo da fare un bel effetto arcobaleno continuo */ - public void colorLoop() { setState("effect", "colorloop"); } - - /** - * Invia una richiesta a tutte le luci hue con l'attributo selezionato ed il suo valore
- * Con esso invia anche una transition:20 in modo che sia piu fluido il cambiamento - * @param attribute l'attributo che si vuole cambiare - * @param value il valore da inserire - */ - public void setState(String attribute, Object value){ - setState(attribute, value, true); - } + public void colorLoop() { setState("effect", "colorloop", false); } /** * Invia una richiesta a tutte le luci hue con l'attributo selezionato ed il suo valore
@@ -221,7 +212,7 @@ public class Hue { * @param value il valore da inserire * @param transition se includere la transizione o meno */ - public void setState(String attribute, Object value, boolean transition){ + private void setState(String attribute, Object value, boolean transition){ Map map = new HashMap<>(); map.put(attribute, value); if(transition) @@ -233,7 +224,7 @@ public class Hue { * Invia una richiesta a tutte le luci hue con gli attributi selezionati ed il loro valore * @param attributes una mappa di attributi -> valori */ - public synchronized void setState(Map attributes) { + private synchronized void setState(Map attributes) { String body = GsonFactory.getDefaultFactory().getGson().toJson(attributes); LOG.info("Setting: " + body); for (String light : allLights.keySet()) { diff --git a/src/main/java/device/fitbitdata/Steps.java b/src/main/java/device/fitbitdata/Steps.java index f092152..edcfd9a 100644 --- a/src/main/java/device/fitbitdata/Steps.java +++ b/src/main/java/device/fitbitdata/Steps.java @@ -1,19 +1,22 @@ package device.fitbitdata; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Map; - import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + @JsonIgnoreProperties(ignoreUnknown = true) public class Steps extends FitbitData { - private int steps = 0; + private int steps; + private List> stepsData; @JsonProperty("activities-steps") - public void setSteps(Map[] array) { + private void setSteps(Map[] array) { SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd"); Date now = new Date(); String strDate = sdfDate.format(now); @@ -23,6 +26,12 @@ public class Steps extends FitbitData { steps = Integer.parseInt(map.get("value")); } + @JsonProperty("activities-steps-intraday") + private void setDetailedSteps(Map map) { + stepsData = new ArrayList<>((List>) map.get("dataset")); + } + public void setSteps(int steps) { this.steps = steps; } public int getSteps() { return steps; } + public List> getStepsData() { return stepsData; } } diff --git a/src/main/java/main/SeniorAssistant.java b/src/main/java/main/SeniorAssistant.java index d08972e..0b593c8 100644 --- a/src/main/java/main/SeniorAssistant.java +++ b/src/main/java/main/SeniorAssistant.java @@ -41,6 +41,7 @@ public class SeniorAssistant { * @param args i possibili argomenti da passare al programma */ public static void main(String[] args) { + //TODO magari aggiungere un arg con la path per la musica in modo che si possa ampliare in futuro VariousThreads threads = new VariousThreads(); // this should be the first action of the main Map arguments = getArgsMap(args); @@ -65,22 +66,24 @@ public class SeniorAssistant { LOG.warn(e.getMessage()); } + // Lo dichiaro qui, cosi' anche se non ci si puo' collegare al fitbit si puo comunque comandare le luci + Fitbit fitbit=null; try { LOG.info("Connessione al Fitbit, ignorare eventuale errore per setPermissionsToOwnerOnly..."); - Fitbit fitbit = new Fitbit(); + fitbit = new Fitbit(); LOG.info("Connessione al database..."); Database database = remoteDbUser == null ? new LocalDB() : new RemoteDB(remoteDbUser); threads.startInsertData(database, fitbit); threads.startHueControlledByHeartBeat(lights, fitbit, database); - threads.startCheckSteps(fitbit); + threads.startCheckSteps(database); } catch (Exception e) { LOG.warn("Non e' stato possibile collegarsi al fitbit"); e.printStackTrace(); } - threads.startWebhook(lights); + threads.startWebhook(lights, fitbit); } catch (Exception e) { LOG.error(e.getMessage()); } @@ -88,19 +91,19 @@ public class SeniorAssistant { } /* - TODO AUTOMATIC: {D} + AUTOMATIC: XXX Gestione DB in modo che si aggiorni ogni ora XXX Gestione luci in modo che la luminosità sia sempre la stessa XXX Gestione luci a seconda del battito cardiaco - /D/ Ogni X ore/minuti guarda i passi e se sono pochi dillo + XXX Ogni X ore/minuti guarda i passi e se sono pochi dillo XXX Se i battiti sono troppo bassi/alti avvisare il tizio - TODO USER-INTERACTION {A} + USER-INTERACTION: - /A/ Dati del sonno/battito/passi che l'utente puo' richiedere + XXX Dati del sonno/battito/passi che l'utente puo' richiedere XXX Gestione luci secondo le esigenze dell'utente ( settare Dialogflow e server + risolvere bug ) - XXX Gestione musica tramite comando vocale //TODO mettere a posto dialogflow e inserire qualche canzone + XXX Gestione musica tramite comando vocale //TODO inserire qualche canzone */ diff --git a/src/main/java/main/VariousThreads.java b/src/main/java/main/VariousThreads.java index 90c2d90..ac321f2 100644 --- a/src/main/java/main/VariousThreads.java +++ b/src/main/java/main/VariousThreads.java @@ -5,6 +5,7 @@ import device.Fitbit; import device.Hue; import device.Sensor; import device.fitbitdata.HeartRate; +import device.fitbitdata.Steps; import support.audio.Audio; import support.audio.AudioFile; import support.audio.Musich; @@ -15,6 +16,9 @@ import java.util.Calendar; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +/** + * Classe che contiene tutti i thread che servono al programma per funzionare + */ public class VariousThreads { /** @@ -41,16 +45,15 @@ public class VariousThreads { * Costruttore */ public VariousThreads() { - // audio = new AudioFile(); + // audio = new AudioFile(); se si vuole solamente questo e non YT audio = System.getProperty("os.name").startsWith("Windows")? new Musich():new AudioFile(); } - // TODO aggingere il fitbit per la richiesta dei dati /** * Fa partire il server Webhook per DialogFlow e continua l'esecuzione * @param lights le luci che deve controllare */ - public void startWebhook(final Hue lights) { + public void startWebhook(final Hue lights, final Fitbit fitbit) { DialogFlowWebHook df = new DialogFlowWebHook(); df.addOnAction("LightsON", (params) -> { lights.turnOn(); cooldown.set(COOLDOWN_IN_MINUTES); return null; }); @@ -87,8 +90,23 @@ public class VariousThreads { }); df.addOnAction("StopMusic", (params) -> { audio.stop(); return null; }); - //TODO aggiungere una azione che faccia in modo di richiedere dei dati in particolare - //TODO aggiungere una azione su DialogFlow che riconosca di impostare una playlist (Rilassante, Antica...) + // TODO GIOVEDI aggiungere un orario magari? se no va bene cosi + df.addOnAction("ReqHearthbeat", (params) -> { + double rate = fitbit.getHeartRate(60); + return "Il battito medio dell'ultima ora e' di "+rate; + }); + df.addOnAction("ReqSteps", (params) -> { + int steps = fitbit.getSteps(); + return "I passi fatti oggi sono "+steps; + }); + df.addOnAction("ReqDistance", (params) -> { + double steps = fitbit.getSteps(); + return String.format("Oggi hai percorso circa %.2f kilometri", steps/2000); + }); + df.addOnAction("ReqSleep", (params) -> { + long sleep = fitbit.getHoursSleep(); + return String.format("Oggi hai dormito per %.2f ore", (double)sleep/3600000); + }); df.startServer(); SeniorAssistant.LOG.info("Webhook partito"); @@ -134,7 +152,7 @@ public class VariousThreads { calculateBrightFactor( calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), - sensor.getBrightnessLevel()/10, + sensor.getBrightnessLevel(), minBrightness ); @@ -156,6 +174,7 @@ public class VariousThreads { public void startHueControlledByHeartBeat(final Hue lights, final Fitbit fitbit, final Database database) { final int minutes = 30; final int delta = 15; + final Audio audio = new AudioFile(); Thread thread = getThreadStartingEach(new Runnable() { @Override public synchronized void run() { @@ -177,15 +196,16 @@ public class VariousThreads { } average = count!=0? sum/count:0; - //TODO impostare azioni anche di {E} + //TODO avvisare con una voce registrata? far partire musica rilassante? double rateNow = fitbit.getHeartRate(minutes); - if ((rateNow-average) >= delta ) + if (Math.abs(rateNow-average) > delta ) { lights.decreaseBrightness(); - //avvisare con una voce registrata? - else if ((rateNow-average) <= -delta) - //alzare le luci? - //avvisare con una voce registrata? - ; + audio.play("Tullio.wav"); + } + else if (Math.abs(rateNow-average) < delta) { + lights.increaseBrightness(); + audio.play("Tullio.wav"); + } } }, minutes, "lights-with-heartbeat"); @@ -193,17 +213,49 @@ public class VariousThreads { SeniorAssistant.LOG.info("Thread per il controllo delle luci tramite il battito cardiaco partito"); } - // TODO Ad una certa ora guarda i passi e se sono pochi dillo /** * Controlla che ad una certa ora si siano fatti abbastanza passi, ed in caso contrario avvisa tramite un messaggio vocale.
* E' possibile trasformarlo in controlla ogni X se si sono fatti dei movimenti o meno. - * @param fitbit da dove vediamo se si sono fatti abbastanza passi + * @param database da dove vediamo se si sono fatti abbastanza passi */ - public void startCheckSteps(final Fitbit fitbit) { - // 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) + public void startCheckSteps(final Database database) { + final int minute = 24 * 60; // ogni ventiquattro ore circa + final int norm = 4500; + final int delta = 1500; // average steps for 60 year old -> 3.500-5.500 or so they say + final Audio audio = new AudioFile(); + + // ma la domanda e': ad una certa ora o ad ogni X ore? + // AD UNA DETERMINATA ORA + Thread thread = getThreadStartingAt(new Runnable() { + @Override + public synchronized void run() { + List list = database.getStepDataOfLast(1); + + double sum=0; + int size = list.size(); + for(Steps steps: list) + sum += steps.getSteps(); + final long average = size!=0? (long)(sum/size):0; + + /* Con normale dettata dal tizio e average e' il primo risultato della lista + List list = database.getStepDataOfLast(15); + + //for + + final long norm = size!=0? (long)(sum/size):0; + final long average = size!=0? list.get(0).getSteps():0; + */ + + //TODO avvisare con una voce registrata? + if (Math.abs(norm-average) > delta ) + audio.play("Tullio.wav"); + else if (Math.abs(norm-average) < delta) + audio.play("Tullio.wav"); + + } + }, 20, "checking-steps"); + + thread.start(); } @@ -235,6 +287,42 @@ public class VariousThreads { return maxIntensity-sensorBright; } + /** + * Restuisce un thread che se fatto partire, esegue il runnable in un sub-thread alll'ora indicata ogni giorno
+ * Il sotto thread lanciato avra' lo stesso nome, ma con un trattino e la data di lancio a seguito
+ * Se il thread viene interrotto non fa piu' partire il runnable e si ferma + * @param runnable il runnable da lanciare + * @param hour l'ora a cui far partire il thread, se negativa o maggiore di 23 ritorna null + * @param threadName il nome da dare al thread + */ + public static Thread getThreadStartingAt(final Runnable runnable, final int hour, String threadName) { + if(hour<0 || hour>23) + return null; + + return new Thread(new Runnable() { + @Override + public synchronized void run() { + boolean notInterrupted = true; + Calendar calendar = Calendar.getInstance(); + do { + try { + double hourCalculated = calendar.get(Calendar.HOUR_OF_DAY)+((double)calendar.get(Calendar.MINUTE)/60); + double hourToWait = hour-hourCalculated; + if(hourToWait<0) + hourToWait += 24; + + wait((long)(hourToWait * 60 * MILLISEC_IN_MINUTE)); + Thread thread = new Thread(runnable, threadName + "-" + new Timestamp(System.currentTimeMillis())); + thread.start(); + } catch (Exception e) { + e.printStackTrace(); + notInterrupted = false; + } + } while (notInterrupted); + } + }); + } + /** * Restuisce un thread che se fatto partire, esegue il runnable in un sub-thread ogni X minuti
* Il sotto thread lanciato avra' lo stesso nome, ma con un trattino e la data di lancio a seguito
@@ -243,7 +331,7 @@ public class VariousThreads { * @param minutes i minuti da aspettare, se negativi o 0 ritorna null * @param threadName il nome da dare al thread */ - public static Thread getThreadStartingEach(final Runnable runnable, int minutes, String threadName) { + public static Thread getThreadStartingEach(final Runnable runnable, final int minutes, String threadName) { if(minutes<1) return null; diff --git a/src/main/java/oauth/AuthFitbit.java b/src/main/java/oauth/AuthFitbit.java index 406fcaa..841fae9 100644 --- a/src/main/java/oauth/AuthFitbit.java +++ b/src/main/java/oauth/AuthFitbit.java @@ -26,7 +26,7 @@ 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 Auth"); /** * Un mapper per trasformare i json in mappe. @@ -132,7 +132,7 @@ public class AuthFitbit { String content = response.parseAsString(); response.disconnect(); - LOG.info("Recived: " + content); + LOG.debug("Recived: " + content); return content; } @@ -150,7 +150,7 @@ public class AuthFitbit { */ public O run(String url, Class returnClass) throws IOException { O ret = MAPPER.readValue(this.run(url), returnClass); - LOG.info("Saved in class: " + JSON_FACTORY.toString(ret)); + LOG.debug("Saved in class: " + JSON_FACTORY.toString(ret)); return ret; /**/ diff --git a/src/main/java/support/audio/AudioFile.java b/src/main/java/support/audio/AudioFile.java index 706f4a7..2b78a6d 100644 --- a/src/main/java/support/audio/AudioFile.java +++ b/src/main/java/support/audio/AudioFile.java @@ -24,7 +24,7 @@ public class AudioFile implements Audio { /** * L'ultimo audio fatto partire */ - private AudioStream lastIn = null; + private static AudioStream lastIn = null; /** * Serve per crearsi una mapp di tutte le canzoni diff --git a/src/main/java/support/database/Database.java b/src/main/java/support/database/Database.java index beeff57..79b2043 100644 --- a/src/main/java/support/database/Database.java +++ b/src/main/java/support/database/Database.java @@ -3,13 +3,13 @@ package support.database; import device.Fitbit; import device.fitbitdata.HeartRate; import device.fitbitdata.Sleep; +import device.fitbitdata.Steps; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; -import static main.VariousThreads.MILLISEC_IN_MINUTE; -import static main.VariousThreads.getThreadStartingEach; +import static main.VariousThreads.*; public interface Database { @@ -53,6 +53,13 @@ public interface Database { */ List getHeartDataOfLast(int days); + /** + * Riceve i dati dei passi dal giorno selezionato fino ad oggi + * @param days quanti giorni devono esser considerati + * @return una lista dei passifatti negli ultimi X giorni (ordinati da oggi al giorno X) + */ + List getStepDataOfLast(int days); + /** * Prendi il Thread che automaticamente gestisce l'inserimento dei dati orari (per ora solo il battito cardiaco)
* Se per caso c'e' un fallimento riprova ad inserire i dati ogni x minuti, indicati dal terzo parametro
@@ -65,19 +72,20 @@ public interface Database { Runnable runnable = new Runnable() { @Override public synchronized void run() { - LOG.info("Aggiornamento orario iniziato"); try { boolean retry; long now = System.currentTimeMillis(); - double heartRate = 30;//fitbit.getHeartRate(60); + double heartRate = fitbit.getHeartRate(60); + int steps = fitbit.getSteps(1); do { retry = !database.updateHeart(now, heartRate); - LOG.info("Aggiornamento " + (!retry ? "riuscito" : "fallito, riprovo fra " + retryMinutes + " minuti")); + retry = retry && !database.updateSteps(now, steps); + LOG.info("Aggiornamento orario " + (!retry ? "riuscito" : "fallito, riprovo fra " + retryMinutes + " minuti")); if (retry) wait(retryMinutes * MILLISEC_IN_MINUTE); } while(retry); } catch (Exception e) { - LOG.warn("Aggiornamento interrotto"); + LOG.warn("Aggiornamento orario interrotto"); } } }; @@ -86,7 +94,7 @@ public interface Database { } /** - * Prendi il Thread che automaticamente gestisce l'inserimento dei dati giornalieri
+ * Prendi il Thread che automaticamente gestisce l'inserimento dei dati giornalieri, esso fara' i tentativi alle 23
* Se per caso c'e' un fallimento riprova ad inserire i dati ogni x minuti, indicati dal terzo parametro
* @param database il database in cui inserirlo * @param fitbit la classe che contiene i dati aggiornati @@ -97,27 +105,23 @@ public interface Database { Runnable runnable = new Runnable() { @Override public synchronized void run() { - LOG.info("Aggiornamento giornaliero iniziato"); try { - boolean retry; - long steps = fitbit.getSteps(); List sleepDatas = fitbit.getDetailedSleep(); - long now = System.currentTimeMillis(); + boolean retry = !sleepDatas.isEmpty(); do { - retry = !database.updateSteps(now, steps); for (Sleep.SleepData data : sleepDatas) retry = retry && !database.updateSleep(data.start_date, data.duration); - LOG.info("Aggiornamento " + (!retry ? "riuscito" : "fallito, riprovo fra " + retryMinutes + " minuti")); + LOG.info("Aggiornamento giornaliero" + (!retry ? "riuscito" : "fallito, riprovo fra " + retryMinutes + " minuti")); if (retry) wait(retryMinutes * MILLISEC_IN_MINUTE); } while (retry); } catch (Exception e) { - LOG.warn("Aggiornamento interrotto"); + LOG.warn("Aggiornamento giornaliero interrotto"); } } }; - return getThreadStartingEach(runnable, 24*60, "update-daily-data"); + return getThreadStartingAt(runnable, 23, "update-daily-data"); } } diff --git a/src/main/java/support/database/LocalDB.java b/src/main/java/support/database/LocalDB.java index c57c18c..762374c 100644 --- a/src/main/java/support/database/LocalDB.java +++ b/src/main/java/support/database/LocalDB.java @@ -1,6 +1,7 @@ package support.database; import device.fitbitdata.HeartRate; +import device.fitbitdata.Steps; import java.sql.*; import java.util.LinkedList; @@ -112,6 +113,29 @@ public class LocalDB implements Database { return null; } + @Override + public List getStepDataOfLast(int days) { + try { + int dayToSubtract = 15; + long time = System.currentTimeMillis() - (dayToSubtract * 24 * 60 * 1000); + + ResultSet result = conn.createStatement().executeQuery("SELECT * FROM steps WHERE day>='" + new Timestamp(time) + "'"); + List list = new LinkedList<>(); + + while(result.next()) { + Steps steps = new Steps(); + steps.setSteps(result.getInt("rate")); + steps.setDate(result.getDate("day").getTime()); + + list.add(steps); + } + return list; + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + private boolean query(String sql) { try { conn.createStatement().execute(sql); diff --git a/src/main/java/support/database/RemoteDB.java b/src/main/java/support/database/RemoteDB.java index bed4b4e..2a389aa 100644 --- a/src/main/java/support/database/RemoteDB.java +++ b/src/main/java/support/database/RemoteDB.java @@ -3,6 +3,7 @@ package support.database; import ai.api.GsonFactory; import com.google.gson.Gson; import device.fitbitdata.HeartRate; +import device.fitbitdata.Steps; import support.Rest; import java.sql.Timestamp; @@ -73,13 +74,7 @@ public class RemoteDB implements Database { @Override public List getHeartDataOfLast(int days) { try { - Calendar ago = Calendar.getInstance(); - ago.setTimeInMillis(System.currentTimeMillis()); - ago.add(Calendar.DATE, -days); - - DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); - - String url = base_url+"heartbeat/"+username+"/"+format.format(ago.getTime())+"/today/"; + String url = base_url+"heartbeat/"+username+"/last/"+(days*24); Map>> map = (Map>>) Rest.get(url); List list = new ArrayList<>(map.get("list").size()); @@ -97,6 +92,27 @@ public class RemoteDB implements Database { } } + @Override + public List getStepDataOfLast(int days) { + try { + String url = base_url+"step/"+username+"/last/"+(days*24); + Map>> map = (Map>>) Rest.get(url); + + List list = new ArrayList<>(map.get("list").size()); + for(Map data: map.get("list")) { + Steps steps = new Steps(); + steps.setSteps((int)data.get("value")); + steps.setDate(STD_FORMAT.parse((String)data.get("time")).getTime()); + + list.add(steps); + } + return list; + } catch (ParseException e) { + LOG.error(e.getMessage()); + return null; + } + } + /** * Serve ad inviare una richiesta PUT per aggiornare i dati * @param type il tipo di dato che deve esser aggiornato diff --git a/src/test/java/test/TestFitbit.java b/src/test/java/test/TestFitbit.java index 6e3e4fc..cddb86d 100644 --- a/src/test/java/test/TestFitbit.java +++ b/src/test/java/test/TestFitbit.java @@ -2,9 +2,10 @@ package test; import device.Fitbit; import org.junit.Test; +import support.Rest; public class TestFitbit { - + @Test public void test01() throws Exception { Fitbit fitBit = new Fitbit(); @@ -14,5 +15,13 @@ public class TestFitbit { System.out.println("Today's hours of sleep: "+fitBit.getHoursSleep()); System.out.println("Today's steps: "+fitBit.getSteps()); System.out.println("Fine."); + + Rest.get("https://api.fitbit.com/1/user/-/activities/steps/date/today/1d.json"); } + + @Test + public void test02() throws Exception { + Fitbit fitbit = new Fitbit(); + System.out.println(fitbit.getSteps(60)); + } } diff --git a/src/test/java/test/TestLights.java b/src/test/java/test/TestLights.java index 86f383b..f4c0ce9 100644 --- a/src/test/java/test/TestLights.java +++ b/src/test/java/test/TestLights.java @@ -1,6 +1,5 @@ package test; -import ai.api.GsonFactory; import device.Hue; import main.VariousThreads; import org.junit.Before; @@ -51,12 +50,10 @@ public class TestLights { @Test synchronized public void testColor() throws InterruptedException { - // change colors - for (int i=0; i<=360; i++) { - double radian = (0.0174533*i); - double x = Math.cos(radian); - double y = Math.sin(radian); - lights.setState("xy", GsonFactory.getDefaultFactory().getGson().toJson(new Double[]{x, y})); + String[] colors = {"rosso", "giallo", "verde", "blu", "bianco", "azzurro", "arancio"}; + + for (String color : colors) { + lights.changeColor(color); this.wait(TIMEOUT); } } diff --git a/src/test/java/test/TestMusich.java b/src/test/java/test/TestMusich.java index df898cf..9732491 100644 --- a/src/test/java/test/TestMusich.java +++ b/src/test/java/test/TestMusich.java @@ -34,6 +34,18 @@ public class TestMusich { audio.stop(); } + @Test + public void test3() { + AudioFile audio = new AudioFile(); + AudioFile audio2 = new AudioFile(); + audio.play("Tullio.wav"); + waitAndPrint(3); + audio2.play("Tullio.wav"); + waitAndPrint(10); + audio.stop(); + audio2.stop(); + } + public void waitAndPrint(Integer seconds) { if(seconds != null) synchronized (seconds) { try {