Some changes

- Database now has insert in background (not tested tho)
- Hue lights tested on simulator
- LocalServerReciver.java is now part of package, but still throw exception (maybe is not only this class)
- Sleep class have changed (not tested)
- Fitbit now thread safe (maybe?)
- build.gradle cleaned

* for all the classes not tested is because i need the account fitbit, and TROPPO SBATTI for ask
This commit is contained in:
2018-09-01 01:54:09 +02:00
parent f2e8991049
commit d891e53c8e
15 changed files with 672 additions and 172 deletions

View File

@@ -16,14 +16,9 @@ repositories {
dependencies {
// Tests
testCompile group: 'junit', name: 'junit', version: '4.12'
// implementation 'junit:junit:4.12'
// GSON but works even without it anyway
// compile "com.google.code.gson:gson:2.8.4"
// Database
compile "org.xerial:sqlite-jdbc:3.21.0.1"
// compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.12'
// Rest request
compile 'org.apache.httpcomponents:httpclient:4.5.6'
@@ -36,26 +31,19 @@ dependencies {
// Server Spark
compile "com.sparkjava:spark-core:2.7.2"
// compile 'org.apache.commons:commons-lang3:3.4'
// compile group: 'org.eclipse.jetty', name: 'jetty-webapp', version: '9.4.11.v20180605'
// compile 'org.eclipse.jetty:jetty-client:9.4.11.v20180605'
// compile 'org.eclipse.jetty.websocket:websocket-server:9.4.11.v20180605'
// Oauth
// compile group: 'org.pac4j', name: 'spark-pac4j', version: '2.3.0' //todo use this insted?
compile ( group: 'com.google.oauth-client', name: 'google-oauth-client-jetty', version: '1.23.0') {
// don't pull in an old ancient jetty version
// todo it doesn't seems to work
exclude group: 'org.mortbay.jetty', module: 'jetty'
exclude group: 'org.mortbay.jetty', module: 'jetty-util'
exclude group: 'org.mortbay.jetty', module: 'servlet-api'
}
// compile group: 'org.mortbay.jetty', name: 'jetty', version: '7.0.0.pre5'
// compile group: 'org.mortbay.jetty', name: 'servlet-api', version: '3.0.20100224'
compile 'com.google.api-client:google-api-client:1.23.0'
// DialogFlow
compile "ai.api:libai:1.6.12"
// compile 'com.google.cloud:google-cloud-dialogflow:0.59.0-alpha' // for the v2 of dialogflow
compile group: "ai.api", name:"libai", version:"1.6.12"
// objectMapper for fitbitdata
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.5' // maybe duplicate in google-api

View File

@@ -4,6 +4,7 @@ import ai.api.GsonFactory;
import ai.api.model.AIResponse;
import ai.api.model.Fulfillment;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -78,7 +79,7 @@ public class DialogFlowWebHook {
try {
log.info("AZIONE: "+input.getResult().getAction());
Action action = actions.get(input.getResult().getAction());
text = action.doAction();
text = action.doAction(input.getResult().getParameters());
} catch (NullPointerException e) {
log.info("NESSUNA AZIONE TROVATA");
text = ERROR;
@@ -102,8 +103,10 @@ public class DialogFlowWebHook {
/**
* Fai l'azione desiderata.
* 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
* @return Una stringa che verra' usata come messaggio o null se non si vuole
*/
String doAction();
String doAction(Map<String, JsonElement> params);
}
}

View File

@@ -3,6 +3,7 @@ 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;
@@ -74,9 +75,8 @@ public class Fitbit {
* @return un intero rappresentante i passi effettuati
* @throws IOException nel caso la richiesta non vada a buon fine
*/
public int getSteps() throws IOException {
if (shouldUpdateFor(Steps.class))
steps = auth.run(BASIC_URL + "1" + USER + "activities/steps/date/today/1w.json", Steps.class);
public synchronized int getSteps() throws IOException {
steps = update(Steps.class, steps, "1" + USER + "activities/steps/date/today/1w.json");
return steps.getSteps();
}
@@ -87,9 +87,10 @@ public class Fitbit {
* @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 double getHeartRate() throws IOException {
public synchronized double getHeartRate() throws IOException {
return getHeartRate(15);
}
/**
* Ricevi il battito cardiaco dell'utente<br>
* Il risultato e' una media del battito che l'utente ha avuto negli ultimi minuti
@@ -98,22 +99,19 @@ public class Fitbit {
* @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 double getHeartRate(int lastMinutes) throws IOException {
public synchronized double getHeartRate(int lastMinutes) throws IOException {
if(lastMinutes<=0)
return -1;
if (shouldUpdateFor(HeartRate.class)) {
long currentMillisec = System.currentTimeMillis();
String now = getHourMinutes(currentMillisec);
String ago = getHourMinutes(currentMillisec - (MINUTE * lastMinutes));
long currentMillisec = System.currentTimeMillis();
if (now.compareTo(ago) < 0)
ago = "00:00";
String now = getHourMinutes(currentMillisec);
String ago = getHourMinutes(currentMillisec - (MINUTE * lastMinutes));
heart = auth.run(
BASIC_URL + "1" + USER + "activities/heart/date/today/1d/1sec/time/" + ago + "/" + now + ".json",
HeartRate.class);
}
if (now.compareTo(ago) < 0)
ago = "00:00";
heart = update(HeartRate.class, heart,"1" + USER + "activities/heart/date/today/1d/1sec/time/" + ago + "/" + now + ".json");
return heart.getAverage();
}
@@ -123,33 +121,48 @@ public class Fitbit {
* @return un intero rappresentante le ore passate a dormire
* @throws IOException nel caso la richiesta non vada a buon fine
*/
public Object getHoursSleep() throws IOException {
if (shouldUpdateFor(Sleep.class))
sleep = auth.run(BASIC_URL + "1.2" + USER + "sleep/date/today.json", Sleep.class);
return sleep.getMinutesAsleep();
public synchronized long getHoursSleep() throws IOException {
sleep = update(Sleep.class, sleep,"1.2" + USER + "sleep/date/today.json");
return sleep.getMinutesAsleep()/60;
}
/**
* Semplice classe che controlla che si possa fare l'update o meno di una specifica classe<br>
* Se e' possibile fare l'update inserisce la classe nella mappa<br>
* In questo modo se questa funzione viene chiamata una seconda volta con lo stesso parametro restituira' falso<br>
* a meno che non si aspetti 5 minuti
*
* @param type la classe da fare l'update
* @return vero se si puo' fare l'update
* Ricevi tutti i dati presenti per il sonno di questo giorno.
* La lista contiene per ogni volta che l'utente ha dormito:<br>
* - la data di quando si e' addormentato<br>
* - la durata del sonno<br>
* - la data di fine<br>
* @return una lista contenente ogni volta che l'utente ha dormito
* @throws IOException
*/
private boolean shouldUpdateFor(Class<?> type) {
public synchronized List<Sleep.SleepData> getDetailedSleep() throws IOException {
sleep = update(Sleep.class, sleep,"1.2" + USER + "sleep/date/today.json");
return sleep.getDatas();
}
/**
* 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>
* Altrimenti viene ritornata la variabile passata
*
* @param varClass la classe della variabile passata
* @param variable la variabile che vede fare l'update
* @param url l'url da cui prende i dati aggiornati
* @return la variabile aggiornata
*/
private <T> T update(Class<T> varClass, T variable, String url) throws IOException {
try {
long current = System.currentTimeMillis();
long latest = latestRequest.get(type);
long latest = latestRequest.get(varClass);
// don't update
if(current - latest < MINUTE * 5)
return false;
return variable;
} catch (NullPointerException e) {
// do nothing and update
}
latestRequest.put(heart.getClass(), System.currentTimeMillis());
return true;
latestRequest.put(varClass, System.currentTimeMillis());
return auth.run(BASIC_URL + url, varClass);
}
/**

View File

@@ -1,8 +1,10 @@
package device;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import ai.api.GsonFactory;
import support.Rest;
/**
@@ -13,7 +15,27 @@ public class Hue {
/**
* La luminopsita' massima a cui si puo' arrivare
*/
public static final int MAX_BRIGHTNESS = 255;
public static final int MAX_BRIGHTNESS = 254;
/**
* Una mappa che ad ogni colore (in lingua ita) assegna il proprio valore in hue
*/
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";
/**
@@ -38,14 +60,13 @@ public class Hue {
this("172.30.1.138", "C0vPwqjJZo5Jt9Oe5HgO6sBFFMxgoR532IxFoGmx");
}
//todo maybe the key is the user, but who knows?
/**
* Cerca le luci Philips Hue nell'indirizzo specificato e con la chiave specificata
* Cerca le luci Philips Hue nell'indirizzo specificato e con l'utente specificato
* @param ip l'indirizzo IP
* @param key la chiuave utililzzata
* @param user l'utente
*/
public Hue(String ip, String key) {
this("http://" + ip + "/api/" + key + "/lights/");
public Hue(String ip, String user) {
this("http://" + ip + "/api/" + user + "/lights/");
}
/**
@@ -61,7 +82,7 @@ public class Hue {
/**
* Ritorna un insieme contenente tutti i nomi delle luci che si sono trovate
*
*
* @return l'insieme dei nomi delle luci
*/
public Set<String> getNameLights() {
@@ -82,22 +103,14 @@ public class Hue {
* Accende tutte le luci controllate
*/
public void turnOn() {
for (String light : allLights.keySet()) {
String callURL = lightsURL + light + "/state";
String body = "{ \"on\" : true }";
Rest.put(callURL, body, "application/json");
}
setState("on", "true");
}
/**
* Spegne tutte le luci controllate
*/
public void turnOff() {
for (String light : allLights.keySet()) {
String callURL = lightsURL + light + "/state";
String body = "{ \"on\" : false }";
Rest.put(callURL, body, "application/json");
}
setState("on", "false");
}
/**
@@ -115,11 +128,7 @@ public class Hue {
public void setBrightness(int num) {
if (num<0)
num=0;
for (String light : allLights.keySet()) {
String callURL = lightsURL + light + "/state";
String body = "{ \"bri\" : "+num+" }";
Rest.put(callURL, body, "application/json");
}
setState("bri", String.valueOf(num));
brightness = num;
}
@@ -161,25 +170,27 @@ public class Hue {
decreaseBrightness(10);
}
public void changeColor(String colorName) {
String hueColor = GsonFactory.getDefaultFactory().getGson().toJson(COLORS.get(colorName));
setState("xy", hueColor);
}
/**
* Modifica il colore delle luci in modo da fare un bel effetto arcobaleno continuo
*/
public void colorLoop() {
for (String light : allLights.keySet()) {
String callURL = lightsURL + light + "/state";
String body = "{ \"on\" : true, \"effect\" : \"colorloop\" }";
Rest.put(callURL, body, "application/json");
}
setState("effect", "colorloop");
}
/**
* Funzione generale per poter utilizzare qualunque valore, ma non funziona
* Funzione generale per poter utilizzare qualunque valore, ma non funziona<br>
* Da testare, visto che mi sembra strano che non funzi...
* e invece funziona.
*/
/*public void setAttribute(String attribute, String value){
for (String light : allLights.keySet()) {
String callURL = lightsURL + light + "/state";
String body = "{ \""+attribute+"\" : "+value+" }";
Rest.put(callURL, body, "application/json");
}
}*/
public void setState(String attribute, String value){
for (String light : allLights.keySet())
Rest.put(lightsURL + light + "/state",
"{ \"" + attribute + "\" : " + value + " }",
"application/json");
}
}

View File

@@ -3,6 +3,11 @@ package device.fitbitdata;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
@JsonIgnoreProperties(ignoreUnknown = true)
@@ -10,12 +15,47 @@ public class Sleep {
private int minutesAsleep;
private List<SleepData> datas;
public int getMinutesAsleep() {
return minutesAsleep;
}
public List<SleepData> getDatas() {
return datas;
}
@JsonProperty("summary")
public void setMinutesAsleep(Map<String, Object> map) {
minutesAsleep = (int) map.get("totalMinutesAsleep");
}
@JsonProperty("sleep")
public void setSleepsList(Map<String, Object>[] array) {
datas = new ArrayList<>();
SimpleDateFormat sdf = new SimpleDateFormat();
for(Map<String, Object> map : array) {
Date date_start = null;
try {
date_start = sdf.parse((String) map.get("startTime"));
} catch (ParseException e) {
e.printStackTrace();
}
int duration = (int) map.get("duration");
datas.add(new SleepData(date_start, duration));
}
}
public class SleepData {
public final Date start_date;
public final long duration;
public final Date end_date;
public SleepData(Date start_date, long duration) {
this.start_date = start_date;
this.duration = duration;
this.end_date = start_date!=null? new Date(start_date.getTime() + duration):null;
}
}
}

View File

@@ -1,10 +1,14 @@
package main;
import ai.api.GsonFactory;
import device.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import support.DBConnect;
import support.Database;
import java.io.IOException;
// todo docs
/**
* Created by 20015159 on 28/08/2018.
*/
@@ -13,66 +17,76 @@ public class Main {
/**
* Un Logger per capire meglio quali pezzi vengono eseguiti e quali no.
*/
private static Logger log = LoggerFactory.getLogger("SeniorAssistant");
private static final Logger LOG = LoggerFactory.getLogger("SeniorAssistant");
private static Hue lights;
private static Fitbit fitbit;
private static Sensor sensor;
private static Database database;
/**
* Funzione principale, qui si creano tutte le classi che verranno utilizzate.
* @param args per ora nulla, ma forse in futuro si potrebbe mettere roba
*/
public static void main(String[] args) {
log.info("Connecting to hue lights");
Hue lights = new Hue();
log.info("Connecting to the sensors");
Sensor sensor = new Sensor();
LOG.info("Connecting to hue lights");
lights = new Hue("localhost:8090", "newdeveloper");
LOG.info("Connecting to the sensors");
sensor = new Sensor();
try {
log.info("Connecting to Fitbit");
Fitbit fitbit = new Fitbit();
startInsertData(fitbit); // add here functions associated with fitbit
} catch (Exception e) { // in this way the program will continue without fitbit
LOG.info("Connecting to Database");
database = Database.getInstance();
LOG.info("Connecting to Fitbit");
fitbit = new Fitbit();
startInsertData();
// add here functions associated with fitbit
} catch (Exception e) { // in this way the program will continue even without fitbit
e.printStackTrace();
}
startWebhook(lights);
startWebhook();
}
/**
* Fa partire il server Webhook per DialogFlow e continua l'esecuzione
* @param hue Le luci che vengono modificate a seconda delle richieste dell'utente
*/
private static void startWebhook(Hue hue) {
log.info("Adding actions to Webhook");
private static void startWebhook() {
LOG.info("Adding actions to Webhook");
DialogFlowWebHook df = new DialogFlowWebHook();
df.addOnAction("LightsON", () -> {hue.turnOn(); return "Luci accese";});
df.addOnAction("LightsOFF", () -> {hue.turnOff(); return "Luci spente";});
df.addOnAction("LightsON", (params) -> {lights.turnOn(); return null;});
df.addOnAction("LightsOFF", (params) -> {lights.turnOff(); return null;});
df.addOnAction("ColorLoop", (params) -> {lights.colorLoop(); return null;});
df.addOnAction("ChangeColor", (params) -> {lights.changeColor(params.get("color").getAsString()); return null;});
log.info("Starting Webhook");
LOG.info("Starting Webhook");
df.startServer();
}
/**
* Cose da fare in questa funzione:
* - far partire il database
* - ogni ora aggiornare i dati del cuore. (Runnable che gira da se')
* - alla fine della giornata fare un riepilogo del paziente (Runnable che gira da se')
* (magari ci si calcola quando bisogna risvegliarsi e si mette un wait)
* @param fibit da dove prende i dati
* Gestione DB in modo che si aggiorni ogni ora
*/
private static void startInsertData(Fitbit fibit) {
log.info("Connecting to DB to write fitbit data periodically");
/*
private static void startInsertData() {
LOG.info("Connecting to DB to write fitbit data periodically");
try {
Connection conn = DBConnect.getInstance().getConnection();
PreparedStatement st = conn.prepareStatement("");
fitbit.getHoursSleep();
ResultSet rs = st.executeQuery();
conn.close();
Database database = Database.getInstance();
} catch (SQLException e) {
Thread hourlyData = new Thread(database.insertHourlyData(fitbit), "updating-hour-data");
Thread dailyData = new Thread(database.insertDailyData(fitbit), "updating-day-data");
hourlyData.start();
dailyData.start();
LOG.info("Threads started for updating database");
} catch (IOException e) {
e.printStackTrace();
}
*/
}
/*
@@ -86,7 +100,7 @@ public class Main {
int brightness = sensor.getBrightnessLevel();
// AUTOMATIC
// Gestione DB in modo che si aggiorni ogni ora
/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
@@ -94,7 +108,7 @@ public class Main {
// USER-INTERACTION
// Dati del sonno/battito/passi che l'utente puo' richiedere
// Gestione luci secondo le esigenze dell'utente
/X/ Gestione luci secondo le esigenze dell'utente ( settare Dialogflow e server + risolvere bug )
// EXTRA Gestione musica tramite comando vocale
// Randomly at night heavy metal start

View File

@@ -3,12 +3,12 @@ package oauth;
import java.io.IOException;
import java.util.Arrays;
import ai.api.GsonFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.api.client.auth.oauth2.AuthorizationCodeFlow;
import com.google.api.client.auth.oauth2.BearerToken;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.http.BasicAuthentication;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
@@ -22,10 +22,15 @@ import com.google.api.client.json.JsonObjectParser;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.store.DataStoreFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//todo add docs
public class AuthFitbit {
private static final Logger LOG = LoggerFactory.getLogger("Fitbit Response");
private final HttpRequestFactory requestFactory;
public AuthFitbit() throws Exception {
@@ -114,6 +119,10 @@ public class AuthFitbit {
);
response.disconnect();
// todo remove this, it's only useful if you need to see the request
LOG.info(GsonFactory.getDefaultFactory().getGson().toJson(ret));
return ret;
/**/
}

View File

@@ -3,6 +3,7 @@ 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

View File

@@ -0,0 +1,298 @@
/*
* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
// todo test if fitbit request still works with this class
package oauth;
import com.google.api.client.extensions.java6.auth.oauth2.VerificationCodeReceiver;
import com.google.api.client.util.Throwables;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.Semaphore;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* OAuth 2.0 verification code receiver that runs a Jetty server on a free port, waiting for a
* redirect with the verification code.
*
* <p>
* Implementation is thread-safe.
* </p>
*
* @since 1.11
* @author Yaniv Inbar
*/
public final class LocalServerReceiver implements VerificationCodeReceiver {
private static final String LOCALHOST = "localhost";
private static final String CALLBACK_PATH = "/Callback";
/** Server or {@code null} before {@link #getRedirectUri()}. */
private Server server;
/** Verification code or {@code null} for none. */
String code;
/** Error code or {@code null} for none. */
String error;
/** To block until receiving an authorization response or stop() is called. */
final Semaphore waitUnlessSignaled = new Semaphore(0 /* initially zero permit */);
/** Port to use or {@code -1} to select an unused port in {@link #getRedirectUri()}. */
private int port;
/** Host name to use. */
private final String host;
/** Callback path of redirect_uri */
private final String callbackPath;
/**
* URL to an HTML page to be shown (via redirect) after successful login. If null, a canned
* default landing page will be shown (via direct response).
*/
private String successLandingPageUrl;
/**
* URL to an HTML page to be shown (via redirect) after failed login. If null, a canned
* default landing page will be shown (via direct response).
*/
private String failureLandingPageUrl;
/**
* Constructor that starts the server on {@link #LOCALHOST} and an unused port.
*
* <p>
* Use {@link Builder} if you need to specify any of the optional parameters.
* </p>
*/
public LocalServerReceiver() {
this(LOCALHOST, -1, CALLBACK_PATH, null, null);
}
/**
* Constructor.
*
* @param host Host name to use
* @param port Port to use or {@code -1} to select an unused port
*/
LocalServerReceiver(String host, int port,
String successLandingPageUrl, String failureLandingPageUrl) {
this(host, port, CALLBACK_PATH, successLandingPageUrl, failureLandingPageUrl);
}
/**
* Constructor.
*
* @param host Host name to use
* @param port Port to use or {@code -1} to select an unused port
*/
LocalServerReceiver(String host, int port, String callbackPath,
String successLandingPageUrl, String failureLandingPageUrl) {
this.host = host;
this.port = port;
this.callbackPath = callbackPath;
this.successLandingPageUrl = successLandingPageUrl;
this.failureLandingPageUrl = failureLandingPageUrl;
}
@Override
public String getRedirectUri() throws IOException {
server = new Server(port != -1 ? port : 0);
//Connector connector = server.getConnectors()[0];
//connector.setHost(host);
server.setHandler(new CallbackHandler());
try {
server.start();
//port = connector.getLocalPort();
} catch (Exception e) {
Throwables.propagateIfPossible(e);
throw new IOException(e);
}
return "http://" + host + ":" + port + callbackPath;
}
/**
* Blocks until the server receives a login result, or the server is stopped
* by {@link #stop()}, to return an authorization code.
*
* @return authorization code if login succeeds; may return {@code null} if the server
* is stopped by {@link #stop()}
* @throws IOException if the server receives an error code (through an HTTP request
* parameter {@code error})
*/
@Override
public String waitForCode() throws IOException {
waitUnlessSignaled.acquireUninterruptibly();
if (error != null) {
throw new IOException("User authorization failed (" + error + ")");
}
return code;
}
@Override
public void stop() throws IOException {
waitUnlessSignaled.release();
if (server != null) {
try {
server.stop();
} catch (Exception e) {
Throwables.propagateIfPossible(e);
throw new IOException(e);
}
server = null;
}
}
/** Returns the host name to use. */
public String getHost() {
return host;
}
/**
* Returns the port to use or {@code -1} to select an unused port in {@link #getRedirectUri()}.
*/
public int getPort() {
return port;
}
/**
* Returns callback path used in redirect_uri.
*/
public String getCallbackPath() {
return callbackPath;
}
/**
* Builder.
*
* <p>
* Implementation is not thread-safe.
* </p>
*/
public static final class Builder {
/** Host name to use. */
private String host = LOCALHOST;
/** Port to use or {@code -1} to select an unused port. */
private int port = -1;
private String successLandingPageUrl;
private String failureLandingPageUrl;
private String callbackPath = CALLBACK_PATH;
/** Builds the {@link LocalServerReceiver}. */
public LocalServerReceiver build() {
return new LocalServerReceiver(host, port, callbackPath,
successLandingPageUrl, failureLandingPageUrl);
}
/** Returns the host name to use. */
public String getHost() {
return host;
}
/** Sets the host name to use. */
public Builder setHost(String host) {
this.host = host;
return this;
}
/** Returns the port to use or {@code -1} to select an unused port. */
public int getPort() {
return port;
}
/** Sets the port to use or {@code -1} to select an unused port. */
public Builder setPort(int port) {
this.port = port;
return this;
}
/** Returns the callback path of redirect_uri */
public String getCallbackPath() {
return callbackPath;
}
/** Set the callback path of redirect_uri */
public Builder setCallbackPath(String callbackPath) {
this.callbackPath = callbackPath;
return this;
}
public Builder setLandingPages(String successLandingPageUrl, String failureLandingPageUrl) {
this.successLandingPageUrl = successLandingPageUrl;
this.failureLandingPageUrl = failureLandingPageUrl;
return this;
}
}
/**
* Jetty handler that takes the verifier token passed over from the OAuth provider and stashes it
* where {@link #waitForCode} will find it.
*/
class CallbackHandler extends AbstractHandler {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
if (!CALLBACK_PATH.equals(target)) {
return;
}
try {
((Request) request).setHandled(true);
error = request.getParameter("error");
code = request.getParameter("code");
if (error == null && successLandingPageUrl != null) {
response.sendRedirect(successLandingPageUrl);
} else if (error != null && failureLandingPageUrl != null) {
response.sendRedirect(failureLandingPageUrl);
} else {
writeLandingHtml(response);
}
response.flushBuffer();
}
finally {
waitUnlessSignaled.release();
}
}
private void writeLandingHtml(HttpServletResponse response) throws IOException {
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("text/html");
PrintWriter doc = response.getWriter();
doc.println("<html>");
doc.println("<head><title>OAuth 2.0 Authentication Token Received</title></head>");
doc.println("<body>");
doc.println("Received verification code. You may now close this window.");
doc.println("</body>");
doc.println("</html>");
doc.flush();
}
}
}

View File

@@ -1,5 +1,6 @@
package oauth;
// todo add docs
public class OAuth2ClientCredentials {
/** Value of the "API Key". */

View File

@@ -1,40 +0,0 @@
package support;
import java.sql.*;
/**
* Handle the connection to the SQLite database that stores our tasks.
* @author <a href="mailto:luigi.derussis@uniupo.it">Luigi De Russis</a>
* @version 1.1 (06/05/2018)
*/
public class DBConnect {
// todo add a db where we put daily (or hourly, but only for heart) updates
public static final String DB_LOCATION = "jdbc:sqlite:src/main/resources/";
public static final String DB_NAME = "user_data.db";
private static DBConnect instance;
private final Connection conn;
private DBConnect() throws SQLException {
conn = DriverManager.getConnection(DB_LOCATION + DB_NAME);
buildTablesIfNotExisting();
}
public static DBConnect getInstance() {
try {
instance = instance==null? new DBConnect():instance;
} catch (SQLException e) {
e.printStackTrace();
}
return instance;
}
private void buildTablesIfNotExisting() throws SQLException {
Statement statement = conn.createStatement();
// todo working, but not quite well
statement.execute("CREATE TABLE IF NOT EXISTS user (user VARCHAR(16) PRIMARY KEY, name VARCHAR(16), birthday DATE);");
statement.execute("CREATE TABLE IF NOT EXISTS heart_rate (date DATE, rate DOUBLE, user VARCHAR(16), PRIMARY KEY(date, user));");
}
}

View File

@@ -0,0 +1,150 @@
package support;
import device.Fitbit;
import device.fitbitdata.Sleep;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
// todo add docs
/**
* Handle the connection to the SQLite database that stores our tasks.
* @author <a href="mailto:luigi.derussis@uniupo.it">Luigi De Russis</a>
* @version 1.1 (06/05/2018)
*/
public class Database {
public static final String DB_LOCATION = "jdbc:sqlite:src/main/resources/";
public static final String DB_NAME = "user_data.db";
private static final Calendar CALENDAR = Calendar.getInstance();
private static final Logger LOG = LoggerFactory.getLogger(Database.class);
private static Database instance;
private final Connection conn;
/**
* Crea una connessione al Database specificato in DB_LOCATION e con il nome DB_NAME.
* Se il Database non esiste lo crea e inizializza anche delle tabelle:
* <ul>
* <li>'total' contiene i dati utente giorno per giorno.</li>
* <li>'heart' battito + orario</li>
* <li>'sleep' inizio + fine + durata</li>
* </ul>
* @throws SQLException se qualcosa e' andato storto
*/
private Database() throws SQLException {
CALENDAR.setTimeInMillis(System.currentTimeMillis());
conn = DriverManager.getConnection(DB_LOCATION + DB_NAME);
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 heart (day_hour DATE PRIMARY KEY, heart_rate DOUBLE)");
statement.execute("CREATE TABLE IF NOT EXISTS sleep (sleep_start DATE PRIMARY KEY, sleep_end DATE, duration INTEGER)");
}
/**
* Crea una connessione al Database specificato in DB_LOCATION e con il nome DB_NAME.
* Se il Database non esiste lo crea e inizializza anche delle tabelle:
* <ul>
* <li>'total' contiene i dati utente giorno per giorno.</li>
* <li>'heart' battito + orario</li>
* <li>'sleep' inizio + fine + durata</li>
* </ul>
*/
public static Database getInstance() {
try {
instance = instance==null? new Database():instance;
} catch (SQLException e) {
e.printStackTrace();
}
return instance;
}
/**
* Prendi il Runnable che automaticamente gestisce l'inserimento dei dati orari (per ora solo il battito cardiaco)
* @param fitbit la classe che contiene i dati aggiornati
* @return un Runnable
*/
public Runnable insertHourlyData(Fitbit fitbit) {
Runnable runnable = () -> {
boolean notInterrupted = true;
boolean retry = false;
double heartRate = 0;
Date now = null;
while(notInterrupted) {
try {
wait((retry? 1:59-CALENDAR.get(Calendar.MINUTE)) * 60000);
if (retry == false) {
heartRate = fitbit.getHeartRate(60);
now = CALENDAR.getTime();
}
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
* @param fitbit la classe che contiene i dati aggiornati
* @return un Runnable
*/
public Runnable insertDailyData(Fitbit fitbit) {
Runnable runnable = () -> {
boolean notInterrupted = true;
boolean retry = false;
double heartRate = 0;
long sleepTime = 0;
long steps = 0;
List<Sleep.SleepData> sleepDatas = null;
Date now = null;
while (notInterrupted) {
int hourToWait = 23 - CALENDAR.get(Calendar.HOUR);
try {
wait(hourToWait * 3600000);
if (retry == false) {
heartRate = fitbit.getHeartRate(60 * 24);
sleepTime = fitbit.getHoursSleep();
sleepDatas = fitbit.getDetailedSleep();
steps = fitbit.getSteps();
now = CALENDAR.getTime();
}
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;
}
}

Binary file not shown.

View File

@@ -10,8 +10,8 @@ public class TestDialogFlow {
public void test01() {
DialogFlowWebHook webHook = new DialogFlowWebHook();
webHook.addOnAction("LightsON", () -> {return "Luci accese";});
webHook.addOnAction("LightsOFF", () -> {return "Luci spente";});
webHook.addOnAction("LightsON", (param) -> {return "Luci accese";});
webHook.addOnAction("LightsOFF", (param) -> {return "Luci spente";});
webHook.startServer();
}

View File

@@ -1,5 +1,6 @@
package test;
import ai.api.GsonFactory;
import device.Hue;
import org.junit.Test;
@@ -8,38 +9,49 @@ import java.util.Set;
public class TestLights {
public static final int TIMEOUT = 200;
@Test
synchronized public void firstTestLights() throws InterruptedException {
Hue lights = new Hue();
Hue lights = new Hue("localhost:8090", "newdeveloper");
Set<String> toRemove = new HashSet<>();
for(String str: lights.getNameLights())
if(!str.equals("4"))
if(!(Integer.parseInt(str)%2 == 0))
toRemove.add(str);
lights.removeLights(toRemove);
for(int i=0; i<10; i++) {
lights.turnOn();
this.wait(0b11001000); // 200
this.wait(TIMEOUT);
lights.turnOff();
this.wait(0b11001000); // 200
this.wait(TIMEOUT);
}
lights.turnOn();
for(int i=256; i>=0; i--) {
for(int i=Hue.MAX_BRIGHTNESS; i>0; i-=10) {
lights.setBrightness(i);
this.wait(25);
this.wait(TIMEOUT);
}
for(int i=0; i<256; i++) {
for(int i=0; i<Hue.MAX_BRIGHTNESS; i+=10) {
lights.setBrightness(i);
this.wait(25);
this.wait(TIMEOUT);
}
lights.setBrightness(Hue.MAX_BRIGHTNESS);
lights.colorLoop(); // todo not working in simulator
this.wait(TIMEOUT*10);
// 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}));
this.wait(TIMEOUT);
}
lights.setBrightness(150);
lights.colorLoop();
this.wait(20000); // 10 sec
lights.turnOff();
}
}