Fix and last TODO

- Various fix in code
- Fitbit now support steps by minutes
- Added data retrieving from WebHook
- Added checkSteps
This commit is contained in:
2018-09-19 22:57:11 +02:00
parent 2ed9eef8d1
commit eea9dd50e0
14 changed files with 284 additions and 106 deletions

View File

@@ -102,11 +102,17 @@ public class DialogFlowWebHook {
} catch (NullPointerException e) { } catch (NullPointerException e) {
LOG.error("NESSUNA AZIONE TROVATA: "+ inputAction); LOG.error("NESSUNA AZIONE TROVATA: "+ inputAction);
text = ERROR; text = ERROR;
} catch (Exception e) {
LOG.error("Qualcosa e' andato storto: " + e.getMessage());
text = ERROR;
} }
if(text == null) if(text == null)
text = input.getResult().getFulfillment().getSpeech(); text = input.getResult().getFulfillment().getSpeech();
for(String param: inputParam.keySet())
text = text.replace("@"+param, inputParam.get(param).getAsString());
LOG.info("RISPOSTA: " + text); LOG.info("RISPOSTA: " + text);
output.setDisplayText(text); output.setDisplayText(text);
output.setSpeech(text); output.setSpeech(text);
@@ -135,7 +141,8 @@ public class DialogFlowWebHook {
* *
* @param params una mappa contenente tutti i parametri impostati da dialogflow * @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
* @throws Exception una qualunque eccezione che si vuole lanciare
*/ */
String doAction(Map<String, JsonElement> params); String doAction(Map<String, JsonElement> params) throws Exception;
} }
} }

View File

@@ -1,11 +1,5 @@
package device; 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.HeartRate;
import device.fitbitdata.Sleep; import device.fitbitdata.Sleep;
import device.fitbitdata.Steps; import device.fitbitdata.Steps;
@@ -13,6 +7,12 @@ import oauth.AuthFitbit;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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 * 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 * Ricevi i passi che l'utente ha effettuato nell'ultimo giorno
* *
* @return un intero rappresentante i passi effettuati * @return un intero rappresentante i passi effettuati
* @throws IOException nel caso la richiesta non vada a buon fine
*/ */
public synchronized int getSteps() { 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(); 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<Map<String, Object>> list = steps.getStepsData();
final int now = list.size()-1;
int totalSteps = 0;
for(int i=0; i<lastMinutes; i++)
totalSteps += (int)list.get(now-i).get("value");
return totalSteps;
}
/** /**
* 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 15 minuti * 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 * @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); } 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) * @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
*/ */
public synchronized double getHeartRate(int lastMinutes) { public synchronized double getHeartRate(int lastMinutes) {
if(lastMinutes<=0) if(lastMinutes<=0)
@@ -127,7 +146,6 @@ public class Fitbit {
* Ricevi le ore di sonno che l'utente ha fatto nell'ultimo giorno * Ricevi le ore di sonno che l'utente ha fatto nell'ultimo giorno
* *
* @return un intero rappresentante le ore passate a dormire * @return un intero rappresentante le ore passate a dormire
* @throws IOException nel caso la richiesta non vada a buon fine
*/ */
public synchronized long getHoursSleep() { public synchronized long getHoursSleep() {
sleep = update(Sleep.class, sleep,"1.2" + USER + "sleep/date/today.json"); sleep = update(Sleep.class, sleep,"1.2" + USER + "sleep/date/today.json");
@@ -141,7 +159,6 @@ public class Fitbit {
* - la durata del sonno<br> * - la durata del sonno<br>
* - la data di fine<br> * - la data di fine<br>
* @return una lista contenente ogni volta che l'utente ha dormito * @return una lista contenente ogni volta che l'utente ha dormito
* @throws IOException
*/ */
public synchronized List<Sleep.SleepData> getDetailedSleep() { public synchronized List<Sleep.SleepData> getDetailedSleep() {
sleep = update(Sleep.class, sleep,"1.2" + USER + "sleep/date/today.json"); sleep = update(Sleep.class, sleep,"1.2" + USER + "sleep/date/today.json");
@@ -169,16 +186,17 @@ public class Fitbit {
return variable; return variable;
} catch (NullPointerException e) { } catch (NullPointerException e) {
// do nothing and update // 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;
} }
/** /**

View File

@@ -80,6 +80,7 @@ public class Hue {
if(allLights.isEmpty()) if(allLights.isEmpty())
throw new NullPointerException("Non e' stato possibile connettersi alle luci"); throw new NullPointerException("Non e' stato possibile connettersi alle luci");
// TODO GIOVEDI impostare una luminosita' e un colore di default
double bri = 0; double bri = 0;
double hue = 0; double hue = 0;
for (String name: allLights.keySet()) { for (String name: allLights.keySet()) {
@@ -147,7 +148,7 @@ public class Hue {
else if (num>100) else if (num>100)
num=100; num=100;
setState("bri", (int) (num*MAX_BRIGHTNESS)/100 ); setState("bri", (int) (num*MAX_BRIGHTNESS)/100, true);
brightness.set(num); brightness.set(num);
} }
@@ -196,23 +197,13 @@ public class Hue {
public void changeColor(String colorName) { public void changeColor(String colorName) {
for (String regex: COLORS.keySet()) for (String regex: COLORS.keySet())
if(colorName.matches("(?i)" + regex)) 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 * Modifica il colore delle luci in modo da fare un bel effetto arcobaleno continuo
*/ */
public void colorLoop() { setState("effect", "colorloop"); } public void colorLoop() { setState("effect", "colorloop", false); }
/**
* Invia una richiesta a tutte le luci hue con l'attributo selezionato ed il suo valore<br>
* 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);
}
/** /**
* Invia una richiesta a tutte le luci hue con l'attributo selezionato ed il suo valore<br> * Invia una richiesta a tutte le luci hue con l'attributo selezionato ed il suo valore<br>
@@ -221,7 +212,7 @@ public class Hue {
* @param value il valore da inserire * @param value il valore da inserire
* @param transition se includere la transizione o meno * @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<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
map.put(attribute, value); map.put(attribute, value);
if(transition) 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 * Invia una richiesta a tutte le luci hue con gli attributi selezionati ed il loro valore
* @param attributes una mappa di attributi -> valori * @param attributes una mappa di attributi -> valori
*/ */
public synchronized void setState(Map<String, Object> attributes) { private synchronized void setState(Map<String, Object> attributes) {
String body = GsonFactory.getDefaultFactory().getGson().toJson(attributes); String body = GsonFactory.getDefaultFactory().getGson().toJson(attributes);
LOG.info("Setting: " + body); LOG.info("Setting: " + body);
for (String light : allLights.keySet()) { for (String light : allLights.keySet()) {

View File

@@ -1,19 +1,22 @@
package device.fitbitdata; 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.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; 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) @JsonIgnoreProperties(ignoreUnknown = true)
public class Steps extends FitbitData { public class Steps extends FitbitData {
private int steps = 0; private int steps;
private List<Map<String, Object>> stepsData;
@JsonProperty("activities-steps") @JsonProperty("activities-steps")
public void setSteps(Map<String, String>[] array) { private void setSteps(Map<String, String>[] array) {
SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd"); SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
Date now = new Date(); Date now = new Date();
String strDate = sdfDate.format(now); String strDate = sdfDate.format(now);
@@ -23,6 +26,12 @@ public class Steps extends FitbitData {
steps = Integer.parseInt(map.get("value")); steps = Integer.parseInt(map.get("value"));
} }
@JsonProperty("activities-steps-intraday")
private void setDetailedSteps(Map<String, Object> map) {
stepsData = new ArrayList<>((List<Map<String, Object>>) map.get("dataset"));
}
public void setSteps(int steps) { this.steps = steps; } public void setSteps(int steps) { this.steps = steps; }
public int getSteps() { return steps; } public int getSteps() { return steps; }
public List<Map<String, Object>> getStepsData() { return stepsData; }
} }

View File

@@ -41,6 +41,7 @@ public class SeniorAssistant {
* @param args i possibili argomenti da passare al programma * @param args i possibili argomenti da passare al programma
*/ */
public static void main(String[] args) { 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 VariousThreads threads = new VariousThreads(); // this should be the first action of the main
Map<String, String> arguments = getArgsMap(args); Map<String, String> arguments = getArgsMap(args);
@@ -65,22 +66,24 @@ public class SeniorAssistant {
LOG.warn(e.getMessage()); 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 { try {
LOG.info("Connessione al Fitbit, ignorare eventuale errore per setPermissionsToOwnerOnly..."); LOG.info("Connessione al Fitbit, ignorare eventuale errore per setPermissionsToOwnerOnly...");
Fitbit fitbit = new Fitbit(); fitbit = new Fitbit();
LOG.info("Connessione al database..."); LOG.info("Connessione al database...");
Database database = remoteDbUser == null ? new LocalDB() : new RemoteDB(remoteDbUser); Database database = remoteDbUser == null ? new LocalDB() : new RemoteDB(remoteDbUser);
threads.startInsertData(database, fitbit); threads.startInsertData(database, fitbit);
threads.startHueControlledByHeartBeat(lights, fitbit, database); threads.startHueControlledByHeartBeat(lights, fitbit, database);
threads.startCheckSteps(fitbit); threads.startCheckSteps(database);
} catch (Exception e) { } catch (Exception e) {
LOG.warn("Non e' stato possibile collegarsi al fitbit"); LOG.warn("Non e' stato possibile collegarsi al fitbit");
e.printStackTrace(); e.printStackTrace();
} }
threads.startWebhook(lights); threads.startWebhook(lights, fitbit);
} catch (Exception e) { } catch (Exception e) {
LOG.error(e.getMessage()); 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 DB in modo che si aggiorni ogni ora
XXX Gestione luci in modo che la luminosità sia sempre la stessa XXX Gestione luci in modo che la luminosità sia sempre la stessa
XXX Gestione luci a seconda del battito cardiaco 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 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 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
*/ */

View File

@@ -5,6 +5,7 @@ import device.Fitbit;
import device.Hue; import device.Hue;
import device.Sensor; import device.Sensor;
import device.fitbitdata.HeartRate; import device.fitbitdata.HeartRate;
import device.fitbitdata.Steps;
import support.audio.Audio; import support.audio.Audio;
import support.audio.AudioFile; import support.audio.AudioFile;
import support.audio.Musich; import support.audio.Musich;
@@ -15,6 +16,9 @@ import java.util.Calendar;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/**
* Classe che contiene tutti i thread che servono al programma per funzionare
*/
public class VariousThreads { public class VariousThreads {
/** /**
@@ -41,16 +45,15 @@ public class VariousThreads {
* Costruttore * Costruttore
*/ */
public VariousThreads() { 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(); 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 * Fa partire il server Webhook per DialogFlow e continua l'esecuzione
* @param lights le luci che deve controllare * @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(); DialogFlowWebHook df = new DialogFlowWebHook();
df.addOnAction("LightsON", (params) -> { lights.turnOn(); cooldown.set(COOLDOWN_IN_MINUTES); return null; }); 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; }); df.addOnAction("StopMusic", (params) -> { audio.stop(); return null; });
//TODO aggiungere una azione che faccia in modo di richiedere dei dati in particolare // TODO GIOVEDI aggiungere un orario magari? se no va bene cosi
//TODO aggiungere una azione su DialogFlow che riconosca di impostare una playlist (Rilassante, Antica...) 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(); df.startServer();
SeniorAssistant.LOG.info("Webhook partito"); SeniorAssistant.LOG.info("Webhook partito");
@@ -134,7 +152,7 @@ public class VariousThreads {
calculateBrightFactor( calculateBrightFactor(
calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE), calendar.get(Calendar.MINUTE),
sensor.getBrightnessLevel()/10, sensor.getBrightnessLevel(),
minBrightness minBrightness
); );
@@ -156,6 +174,7 @@ public class VariousThreads {
public void startHueControlledByHeartBeat(final Hue lights, final Fitbit fitbit, final Database database) { public void startHueControlledByHeartBeat(final Hue lights, final Fitbit fitbit, final Database database) {
final int minutes = 30; final int minutes = 30;
final int delta = 15; final int delta = 15;
final Audio audio = new AudioFile();
Thread thread = getThreadStartingEach(new Runnable() { Thread thread = getThreadStartingEach(new Runnable() {
@Override @Override
public synchronized void run() { public synchronized void run() {
@@ -177,15 +196,16 @@ public class VariousThreads {
} }
average = count!=0? sum/count:0; 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); double rateNow = fitbit.getHeartRate(minutes);
if ((rateNow-average) >= delta ) if (Math.abs(rateNow-average) > delta ) {
lights.decreaseBrightness(); lights.decreaseBrightness();
//avvisare con una voce registrata? audio.play("Tullio.wav");
else if ((rateNow-average) <= -delta) }
//alzare le luci? else if (Math.abs(rateNow-average) < delta) {
//avvisare con una voce registrata? lights.increaseBrightness();
; audio.play("Tullio.wav");
}
} }
}, minutes, "lights-with-heartbeat"); }, 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"); 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.<br> * Controlla che ad una certa ora si siano fatti abbastanza passi, ed in caso contrario avvisa tramite un messaggio vocale.<br>
* E' possibile trasformarlo in controlla ogni X se si sono fatti dei movimenti o meno. * 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) { public void startCheckSteps(final Database database) {
// trovare un orario (magari inserirlo tramite arg) final int minute = 24 * 60; // ogni ventiquattro ore circa
// a quell'ora controllare i passi fatti durante la giornata final int norm = 4500;
// se pochi mandare un avviso tramite dialogFlow(?) final int delta = 1500; // average steps for 60 year old -> 3.500-5.500 or so they say
// (secondo me si puo' evitare) 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<Steps> 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<Steps> 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; return maxIntensity-sensorBright;
} }
/**
* Restuisce un thread che se fatto partire, esegue il runnable in un sub-thread alll'ora indicata ogni giorno<br>
* Il sotto thread lanciato avra' lo stesso nome, ma con un trattino e la data di lancio a seguito<br>
* 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<br> * Restuisce un thread che se fatto partire, esegue il runnable in un sub-thread ogni X minuti<br>
* Il sotto thread lanciato avra' lo stesso nome, ma con un trattino e la data di lancio a seguito<br> * Il sotto thread lanciato avra' lo stesso nome, ma con un trattino e la data di lancio a seguito<br>
@@ -243,7 +331,7 @@ public class VariousThreads {
* @param minutes i minuti da aspettare, se negativi o 0 ritorna null * @param minutes i minuti da aspettare, se negativi o 0 ritorna null
* @param threadName il nome da dare al thread * @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) if(minutes<1)
return null; return null;

View File

@@ -26,7 +26,7 @@ public class AuthFitbit {
/** /**
* Un logger per rendere le cose semplici in caso di casini * 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. * Un mapper per trasformare i json in mappe.
@@ -132,7 +132,7 @@ public class AuthFitbit {
String content = response.parseAsString(); String content = response.parseAsString();
response.disconnect(); response.disconnect();
LOG.info("Recived: " + content); LOG.debug("Recived: " + content);
return content; return content;
} }
@@ -150,7 +150,7 @@ public class AuthFitbit {
*/ */
public <O> O run(String url, Class<O> returnClass) throws IOException { public <O> O run(String url, Class<O> returnClass) throws IOException {
O ret = MAPPER.readValue(this.run(url), returnClass); 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; return ret;
/**/ /**/

View File

@@ -24,7 +24,7 @@ public class AudioFile implements Audio {
/** /**
* L'ultimo audio fatto partire * L'ultimo audio fatto partire
*/ */
private AudioStream lastIn = null; private static AudioStream lastIn = null;
/** /**
* Serve per crearsi una mapp di tutte le canzoni * Serve per crearsi una mapp di tutte le canzoni

View File

@@ -3,13 +3,13 @@ package support.database;
import device.Fitbit; import device.Fitbit;
import device.fitbitdata.HeartRate; import device.fitbitdata.HeartRate;
import device.fitbitdata.Sleep; import device.fitbitdata.Sleep;
import device.fitbitdata.Steps;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.List; import java.util.List;
import static main.VariousThreads.MILLISEC_IN_MINUTE; import static main.VariousThreads.*;
import static main.VariousThreads.getThreadStartingEach;
public interface Database { public interface Database {
@@ -53,6 +53,13 @@ public interface Database {
*/ */
List<HeartRate> getHeartDataOfLast(int days); List<HeartRate> 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<Steps> getStepDataOfLast(int days);
/** /**
* Prendi il Thread che automaticamente gestisce l'inserimento dei dati orari (per ora solo il battito cardiaco)<br> * Prendi il Thread che automaticamente gestisce l'inserimento dei dati orari (per ora solo il battito cardiaco)<br>
* Se per caso c'e' un fallimento riprova ad inserire i dati ogni x minuti, indicati dal terzo parametro<br> * Se per caso c'e' un fallimento riprova ad inserire i dati ogni x minuti, indicati dal terzo parametro<br>
@@ -65,19 +72,20 @@ public interface Database {
Runnable runnable = new Runnable() { Runnable runnable = new Runnable() {
@Override @Override
public synchronized void run() { public synchronized void run() {
LOG.info("Aggiornamento orario iniziato");
try { try {
boolean retry; boolean retry;
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
double heartRate = 30;//fitbit.getHeartRate(60); double heartRate = fitbit.getHeartRate(60);
int steps = fitbit.getSteps(1);
do { do {
retry = !database.updateHeart(now, heartRate); 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) if (retry)
wait(retryMinutes * MILLISEC_IN_MINUTE); wait(retryMinutes * MILLISEC_IN_MINUTE);
} while(retry); } while(retry);
} catch (Exception e) { } 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<br> * Prendi il Thread che automaticamente gestisce l'inserimento dei dati giornalieri, esso fara' i tentativi alle 23<br>
* Se per caso c'e' un fallimento riprova ad inserire i dati ogni x minuti, indicati dal terzo parametro<br> * Se per caso c'e' un fallimento riprova ad inserire i dati ogni x minuti, indicati dal terzo parametro<br>
* @param database il database in cui inserirlo * @param database il database in cui inserirlo
* @param fitbit la classe che contiene i dati aggiornati * @param fitbit la classe che contiene i dati aggiornati
@@ -97,27 +105,23 @@ public interface Database {
Runnable runnable = new Runnable() { Runnable runnable = new Runnable() {
@Override @Override
public synchronized void run() { public synchronized void run() {
LOG.info("Aggiornamento giornaliero iniziato");
try { try {
boolean retry;
long steps = fitbit.getSteps();
List<Sleep.SleepData> sleepDatas = fitbit.getDetailedSleep(); List<Sleep.SleepData> sleepDatas = fitbit.getDetailedSleep();
long now = System.currentTimeMillis(); boolean retry = !sleepDatas.isEmpty();
do { do {
retry = !database.updateSteps(now, steps);
for (Sleep.SleepData data : sleepDatas) for (Sleep.SleepData data : sleepDatas)
retry = retry && !database.updateSleep(data.start_date, data.duration); 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) if (retry)
wait(retryMinutes * MILLISEC_IN_MINUTE); wait(retryMinutes * MILLISEC_IN_MINUTE);
} while (retry); } while (retry);
} catch (Exception e) { } 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");
} }
} }

View File

@@ -1,6 +1,7 @@
package support.database; package support.database;
import device.fitbitdata.HeartRate; import device.fitbitdata.HeartRate;
import device.fitbitdata.Steps;
import java.sql.*; import java.sql.*;
import java.util.LinkedList; import java.util.LinkedList;
@@ -112,6 +113,29 @@ public class LocalDB implements Database {
return null; return null;
} }
@Override
public List<Steps> 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<Steps> 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) { private boolean query(String sql) {
try { try {
conn.createStatement().execute(sql); conn.createStatement().execute(sql);

View File

@@ -3,6 +3,7 @@ package support.database;
import ai.api.GsonFactory; import ai.api.GsonFactory;
import com.google.gson.Gson; import com.google.gson.Gson;
import device.fitbitdata.HeartRate; import device.fitbitdata.HeartRate;
import device.fitbitdata.Steps;
import support.Rest; import support.Rest;
import java.sql.Timestamp; import java.sql.Timestamp;
@@ -73,13 +74,7 @@ public class RemoteDB implements Database {
@Override @Override
public List<HeartRate> getHeartDataOfLast(int days) { public List<HeartRate> getHeartDataOfLast(int days) {
try { try {
Calendar ago = Calendar.getInstance(); String url = base_url+"heartbeat/"+username+"/last/"+(days*24);
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/";
Map<String, List<Map<String, Object>>> map = (Map<String, List<Map<String, Object>>>) Rest.get(url); Map<String, List<Map<String, Object>>> map = (Map<String, List<Map<String, Object>>>) Rest.get(url);
List<HeartRate> list = new ArrayList<>(map.get("list").size()); List<HeartRate> list = new ArrayList<>(map.get("list").size());
@@ -97,6 +92,27 @@ public class RemoteDB implements Database {
} }
} }
@Override
public List<Steps> getStepDataOfLast(int days) {
try {
String url = base_url+"step/"+username+"/last/"+(days*24);
Map<String, List<Map<String, Object>>> map = (Map<String, List<Map<String, Object>>>) Rest.get(url);
List<Steps> list = new ArrayList<>(map.get("list").size());
for(Map<String, Object> 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 * Serve ad inviare una richiesta PUT per aggiornare i dati
* @param type il tipo di dato che deve esser aggiornato * @param type il tipo di dato che deve esser aggiornato

View File

@@ -2,9 +2,10 @@ package test;
import device.Fitbit; import device.Fitbit;
import org.junit.Test; import org.junit.Test;
import support.Rest;
public class TestFitbit { public class TestFitbit {
@Test @Test
public void test01() throws Exception { public void test01() throws Exception {
Fitbit fitBit = new Fitbit(); 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 hours of sleep: "+fitBit.getHoursSleep());
System.out.println("Today's steps: "+fitBit.getSteps()); System.out.println("Today's steps: "+fitBit.getSteps());
System.out.println("Fine."); 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));
}
} }

View File

@@ -1,6 +1,5 @@
package test; package test;
import ai.api.GsonFactory;
import device.Hue; import device.Hue;
import main.VariousThreads; import main.VariousThreads;
import org.junit.Before; import org.junit.Before;
@@ -51,12 +50,10 @@ public class TestLights {
@Test @Test
synchronized public void testColor() throws InterruptedException { synchronized public void testColor() throws InterruptedException {
// change colors String[] colors = {"rosso", "giallo", "verde", "blu", "bianco", "azzurro", "arancio"};
for (int i=0; i<=360; i++) {
double radian = (0.0174533*i); for (String color : colors) {
double x = Math.cos(radian); lights.changeColor(color);
double y = Math.sin(radian);
lights.setState("xy", GsonFactory.getDefaultFactory().getGson().toJson(new Double[]{x, y}));
this.wait(TIMEOUT); this.wait(TIMEOUT);
} }
} }

View File

@@ -34,6 +34,18 @@ public class TestMusich {
audio.stop(); 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) { public void waitAndPrint(Integer seconds) {
if(seconds != null) synchronized (seconds) { if(seconds != null) synchronized (seconds) {
try { try {