Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ce6c18476 | ||
| d7cef1f623 | |||
| abd68e0eb1 | |||
|
|
70dadc2111 | ||
|
|
75ebc025a3 | ||
|
|
898e25fcf3 | ||
| eea9dd50e0 | |||
| 2ed9eef8d1 | |||
| e5237b5da4 | |||
| afe644c9e7 | |||
|
|
dd708201c1 | ||
| e1efbafe45 | |||
|
|
40fb445262 | ||
| a4c6c095c6 | |||
|
|
662fb40df3 | ||
|
|
e7dd9162c2 | ||
| 4ab578226c | |||
|
|
db1c6e0801 | ||
| f7942abae2 | |||
| d891e53c8e | |||
|
|
f2e8991049 | ||
| 1f4a4b80c0 | |||
| 7b21345f89 | |||
|
|
2c6789c33a | ||
| 8feb04990f | |||
| 09d8b7781f | |||
|
|
3798d18844 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -29,6 +29,7 @@ hs_err_pid*
|
||||
.idea/*.xml
|
||||
.gradle/
|
||||
.iml
|
||||
.db
|
||||
|
||||
# eclipse things #
|
||||
.classpath
|
||||
|
||||
201
LICENSE.md
Normal file
201
LICENSE.md
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
3
META-INF/MANIFEST.MF
Normal file
3
META-INF/MANIFEST.MF
Normal file
@@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: main.SeniorAssistant
|
||||
|
||||
32
README.md
32
README.md
@@ -1,16 +1,16 @@
|
||||
# SeniorAssistant
|
||||
|
||||
## Membri del gruppo
|
||||
|
||||
- Giacomo Bertolazzi, 20015159@studenti.uniupo.it, [Berack96][Jack]
|
||||
- Giulia Vago, 20015091@studenti.uniupo.it, [Giuliavago][Giulia]
|
||||
- Dawit Gulino, 20013954@studenti.uniupo.it, [DawitG96][D]
|
||||
- Stefano Fioretti, 20013886@studenti.uniupo.it, [f19stefano96][Ste]
|
||||
|
||||
## Descrizione
|
||||
Il nostro progetto nasce per aiutare nella quotidianità persone anziane che non possono essere seguite da assistenti e che quindi possono trarre vantaggio da un sistema automatizzato che interagisca attraverso un assistente vocale in modo da permettere al soggetto di controllare i dispositivi collegati alla Home Station. In caso di stress (rilevato attraverso battito cardiaco), le luci vengono soffuse così da creare un ambiente più rilassante e nel caso in cui la situazione non migliori contatti medico o familiari; inoltre viene consigliata in questo caso musica rilassante che il soggetto può avviare con comando vocale. Inoltre il soggetto viene sollecitato vocalmente in caso di sedentarietà. Si mantiene anche il contatto con persone esterne, medico e familiari, i quali possono controllare le attività del soggetto poiché verranno mantenute statistiche su di esse.
|
||||
|
||||
[Jack]:<https://github.com/Berack96>
|
||||
[Giulia]:<https://github.com/Giuliavago>
|
||||
[D]:<https://github.com/DawitG96>
|
||||
[Ste]:<https://github.com/f19stefano96>
|
||||
# SeniorAssistant
|
||||
|
||||
## Membri del gruppo
|
||||
|
||||
- Giacomo Bertolazzi, 20015159@studenti.uniupo.it, [Berack96][Jack]
|
||||
- Giulia Vago, 20015091@studenti.uniupo.it, [Giuliavago][Giulia]
|
||||
- Dawit Gulino, 20013954@studenti.uniupo.it, [DawitG96][D]
|
||||
- Stefano Fioretti, 20013886@studenti.uniupo.it, [f19stefano96][Ste]
|
||||
|
||||
## Descrizione
|
||||
Il nostro progetto nasce per aiutare nella quotidianità persone anziane che non possono essere seguite da assistenti e che quindi possono trarre vantaggio da un sistema automatizzato che interagisca attraverso un assistente vocale in modo da permettere al soggetto di controllare i dispositivi collegati alla Home Station. In caso di stress (rilevato attraverso battito cardiaco), le luci vengono soffuse così da creare un ambiente più rilassante e nel caso in cui la situazione non migliori contatti medico o familiari; inoltre viene consigliata in questo caso musica rilassante che il soggetto può avviare con comando vocale. Inoltre il soggetto viene sollecitato vocalmente in caso di sedentarietà. Si mantiene anche il contatto con persone esterne, medico e familiari, i quali possono controllare le attività del soggetto poiché verranno mantenute statistiche su di esse.
|
||||
|
||||
[Jack]: <https://github.com/Berack96>
|
||||
[Giulia]: <https://github.com/Giuliavago>
|
||||
[D]: <https://github.com/DawitG96>
|
||||
[Ste]: <https://github.com/f19stefano96>
|
||||
@@ -11,7 +11,13 @@
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.idea" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.settings" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/META-INF" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/lib" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/out" />
|
||||
</content>
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
|
||||
97
build.gradle
97
build.gradle
@@ -1,39 +1,58 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
group 'SeniorAssistant'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
// compile "com.sparkjava:spark-core:2.5.5"
|
||||
compile "com.google.code.gson:gson:2.8.0"
|
||||
// compile "org.xerial:sqlite-jdbc:3.15.1"
|
||||
compile 'org.apache.httpcomponents:httpclient:4.5.3'
|
||||
compile 'com.google.api-client:google-api-client:1.23.0'
|
||||
compile group: 'com.google.oauth-client', name: 'google-oauth-client-jetty', version: '1.11.0-beta'
|
||||
|
||||
|
||||
// z-way-lib and all its dependencies (from https://github.com/pathec/ZWay-library-for-Java)
|
||||
compile files('lib/zway-lib-0.2.9-SNAPSHOT.jar')
|
||||
|
||||
compile 'org.apache.commons:commons-lang3:3.4'
|
||||
compile 'org.eclipse.jetty:jetty-client:9.3.11.v20160721'
|
||||
compile 'org.eclipse.jetty:jetty-http:9.3.11.v20160721'
|
||||
compile 'org.eclipse.jetty:jetty-io:9.3.11.v20160721'
|
||||
compile 'org.eclipse.jetty:jetty-util:9.3.11.v20160721'
|
||||
compile 'org.eclipse.jetty.websocket:websocket-api:9.3.12.v20160915'
|
||||
compile 'org.eclipse.jetty.websocket:websocket-client:9.3.12.v20160915'
|
||||
compile 'org.eclipse.jetty.websocket:websocket-common:9.3.12.v20160915'
|
||||
compile 'org.slf4j:slf4j-simple:1.7.21'
|
||||
|
||||
//for objectMapper
|
||||
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.5'
|
||||
}
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
group 'SeniorAssistant'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url("https://plugins.gradle.org/m2/")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Tests
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
|
||||
// Database
|
||||
compile "org.xerial:sqlite-jdbc:3.21.0.1"
|
||||
|
||||
// Rest request
|
||||
compile 'org.apache.httpcomponents:httpclient:4.5.6'
|
||||
|
||||
// Z-way
|
||||
compile files('lib/zway-lib-0.2.9-SNAPSHOT.jar')
|
||||
compile 'org.apache.commons:commons-lang3:3.8'
|
||||
|
||||
// Logger
|
||||
compile 'org.slf4j:slf4j-simple:1.7.25'
|
||||
|
||||
// Server Spark
|
||||
compile "com.sparkjava:spark-core:2.7.2"
|
||||
|
||||
// Oauth
|
||||
compile ( group: 'com.google.oauth-client', name: 'google-oauth-client-jetty', version: '1.23.0') {
|
||||
// don't pull in an old ancient jetty version
|
||||
exclude group: 'org.mortbay.jetty', module: 'jetty'
|
||||
exclude group: 'org.mortbay.jetty', module: 'jetty-util'
|
||||
exclude group: 'org.mortbay.jetty', module: 'servlet-api'
|
||||
}
|
||||
compile 'com.google.api-client:google-api-client:1.23.0'
|
||||
|
||||
// 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
|
||||
|
||||
// YOUTUBE VIDEO
|
||||
|
||||
//compile files('lib/SWT_linux32.jar')
|
||||
//compile files('lib/SWT_linux64.jar')
|
||||
compile files('lib/SWT_win64.jar')
|
||||
compile group: 'com.hynnet', name: 'DJNativeSwing', version: '1.0.0'
|
||||
compile group: 'com.hynnet', name: 'DJNativeSwing-SWT', version: '1.0.0'
|
||||
}
|
||||
|
||||
BIN
lib/SWT_linux32.jar
Normal file
BIN
lib/SWT_linux32.jar
Normal file
Binary file not shown.
BIN
lib/SWT_linux64.jar
Normal file
BIN
lib/SWT_linux64.jar
Normal file
Binary file not shown.
BIN
lib/SWT_win64.jar
Normal file
BIN
lib/SWT_win64.jar
Normal file
Binary file not shown.
@@ -1,13 +0,0 @@
|
||||
import manage.FITBITData.FitBit;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) throws Exception {
|
||||
FitBit fitBit = new FitBit();
|
||||
fitBit.getHoursSleep();
|
||||
|
||||
System.out.println("Today's average heart-rate: "+fitBit.getHeartRate());
|
||||
System.out.println("Today's hours of sleep: "+fitBit.getHoursSleep());
|
||||
System.out.println("Today's steps: "+fitBit.getSteps());
|
||||
System.out.println("Fine.");
|
||||
}
|
||||
}
|
||||
148
src/main/java/device/DialogFlowWebHook.java
Normal file
148
src/main/java/device/DialogFlowWebHook.java
Normal file
@@ -0,0 +1,148 @@
|
||||
package device;
|
||||
|
||||
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;
|
||||
import spark.Spark;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static spark.Spark.get;
|
||||
import static spark.Spark.post;
|
||||
|
||||
/**
|
||||
* Classe per creare un Webhook che Dialog-Flow possa utilizzare per le sue azioni
|
||||
*/
|
||||
public class DialogFlowWebHook {
|
||||
|
||||
/**
|
||||
* Un logger per ottenere messaggi sull'esecuzione del programma
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger("WebHook");
|
||||
|
||||
/**
|
||||
* Stringa che viene usata se l'azione esiste ma lancia un qualche tipo di errore
|
||||
*/
|
||||
public static final String ACTION_ERROR = "L'azione non è eseguibile";
|
||||
|
||||
/**
|
||||
* Errore che viene mostrato all'utente se l'azione inviata non corrisponde a nessuna di quelle inserite
|
||||
*/
|
||||
public static final String ERROR = "Non posso eseguire nessuna azione di questo tipo";
|
||||
|
||||
/**
|
||||
* L'eventuale path successiva all'url dichiarato nel Webhook di Dialog-Flow
|
||||
*/
|
||||
public final String path;
|
||||
|
||||
/**
|
||||
* La porta in cui il server ascoltera'
|
||||
*/
|
||||
public final int port;
|
||||
|
||||
/**
|
||||
* Mappa che contiene tutte le azioni e il loro ID
|
||||
*/
|
||||
private final Map<String, Action> actions;
|
||||
|
||||
/**
|
||||
* Crea una classe vuota per un server che risponde alle chiamate di Dialog-Flow.
|
||||
* La path viene impostata di default a "/" e la porta a 4567
|
||||
*/
|
||||
public DialogFlowWebHook() { this("/", 4567); }
|
||||
|
||||
/**
|
||||
* Crea una classe vuota per un server che risponde alle chiamate di Dialog-Flow.
|
||||
* @param path il percorso dopo l'url inidicato nel WebHook di Dialog-Flow
|
||||
* @param port la porta da cui il Webhoook ascolta (se inserito numero negativo ascolta di default sulla porta 4567)
|
||||
*/
|
||||
public DialogFlowWebHook(String path, int port) {
|
||||
this.path = path;
|
||||
this.port = port>0? port:4567;
|
||||
this.actions = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggiunge un'azione ad una specifica richiesta di Dialog-Flow
|
||||
* @param actionId il nome dell'azione che viene passata da Dialog-Flow
|
||||
* @param action l'azione da fare
|
||||
*/
|
||||
public void addOnAction(String actionId, Action action) { this.actions.put(actionId, action); }
|
||||
|
||||
/**
|
||||
* Fa partire il server per accettare richieste da Dialog-Flow ascoltando connessioni in post.<br>
|
||||
* Ogni richiesta viene esaminata e fatta coincidere con un'azione specificata precedentemente.<br>
|
||||
* Se nessuna azione viene riscontrata, viene inviato un errore, rimuovendo i messaggi<br>
|
||||
* Inoltre aggiunge un'interfaccia in get che riguarda un iframe di dialogflow
|
||||
*/
|
||||
public void startServer() {
|
||||
Spark.port(this.port);
|
||||
Gson gson = GsonFactory.getDefaultFactory().getGson();
|
||||
post(this.path, (request, response) -> {
|
||||
Fulfillment output = new Fulfillment();
|
||||
AIResponse input = gson.fromJson(request.body(), AIResponse.class);
|
||||
|
||||
String inputAction = input.getResult().getAction();
|
||||
Map<String, JsonElement> inputParam = input.getResult().getParameters();
|
||||
String text;
|
||||
try {
|
||||
LOG.info("AZIONE: "+ inputAction + ", PARAMS: " + inputParam);
|
||||
Action action = actions.get(inputAction);
|
||||
try {
|
||||
text = action.doAction(inputParam);
|
||||
} catch (NullPointerException e) {
|
||||
LOG.warn("AZIONE FALLITA: "+ inputAction);
|
||||
text = ACTION_ERROR;
|
||||
}
|
||||
} 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);
|
||||
|
||||
response.type("application/json");
|
||||
return output;
|
||||
}, gson::toJson);
|
||||
|
||||
get(this.path, (request, response) -> {
|
||||
return "<iframe\n" +
|
||||
" allow=\"microphone;\"\n" +
|
||||
" width=\"350\"\n" +
|
||||
" height=\"430\"\n" +
|
||||
" src=\"https://console.dialogflow.com/api-client/demo/embedded/SeniorAssistant\">\n" +
|
||||
"</iframe>";
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Interfaccia usata per fare un'azione per ogni Id di Dialog-Flow
|
||||
*/
|
||||
public interface Action {
|
||||
/**
|
||||
* Fa l'azione desiderata.
|
||||
* Se ritorna una stringa allora il testo viene cambiato. Se ritorna null non cambia il testo
|
||||
*
|
||||
* @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<String, JsonElement> params) throws Exception;
|
||||
}
|
||||
}
|
||||
215
src/main/java/device/Fitbit.java
Normal file
215
src/main/java/device/Fitbit.java
Normal file
@@ -0,0 +1,215 @@
|
||||
package device;
|
||||
|
||||
import device.fitbitdata.HeartRate;
|
||||
import device.fitbitdata.Sleep;
|
||||
import device.fitbitdata.Steps;
|
||||
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
|
||||
*/
|
||||
public class Fitbit {
|
||||
|
||||
/**
|
||||
* Logger per vedere cosa invia e riceve questa classe
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger("Fitbit");
|
||||
|
||||
/**
|
||||
* Url da dove si possono prendere i dati dai vari dispositivi fitbit
|
||||
*/
|
||||
public static final String BASIC_URL = "https://api.fitbit.com/";
|
||||
|
||||
/**
|
||||
* Utente del fitbit<br>
|
||||
* In questo caso e' universale e prende l'utente che e' attualmente loggato
|
||||
*/
|
||||
public static final String USER = "/user/-/";
|
||||
|
||||
/**
|
||||
* Un minuto in millisecondi
|
||||
*/
|
||||
private static final long MINUTE = 60000;
|
||||
|
||||
/**
|
||||
* L'oauth per l'account fitbit
|
||||
*/
|
||||
private final AuthFitbit auth;
|
||||
|
||||
/**
|
||||
* Una mappa contenente le ultime classi usate nelle richieste effettuate<br>
|
||||
* Una sorta di cache
|
||||
*/
|
||||
private final Map<Class<?>, Long> latestRequest = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Un calendario in modo da sapere la data per prendere i dati
|
||||
*/
|
||||
private final Calendar calendar = Calendar.getInstance();
|
||||
|
||||
/**
|
||||
* La classe per sapere i dati sul battito cardiaco
|
||||
*/
|
||||
private HeartRate heart = null;
|
||||
|
||||
/**
|
||||
* La classe per sapere i dati sul sonno
|
||||
*/
|
||||
private Sleep sleep = null;
|
||||
|
||||
/**
|
||||
* La classe per sapere i dati sui passi effettuati
|
||||
*/
|
||||
private Steps steps = null;
|
||||
|
||||
/**
|
||||
* Crea un'istanza di fitbit<br>
|
||||
* Se l'utente non ha ancora accettato i dati richiesti o l'utente non e'
|
||||
* loggato, verra' aperto il browser in modo che si possano inserire i dati
|
||||
*
|
||||
* @throws Exception Nel caso qualunque cosa andasse storta (vedi messaggio)
|
||||
*/
|
||||
public Fitbit() throws Exception { this.auth = new AuthFitbit(); }
|
||||
|
||||
/**
|
||||
* Ricevi i passi che l'utente ha effettuato nell'ultimo giorno
|
||||
*
|
||||
* @return un intero rappresentante i passi effettuati
|
||||
*/
|
||||
public synchronized int getSteps() {
|
||||
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<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>
|
||||
* 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
|
||||
*/
|
||||
public synchronized double getHeartRate() { 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
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public synchronized double getHeartRate(int lastMinutes) {
|
||||
if(lastMinutes<=0)
|
||||
return -1;
|
||||
|
||||
long currentMillisec = System.currentTimeMillis();
|
||||
|
||||
String now = getHourMinutes(currentMillisec);
|
||||
String ago = getHourMinutes(currentMillisec - (MINUTE * lastMinutes));
|
||||
|
||||
if (now.compareTo(ago) < 0)
|
||||
ago = "00:00";
|
||||
|
||||
heart = update(HeartRate.class, null,"1" + USER + "activities/heart/date/today/1d/1sec/time/" + ago + "/" + now + ".json");
|
||||
return heart.getAverage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ricevi le ore di sonno che l'utente ha fatto nell'ultimo giorno
|
||||
*
|
||||
* @return un intero rappresentante le ore passate a dormire
|
||||
*/
|
||||
public synchronized double getHoursSleep() {
|
||||
sleep = update(Sleep.class, sleep,"1.2" + USER + "sleep/date/today.json");
|
||||
return (double)sleep.getMinutesAsleep()/60;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return una lista contenente ogni volta che l'utente ha dormito
|
||||
*/
|
||||
public synchronized List<Sleep.SleepData> getDetailedSleep() {
|
||||
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<br>
|
||||
* Nel caso di fallimento della richiesta varra' restituito la variabile passata in input
|
||||
*
|
||||
* @param varClass la classe della variabile passata
|
||||
* @param variable la variabile che deve fare l'update (passando null si forza la richiesta)
|
||||
* @param url l'url da cui prende i dati aggiornati
|
||||
* @return la variabile aggiornata
|
||||
*/
|
||||
private synchronized <T> T update(Class<T> varClass, T variable, String url) {
|
||||
try {
|
||||
long current = System.currentTimeMillis();
|
||||
long latest = latestRequest.get(varClass);
|
||||
|
||||
// don't update
|
||||
if( (variable!=null) && (current - latest < MINUTE * 5) )
|
||||
return variable;
|
||||
} catch (NullPointerException e) {
|
||||
// do nothing and update
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Funzione che transforma i millisecondi nel formato "hh:mm"
|
||||
*
|
||||
* @param milliseconds millisecondi da trasformare
|
||||
* @return una stringa nel formato "hh:mm"
|
||||
*/
|
||||
private String getHourMinutes(long milliseconds) {
|
||||
calendar.setTimeInMillis(milliseconds);
|
||||
|
||||
int hour = calendar.get(Calendar.HOUR_OF_DAY);
|
||||
int minu = calendar.get(Calendar.MINUTE);
|
||||
return String.format("%02d:%02d", hour, minu);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,76 +1,223 @@
|
||||
package device;
|
||||
|
||||
import ai.api.GsonFactory;
|
||||
import com.google.common.util.concurrent.AtomicDouble;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import support.Rest;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import manage.Rest;
|
||||
|
||||
/**
|
||||
* Classe che permette di controllare le luci Philips Hue
|
||||
*/
|
||||
public class Hue {
|
||||
//private String baseURL = "192.168.0.2";
|
||||
//private String username = "C0vPwqjJZo5Jt9Oe5HgO6sBFFMxgoR532IxFoGmx";
|
||||
private String lightsURL;// = baseURL+"/api/"+username+"/lights/";
|
||||
|
||||
private Map<String, ?> allLights;
|
||||
/**
|
||||
* Log che serve per debug
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger("Hue");
|
||||
|
||||
public Hue () {
|
||||
this("http://172.30.1.138/api/C0vPwqjJZo5Jt9Oe5HgO6sBFFMxgoR532IxFoGmx/lights/");
|
||||
}
|
||||
public Hue (String url) {
|
||||
lightsURL = url;
|
||||
allLights = Rest.get(lightsURL);
|
||||
/**
|
||||
* La luminosita' massima a cui si puo' arrivare
|
||||
*/
|
||||
private static final int MAX_BRIGHTNESS = 254;
|
||||
|
||||
/**
|
||||
* Una mappa che ad ogni colore (in italiano) assegna il proprio valore in hue
|
||||
*/
|
||||
private static final Map<String, Double[]> COLORS = new HashMap<>();
|
||||
|
||||
/**
|
||||
* L'url in cui si possono trovare le luci
|
||||
*/
|
||||
private final String lightsURL;
|
||||
|
||||
/**
|
||||
* Tutte le luci che sono state registrate dall'url
|
||||
*/
|
||||
private final Map<String, Map<String, Object>> allLights;
|
||||
|
||||
/**
|
||||
* L'ultima luminosita' impostata
|
||||
*/
|
||||
private final AtomicDouble brightness = new AtomicDouble(0);
|
||||
|
||||
// Riempimento della mappa con i colori
|
||||
static {
|
||||
COLORS.put("giall[oae]", new Double[]{0.45, 0.45});
|
||||
COLORS.put("ross[oae]", new Double[]{0.7, 0.25});
|
||||
COLORS.put("verd[ei]", new Double[]{0.1, 0.55});
|
||||
COLORS.put("blu", new Double[]{0.12, 0.125});
|
||||
COLORS.put("rosa", new Double[]{0.45, 0.275});
|
||||
COLORS.put("viola", new Double[]{0.25, 0.1});
|
||||
COLORS.put("azzurr[oae]", new Double[]{0.15, 0.25});
|
||||
COLORS.put("arancio(ne|ni)?", new Double[]{0.55, 0.4});
|
||||
//COLORS.put("nero", new Double[]{1.0, 1.0});
|
||||
COLORS.put("bianc(o|a|he)", new Double[]{0.275, 0.3});
|
||||
}
|
||||
|
||||
public Set<String> getNameLights() {
|
||||
return allLights.keySet();
|
||||
/**
|
||||
* Cerca le luci Philips Hue all'indirizzo <a href="http://172.30.1.138/api/C0vPwqjJZo5Jt9Oe5HgO6sBFFMxgoR532IxFoGmx/lights/">http://172.30.1.138/api/C0vPwqjJZo5Jt9Oe5HgO6sBFFMxgoR532IxFoGmx/lights/</a>
|
||||
* @throws NullPointerException se non trova nessun bridge
|
||||
*/
|
||||
public Hue () throws NullPointerException { this("172.30.1.138", "C0vPwqjJZo5Jt9Oe5HgO6sBFFMxgoR532IxFoGmx"); }
|
||||
|
||||
/**
|
||||
* Cerca le luci Philips Hue nell'indirizzo specificato e con l'utente specificato.<br>
|
||||
* Una volta trovate le luci le setta tutte alla stessa luminosita' e allo stesso colore<br>
|
||||
* (luminosità massima e colore bianco)
|
||||
* @param ip l'indirizzo IP (seguito dalla porta se e' diversa dalla solita 8000)
|
||||
* @param user l'utente
|
||||
* @throws NullPointerException se non trova nessun bridge
|
||||
*/
|
||||
public Hue(String ip, String user) throws NullPointerException {
|
||||
lightsURL = "http://" + ip + "/api/" + user + "/lights/";
|
||||
allLights = (Map<String, Map<String, Object>>)Rest.get(lightsURL);
|
||||
|
||||
if(allLights.isEmpty())
|
||||
throw new NullPointerException("Non e' stato possibile connettersi alle luci");
|
||||
|
||||
setBrightness(100);
|
||||
changeColor("bianco");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ritorna un insieme contenente tutti i nomi delle luci che si sono trovate
|
||||
*
|
||||
* @return l'insieme dei nomi delle luci
|
||||
*/
|
||||
public Set<String> getNameLights() { return allLights.keySet(); }
|
||||
|
||||
/**
|
||||
* Rimuove dal controllo tutte le luci che hanno il nome uguale ad uno contenuto nell'insieme passato
|
||||
*
|
||||
* @param toRemove le luci da rimuovere
|
||||
*/
|
||||
public void removeLights(Set<String> toRemove) {
|
||||
for(String string : toRemove)
|
||||
allLights.remove(string);
|
||||
}
|
||||
|
||||
public void turnOn() {
|
||||
for (String light : allLights.keySet()) {
|
||||
String callURL = lightsURL + light + "/state";
|
||||
String body = "{ \"on\" : true }";
|
||||
Rest.put(callURL, body, "application/json");
|
||||
}
|
||||
/**
|
||||
* Accende o spegne le luci controllate
|
||||
* @param on vero se si vuole le luci accese, false per spegnerle
|
||||
*/
|
||||
public void on(boolean on) { setState("on", on, false); }
|
||||
|
||||
/**
|
||||
* Accende tutte le luci controllate
|
||||
*/
|
||||
public void turnOn() { on(true); }
|
||||
|
||||
/**
|
||||
* Spegne tutte le luci controllate
|
||||
*/
|
||||
public void turnOff() { on(false); }
|
||||
|
||||
/**
|
||||
* Ritorna la luminosita' attuale delle luci controllate
|
||||
* @return il valore e' compreso tra 0 e 100
|
||||
*/
|
||||
public double getCurrentBrightness() { return brightness.doubleValue(); }
|
||||
|
||||
/**
|
||||
* Modifica la luminosita' delle luci a seconda del valore inserito
|
||||
* @param num la luminosita' che si vuole da (0 a 100)
|
||||
*/
|
||||
public void setBrightness(double num) {
|
||||
if (num<0)
|
||||
num=0;
|
||||
else if (num>100)
|
||||
num=100;
|
||||
|
||||
setState("bri", (int) (num*MAX_BRIGHTNESS)/100, true);
|
||||
brightness.set(num);
|
||||
}
|
||||
|
||||
public int getCurrentLuminiscence() {
|
||||
return 0;
|
||||
}
|
||||
public void turnOff() {
|
||||
for (String light : allLights.keySet()) {
|
||||
String callURL = lightsURL + light + "/state";
|
||||
String body = "{ \"on\" : false }";
|
||||
Rest.put(callURL, body, "application/json");
|
||||
}
|
||||
/**
|
||||
* Aggiunge il valore delta alla luminosita' corrente delle luci.
|
||||
* @param delta un qualsiasi numero che va da -100 a 100
|
||||
*/
|
||||
public void addBrightness(double delta) {
|
||||
setBrightness(brightness.doubleValue() + delta);
|
||||
}
|
||||
|
||||
public void setBrightness(int num) {
|
||||
for (String light : allLights.keySet()) {
|
||||
String callURL = lightsURL + light + "/state";
|
||||
String body = "{ \"bri\" : "+num+" }";
|
||||
Rest.put(callURL, body, "application/json");
|
||||
}
|
||||
/**
|
||||
* Aumenta la luminosita' delle luci controllate della percentuale che viene passata
|
||||
* @param percentage la percentuale di aumento della luminosita'
|
||||
*/
|
||||
public void increaseBrightness(double percentage) {
|
||||
if (percentage<0)
|
||||
percentage = 0;
|
||||
else if (percentage>100)
|
||||
percentage = 100;
|
||||
setBrightness(brightness.doubleValue() + percentage);
|
||||
}
|
||||
|
||||
/*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");
|
||||
}
|
||||
}*/
|
||||
/**
|
||||
* Aumenta la luminosita' delle luci controllate del 25%
|
||||
*/
|
||||
public void increaseBrightness() { increaseBrightness(25); }
|
||||
|
||||
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");
|
||||
}
|
||||
/**
|
||||
* Diminuisce la luminosita' delle luci controllate della percentuale che viene passata
|
||||
* @param percentage la percentuale di diminuzione della luminosita'
|
||||
*/
|
||||
public void decreaseBrightness(int percentage) {
|
||||
if (percentage<0)
|
||||
percentage = 0;
|
||||
else if (percentage>100)
|
||||
percentage = 100;
|
||||
setBrightness(brightness.doubleValue() - percentage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Diminuisce la luminosita' delle luci controllate del 25%
|
||||
*/
|
||||
public void decreaseBrightness() { decreaseBrightness(25); }
|
||||
|
||||
public void changeColor(String colorName) {
|
||||
for (String regex: COLORS.keySet())
|
||||
if(colorName.matches("(?i)" + 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", false); }
|
||||
|
||||
/**
|
||||
* Invia una richiesta a tutte le luci hue con l'attributo selezionato ed il suo valore<br>
|
||||
* Con esso invia anche un valore di transizione in modo che sia piu fluido il cambiamento se si mette true al terzo parametro
|
||||
* @param attribute l'attributo da modificare
|
||||
* @param value il valore da inserire
|
||||
* @param transition se includere la transizione o meno
|
||||
*/
|
||||
private void setState(String attribute, Object value, boolean transition){
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put(attribute, value);
|
||||
if(transition)
|
||||
map.put("transition", 200);
|
||||
setState(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invia una richiesta a tutte le luci hue con gli attributi selezionati ed il loro valore
|
||||
* @param attributes una mappa di attributi -> valori
|
||||
*/
|
||||
private synchronized void setState(Map<String, Object> attributes) {
|
||||
String body = GsonFactory.getDefaultFactory().getGson().toJson(attributes);
|
||||
LOG.info("Setting: " + body);
|
||||
for (String light : allLights.keySet()) {
|
||||
Rest.put(lightsURL + light + "/state", body,"application/json");
|
||||
|
||||
Map<String, Object> state = (Map<String, Object>)allLights.get(light).get("state");
|
||||
for (String attr : attributes.keySet())
|
||||
state.put(attr, attributes.get(attr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,44 +4,73 @@ import de.fh_zwickau.informatik.sensor.IZWayApi;
|
||||
import de.fh_zwickau.informatik.sensor.ZWayApiHttp;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.Device;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.DeviceList;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import support.ZWaySimpleCallback;
|
||||
|
||||
/**
|
||||
* Sensore che permette di registrare vari dati dell'ambiente (noi utilizziamo solo la luminosità)
|
||||
*/
|
||||
public class Sensor {
|
||||
// init logger
|
||||
Logger logger = LoggerFactory.getLogger(Sensor.class);
|
||||
|
||||
// sample RaZberry IP address
|
||||
public String ipAddress = "172.30.1.137";
|
||||
/**
|
||||
* IP del sensore a cui ci si vuole agganciare
|
||||
*/
|
||||
private static final String IP_ADDRESS = "172.30.1.137";
|
||||
|
||||
// sample username and password
|
||||
public String username = "admin";
|
||||
public String password = "raz4reti2";
|
||||
/**
|
||||
* Porta in cui si ascolta per i sensori
|
||||
*/
|
||||
private static final int PORT = 8083;
|
||||
|
||||
public IZWayApi zwayApi;
|
||||
/**
|
||||
* Username con cui si entra nel dispositivo
|
||||
*/
|
||||
private static final String USERNAME = "admin";
|
||||
/**
|
||||
* Password del dispositivo
|
||||
*/
|
||||
private final String PASSWORD = "raz4reti2";
|
||||
|
||||
/**
|
||||
* Tutti i devices che esistono nella rete
|
||||
*/
|
||||
private DeviceList allZWaveDevices;
|
||||
/**
|
||||
* I device che vengono selezionati e filtrati dall'utente
|
||||
* (ovvero quelli che verranno usati per prendere i dati)
|
||||
*/
|
||||
private DeviceList devices;
|
||||
private Integer nodeId;
|
||||
|
||||
public Sensor() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public Sensor (Integer nodeId) {
|
||||
this.nodeId = nodeId;
|
||||
/**
|
||||
* Crea un sensore contenente tutti i nodi
|
||||
* @throws NullPointerException se non trova nessun sensore
|
||||
*/
|
||||
public Sensor() throws NullPointerException { this(null); }
|
||||
|
||||
/**
|
||||
* Si connette ad un sensore che ha il nodeId selezioniato
|
||||
* @param nodeId nodo che viene selezionato
|
||||
* @throws NullPointerException se non trova nessun sensore
|
||||
*/
|
||||
public Sensor (Integer nodeId) throws NullPointerException {
|
||||
// create an instance of the Z-Way library; all the params are mandatory (we are not going to use the remote service/id)
|
||||
zwayApi = new ZWayApiHttp(ipAddress, 8083, "http", username, password, 0, false, new ZWaySimpleCallback());
|
||||
IZWayApi zwayApi = new ZWayApiHttp(IP_ADDRESS, PORT, "http", USERNAME, PASSWORD, 0, false, new ZWaySimpleCallback());
|
||||
|
||||
// get all the Z-Wave devices
|
||||
allZWaveDevices = zwayApi.getDevices();
|
||||
|
||||
if(allZWaveDevices == null)
|
||||
throw new NullPointerException("I sensori non sono stati trovati");
|
||||
|
||||
if(nodeId != null)
|
||||
useNode(nodeId);
|
||||
else
|
||||
devices = allZWaveDevices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cambia i dispositivi selezionati in base al nodeId che viene scelto
|
||||
* @param nodeId il nodo che viene selezionato
|
||||
*/
|
||||
public void useNode(int nodeId) {
|
||||
devices = new DeviceList();
|
||||
for (Device devi : allZWaveDevices.getAllDevices())
|
||||
@@ -49,25 +78,25 @@ public class Sensor {
|
||||
devices.addDevice(devi);
|
||||
}
|
||||
|
||||
public int luminiscenceLevel() {
|
||||
/**
|
||||
* Legge i valori della luminosita' segnata dai dispositivi e ne ritorna il valore
|
||||
* @return la luminosita' segnata dai dispositivi (da 0 a 100)
|
||||
*/
|
||||
public double getBrightnessLevel() {
|
||||
update();
|
||||
for (Device device : devices.getAllDevices())
|
||||
if (device.getMetrics().getProbeTitle().equalsIgnoreCase("luminiscence"))
|
||||
return Integer.parseInt(device.getMetrics().getLevel());
|
||||
return -99;
|
||||
if (device.getMetrics().getProbeTitle().equalsIgnoreCase("luminiscence"))
|
||||
return Double.parseDouble(device.getMetrics().getLevel())/10;
|
||||
return 0;
|
||||
}
|
||||
|
||||
synchronized public void update(int timeout) throws InterruptedException {
|
||||
//setInitialValues();
|
||||
/**
|
||||
* Fa in modo di forzare l'aggiornamento dei dispositivi
|
||||
*/
|
||||
synchronized private void update() {
|
||||
for (Device device : devices.getAllDevices())
|
||||
device.update();
|
||||
wait(timeout);
|
||||
try {
|
||||
device.update();
|
||||
} catch (Exception e) { }
|
||||
}
|
||||
/*public boolean IsLowLuminescence(int Luminescence) {
|
||||
if (dev.getProbeType().equalsIgnoreCase("Luminescence"))
|
||||
if (Integer.parseInt(dev.getMetrics().getLevel()) < Luminescence)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
return false;
|
||||
}*/
|
||||
}
|
||||
|
||||
20
src/main/java/device/fitbitdata/FitbitData.java
Normal file
20
src/main/java/device/fitbitdata/FitbitData.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package device.fitbitdata;
|
||||
|
||||
/**
|
||||
* Classe che serve ad avere una data collegata al dato preso dal fitbit
|
||||
*/
|
||||
public abstract class FitbitData {
|
||||
private long millisec = 0;
|
||||
|
||||
/**
|
||||
* Setta la data collegata al dato preso dal fitbit
|
||||
* @param millisec la data in millisecondi
|
||||
*/
|
||||
public void setDate(long millisec) { this.millisec = millisec; }
|
||||
|
||||
/**
|
||||
* Ricevi la data collegata al dato richiesto dal fitbit
|
||||
* @return la data in millisecondi
|
||||
*/
|
||||
public long getDate() { return this.millisec; }
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package manage.FITBITData;
|
||||
package device.fitbitdata;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
@@ -6,23 +6,16 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Classe per vedere le informazioni sul battito cardiaco
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class HeartRate {
|
||||
public class HeartRate extends FitbitData {
|
||||
|
||||
private String dateTime;
|
||||
private double average;
|
||||
|
||||
public double getAverage() {
|
||||
return average;
|
||||
}
|
||||
|
||||
@JsonProperty("activities-heart")
|
||||
public void quelloCheVoglio(Map<String, Object>[] activities){
|
||||
dateTime = (String) activities[0].get("dateTime");
|
||||
}
|
||||
|
||||
@JsonProperty("activities-heart-intraday")
|
||||
public void setAverage(Map<String, Object> map) {
|
||||
private void setAverage(Map<String, Object> map) {
|
||||
List<Map> data = (List) map.get("dataset");
|
||||
|
||||
int sum = 0;
|
||||
@@ -33,4 +26,16 @@ public class HeartRate {
|
||||
if(data.size() == 0)
|
||||
average = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setta il vaore medio del battito cardiaco
|
||||
* @param average il valore medio del battito
|
||||
*/
|
||||
public void setAverage(double average) { this.average = average; }
|
||||
|
||||
/**
|
||||
* Ricevi il valore medio del battito cardiaco
|
||||
* @return il valore medio
|
||||
*/
|
||||
public double getAverage() { return average; }
|
||||
}
|
||||
79
src/main/java/device/fitbitdata/Sleep.java
Normal file
79
src/main/java/device/fitbitdata/Sleep.java
Normal file
@@ -0,0 +1,79 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* Classe per recuperare i dati del sonno dell'utente
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Sleep {
|
||||
|
||||
private int minutesAsleep;
|
||||
|
||||
private List<SleepData> datas;
|
||||
|
||||
@JsonProperty("summary")
|
||||
private void setMinutesAsleep(Map<String, Object> map) {
|
||||
minutesAsleep = (int) map.get("totalMinutesAsleep");
|
||||
}
|
||||
|
||||
@JsonProperty("sleep")
|
||||
private 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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* I minuti totali che l'utente ha avuto di sonno durante il giorno
|
||||
* @return i minuti totali
|
||||
*/
|
||||
public long getMinutesAsleep() {
|
||||
return minutesAsleep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ricevi i dati più specifici riguardo al sonno
|
||||
* @return una lista con i dati specifici del sonno
|
||||
*/
|
||||
public List<SleepData> getDatas() {
|
||||
return datas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Classe utilizzata per avere i dati più specifici del sonno
|
||||
*/
|
||||
public class SleepData {
|
||||
/**
|
||||
* La data d'inizio del sonno in millisec
|
||||
*/
|
||||
public final long start_date;
|
||||
/**
|
||||
* La durata del sonno in millisec
|
||||
*/
|
||||
public final long duration;
|
||||
|
||||
SleepData(Date start_date, long duration) {
|
||||
this.start_date = start_date.getTime();
|
||||
this.duration = duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/main/java/device/fitbitdata/Steps.java
Normal file
54
src/main/java/device/fitbitdata/Steps.java
Normal file
@@ -0,0 +1,54 @@
|
||||
package device.fitbitdata;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Classe che serve a vedere i passi fatti secondo il fitbit
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Steps extends FitbitData {
|
||||
|
||||
private int steps;
|
||||
private List<Map<String, Object>> stepsData;
|
||||
|
||||
@JsonProperty("activities-steps")
|
||||
private void setSteps(Map<String, String>[] array) {
|
||||
SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Date now = new Date();
|
||||
String strDate = sdfDate.format(now);
|
||||
|
||||
for(Map<String, String> map : array)
|
||||
if(map.get("dateTime").equals(strDate))
|
||||
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"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assegna il valore dei passi
|
||||
* @param steps i passi
|
||||
*/
|
||||
public void setSteps(int steps) { this.steps = steps; }
|
||||
|
||||
/**
|
||||
* I passi totali fatti durante il giorno
|
||||
* @return i passi totali
|
||||
*/
|
||||
public int getSteps() { return steps; }
|
||||
|
||||
/**
|
||||
* Prendi i dati specifici dei passi
|
||||
* @return una lista contenente una mappa con delle ore e minuti ad un valore dei passi
|
||||
*/
|
||||
public List<Map<String, Object>> getStepsData() { return stepsData; }
|
||||
}
|
||||
124
src/main/java/main/SeniorAssistant.java
Normal file
124
src/main/java/main/SeniorAssistant.java
Normal file
@@ -0,0 +1,124 @@
|
||||
package main;
|
||||
|
||||
import device.Fitbit;
|
||||
import device.Hue;
|
||||
import device.Sensor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import support.database.Database;
|
||||
import support.database.LocalDB;
|
||||
import support.database.RemoteDB;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Ci si puo' interfacciare con l'assistente tramite Telegram o dal sito di ngrok.
|
||||
*/
|
||||
public class SeniorAssistant {
|
||||
|
||||
/**
|
||||
* Un Logger per seguire l'esecuzione del programma.
|
||||
*/
|
||||
public static final Logger LOG = LoggerFactory.getLogger("SeniorAssistant");
|
||||
|
||||
/**
|
||||
* Funzione principale, qui si creano tutte le classi che verranno utilizzate.<br>
|
||||
* Si possono passare dei parametri usando -(nome parametro)::(valore parametro)<br>
|
||||
* Ogni parametro deve esser separato da uno o piu spazi<br>
|
||||
* Parametri possibili:<br>
|
||||
* <ul>
|
||||
* <li>hueAddress</li>
|
||||
* <li>hueUser</li>
|
||||
* <li>autoBrightness</li>
|
||||
* <li>sensorNode</li>
|
||||
* <li>remoteDbUser</li>
|
||||
* </ul>
|
||||
* @param args i possibili argomenti da passare al programma
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
VariousThreads threads = new VariousThreads(); // this should be the first action of the main
|
||||
Map<String, String> arguments = getArgsMap(args);
|
||||
|
||||
// list of arguments to use in the classes
|
||||
String hueAddress = arguments.get("hueaddress");
|
||||
String hueUser = arguments.get("hueuser");
|
||||
Integer sensorNode = getInt(arguments.get("sensornode"));
|
||||
String remoteDbUser = arguments.get("remotedbuser");
|
||||
boolean autoBrightness = arguments.containsKey("autobrightness");
|
||||
|
||||
try {
|
||||
LOG.info("Connessione alle Philips Hue...");
|
||||
Hue lights = (hueAddress!=null && hueUser!=null? new Hue(hueAddress, hueUser):new Hue());
|
||||
|
||||
if(autoBrightness) try {
|
||||
LOG.info("Connessione ai sensori...");
|
||||
Sensor sensor = new Sensor(sensorNode);
|
||||
|
||||
threads.startHueAutoBrightness(lights, sensor);
|
||||
} catch (Exception e) {
|
||||
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 = new Fitbit();
|
||||
|
||||
LOG.info("Connessione al database...");
|
||||
Database database = remoteDbUser == null ? new LocalDB() : new RemoteDB(remoteDbUser);
|
||||
if(remoteDbUser != null && !database.isReachable())
|
||||
database = new LocalDB();
|
||||
|
||||
threads.startInsertData(database, fitbit);
|
||||
threads.startHueControlledByHeartBeat(lights, fitbit, database);
|
||||
threads.startCheckSteps(database);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Non e' stato possibile collegarsi al fitbit");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
threads.startWebhook(lights, fitbit);
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
LOG.info("FINE MAIN");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------
|
||||
Le funzioni qui sotto servono solamente per gli argomenti passati al main
|
||||
------------------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* Prende gli argomenti nel formato "^-(?<name>[a-zA-Z]+)(::)?(?<argument>.*)$" e li inserisce in una mappa.
|
||||
* Se l'argomento non e' nel formato giusto lo ignora.
|
||||
* @param args un array di stringhe contenente i vari argomenti
|
||||
* @return una mappa con key il nome dell'argomento (la parte prima del :: e dopo il meno) e come valore il valore di esso (la parte dopo ::)
|
||||
*/
|
||||
private static Map<String, String> getArgsMap(String[] args) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
Pattern pattern = Pattern.compile("^-(?<name>[a-zA-Z]+)(::)?(?<argument>.*)$");
|
||||
|
||||
for (String arg: args) {
|
||||
Matcher matcher = pattern.matcher(arg);
|
||||
if (matcher.find())
|
||||
map.put(matcher.group("name").toLowerCase(), matcher.group("argument"));
|
||||
}
|
||||
LOG.info(map.toString());
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Funzione creata per gli argomenti che vengono passati in modo da evitare troppi try and catch
|
||||
* @param num la stringa da trasformare in numero
|
||||
* @return il numero trasformato, null se fallisce
|
||||
*/
|
||||
private static Integer getInt(String num) {
|
||||
Integer returnNum = null;
|
||||
try { returnNum = Integer.parseInt(num); } catch (Exception e) {}
|
||||
return returnNum;
|
||||
}
|
||||
}
|
||||
337
src/main/java/main/VariousThreads.java
Normal file
337
src/main/java/main/VariousThreads.java
Normal file
@@ -0,0 +1,337 @@
|
||||
package main;
|
||||
|
||||
import device.DialogFlowWebHook;
|
||||
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.database.Database;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
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 {
|
||||
|
||||
/**
|
||||
* Una costante che indica quanti millisecondi ci sono in un minuto (utile per le conversioni)
|
||||
*/
|
||||
public static final int MILLISEC_IN_MINUTE = 60000;
|
||||
|
||||
/**
|
||||
* Quanti minuti di cooldown da impostare dopo che l'utente ha chiesto di modificare le luci
|
||||
*/
|
||||
private static final int COOLDOWN_IN_MINUTES = 20;
|
||||
|
||||
/**
|
||||
* Variabile che serve ad impostare un cooldown per la luminosita' automatica
|
||||
*/
|
||||
private final AtomicInteger cooldown = new AtomicInteger(0);
|
||||
|
||||
/**
|
||||
* La variabile per far partire della musica
|
||||
*/
|
||||
private final Audio audio;
|
||||
|
||||
/**
|
||||
* Costruttore
|
||||
*/
|
||||
public VariousThreads() {
|
||||
audio = new AudioFile(); // se si vuole solamente far partire una traccia tra quelle fornite e non Youtube
|
||||
// audio = System.getProperty("os.name").startsWith("Windows")? new Musich():new AudioFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fa partire il server Webhook per DialogFlow e continua l'esecuzione
|
||||
* @param lights le luci che deve controllare
|
||||
*/
|
||||
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; });
|
||||
df.addOnAction("LightsOFF", (params) -> { lights.turnOff(); cooldown.set(COOLDOWN_IN_MINUTES); return null; });
|
||||
df.addOnAction("ColorLoop", (params) -> { lights.colorLoop(); return null; });
|
||||
df.addOnAction("ChangeColor", (params) -> {
|
||||
lights.changeColor(params.get("color").getAsString());
|
||||
return null;
|
||||
});
|
||||
df.addOnAction("SetLights", (params) -> {
|
||||
lights.setBrightness(params.get("intensity").getAsInt());
|
||||
cooldown.set(COOLDOWN_IN_MINUTES);
|
||||
return null;
|
||||
});
|
||||
df.addOnAction("LightsDOWN", (params) -> {
|
||||
if(params.get("intensity").getAsString().equals(""))
|
||||
lights.decreaseBrightness();
|
||||
else
|
||||
lights.decreaseBrightness(params.get("intensity").getAsInt());
|
||||
cooldown.set(COOLDOWN_IN_MINUTES);
|
||||
return null;
|
||||
});
|
||||
df.addOnAction("LightsUP", (params) -> {
|
||||
if(params.get("intensity").getAsString().equals(""))
|
||||
lights.increaseBrightness();
|
||||
else
|
||||
lights.increaseBrightness(params.get("intensity").getAsInt());
|
||||
cooldown.set(COOLDOWN_IN_MINUTES);
|
||||
return null;
|
||||
});
|
||||
df.addOnAction("SetMusic", (param) -> {
|
||||
audio.playRandom(param.get("musicType").getAsString());
|
||||
return null;
|
||||
});
|
||||
df.addOnAction("StopMusic", (params) -> { audio.stop(); return null; });
|
||||
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) -> {
|
||||
double sleep = fitbit.getHoursSleep();
|
||||
return String.format("Oggi hai dormito per %.2f ore", sleep);
|
||||
});
|
||||
|
||||
df.startServer();
|
||||
SeniorAssistant.LOG.info("Webhook partito");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gestione DB in modo che si aggiorni ogni ora e ogni giorno.<br>
|
||||
* Da quando viene fatto partire aspetta un giorno e poi aggiorna i dati sul DB; ripete.<br>
|
||||
* Da quando viene fatto partire aspetta un'ora e poi aggiorna i dati sul DB; ripete.
|
||||
* @param database il database a cui inviare i dati
|
||||
* @param fitbit la sorgente di dati
|
||||
*/
|
||||
public void startInsertData(final Database database, final Fitbit fitbit) {
|
||||
try {
|
||||
Thread hourlyData = Database.insertHourlyDataIn(database, fitbit, 5);
|
||||
Thread dailyData = Database.insertDailyData(database, fitbit, 5);
|
||||
|
||||
hourlyData.start();
|
||||
dailyData.start();
|
||||
SeniorAssistant.LOG.info("Thread per gli aggiornamenti automatici partiti");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gestione delle luci in modo che cambino la luminosita' in base ai dati ricevuti dal sensore<br>
|
||||
* Se l'utente pero' cambia il valore delle luci di sua volonta', il processo non modifichera' le luci per almeno un'ora.
|
||||
* @param lights le luci da controllare
|
||||
* @param sensor i sensori da cui prendere i dati
|
||||
*/
|
||||
public void startHueAutoBrightness(final Hue lights, final Sensor sensor) {
|
||||
final int minute = 1;
|
||||
final int minBrightness = 20; // valore che va da 0 a 100
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
|
||||
Thread thread = getThreadStartingEach(() -> {
|
||||
if(cooldown.addAndGet(-minute) <= 0) {
|
||||
calendar.setTimeInMillis(System.currentTimeMillis());
|
||||
|
||||
// puo' avere un valore compreso tra -1 e 1
|
||||
final double brightFactor =
|
||||
calculateBrightFactor(
|
||||
calendar.get(Calendar.HOUR_OF_DAY),
|
||||
calendar.get(Calendar.MINUTE),
|
||||
sensor.getBrightnessLevel(),
|
||||
minBrightness
|
||||
);
|
||||
|
||||
lights.addBrightness(brightFactor*100);
|
||||
}
|
||||
}, minute, "auto-brightness");
|
||||
|
||||
thread.start();
|
||||
SeniorAssistant.LOG.info("Thread per l'impostazione automatica della luminosita' partito");
|
||||
}
|
||||
|
||||
/**
|
||||
* Permette di far partire un thread che controlla le Hue in base al battito cardiaco.<br>
|
||||
* Il battito e' preso sia in tempo reale dal fitbit, che dal databse, che permette di fare una analisi statistica.
|
||||
* @param lights le luci da controllare
|
||||
* @param fitbit da dove ricevo i dati in tempo reale
|
||||
* @param database da dove posso analizzare i vecchi dati
|
||||
*/
|
||||
public void startHueControlledByHeartBeat(final Hue lights, final Fitbit fitbit, final Database database) {
|
||||
final int minutes = 30;
|
||||
final int delta = 15;
|
||||
Thread thread = getThreadStartingEach(new Runnable() {
|
||||
@Override
|
||||
public synchronized void run() {
|
||||
double sum=0;
|
||||
int count=0;
|
||||
double average;
|
||||
|
||||
List<HeartRate> heartRate = database.getHeartDataOfLast(15);
|
||||
Calendar now = Calendar.getInstance();
|
||||
Calendar past = Calendar.getInstance();
|
||||
now.setTimeInMillis(System.currentTimeMillis());
|
||||
|
||||
for(HeartRate rate: heartRate) {
|
||||
past.setTimeInMillis(rate.getDate());
|
||||
if(past.get(Calendar.HOUR_OF_DAY) == now.get(Calendar.HOUR_OF_DAY)) {
|
||||
sum += rate.getAverage();
|
||||
count++;
|
||||
}
|
||||
}
|
||||
average = count!=0? sum/count:0;
|
||||
|
||||
double rateNow = fitbit.getHeartRate(minutes);
|
||||
if (Math.abs(rateNow-average) > delta ) { // ALTO
|
||||
lights.decreaseBrightness();
|
||||
audio.playRandom("relax");
|
||||
new AudioFile("molti battiti.wav");
|
||||
}
|
||||
else if (Math.abs(rateNow-average) < delta) { // BASSO avvisa ma niente musica
|
||||
lights.increaseBrightness();
|
||||
new AudioFile("pochi battiti.wav");
|
||||
}
|
||||
}
|
||||
}, minutes, "lights-with-heartbeat");
|
||||
|
||||
thread.start();
|
||||
SeniorAssistant.LOG.info("Thread per il controllo delle luci tramite il battito cardiaco partito");
|
||||
}
|
||||
|
||||
/**
|
||||
* Controlla che ad una certa ora si siano fatti abbastanza passi, ed in caso contrario avvisa tramite un messaggio vocale.<br>
|
||||
*
|
||||
* @param database da dove vediamo se si sono fatti abbastanza passi
|
||||
*/
|
||||
public void startCheckSteps(final Database database) {
|
||||
final int norm = 4500;
|
||||
final int delta = 1500; // average steps for 60 year old -> 3.500-5.500 or so they say
|
||||
|
||||
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;
|
||||
|
||||
if (Math.abs(norm-average) > delta )
|
||||
new AudioFile("molti passi.wav");
|
||||
else if (Math.abs(norm-average) < delta)
|
||||
new AudioFile("pochi passi.wav");
|
||||
|
||||
}
|
||||
}, 20, "checking-steps");
|
||||
|
||||
thread.start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Calcola un numero compreso fra -1 e 1 che indica se c'e' bisogno o meno di luminosita'<br>
|
||||
* Se i valori inseriti sono maggiori o minori di quelli consentiti, allora verranno limitati<br>
|
||||
* ovvero se sono minori del minimo esso diventera' il minimo, stessa cosa con il massimo.
|
||||
* @param hour l'ora corrente (valore da 0 a 23)
|
||||
* @param minutes i minuti correnti (valore da 0 a 59)
|
||||
* @param sensorBright la luminosita' segnata dal sensore (valore da 0 a 100)
|
||||
* @param minBrightness la luminosita' minima che si vuole avere (valore da 0 a 100)
|
||||
* @return un valore indicante quanta luminosita' si ha bisogno nell'ora indicata e con la luminosita' indicata
|
||||
*/
|
||||
public static double calculateBrightFactor(int hour, int minutes, double sensorBright, double minBrightness) {
|
||||
hour = hour<0? 0:hour>23? 23:hour;
|
||||
minutes = minutes<0? 0:minutes>59? 59:minutes;
|
||||
minBrightness = minBrightness<0? 0:minBrightness>100? 100:minBrightness;
|
||||
sensorBright = sensorBright<0? 0:sensorBright>100? 100:sensorBright;
|
||||
|
||||
// Valore compreso tra -1(poca luminosita') e 1(molta luminosita')
|
||||
sensorBright = sensorBright/100;
|
||||
minBrightness = 0.5*Math.abs(1-minBrightness/100);
|
||||
|
||||
// Puo' avere un valore compreso tra 1(mezzanotte) e 0(mezzogiorno) => il valore minimo(0) puo' aumentare grazie a minBrightness)
|
||||
final double maxIntensity = minBrightness*Math.cos((2*Math.PI*(hour + (minutes/60.0)) /24)) + (1-minBrightness);
|
||||
|
||||
return maxIntensity-sensorBright;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restuisce un thread che se fatto partire, esegue il runnable in un sub-thread all'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>
|
||||
* 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 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, final int minutes, String threadName) {
|
||||
if(minutes<1)
|
||||
return null;
|
||||
|
||||
return new Thread(new Runnable() {
|
||||
@Override
|
||||
public synchronized void run() {
|
||||
boolean notInterrupted = true;
|
||||
do {
|
||||
try {
|
||||
wait(minutes * MILLISEC_IN_MINUTE);
|
||||
Thread thread = new Thread(runnable, threadName + "-" + new Timestamp(System.currentTimeMillis()));
|
||||
thread.start();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
notInterrupted = false;
|
||||
}
|
||||
} while (notInterrupted);
|
||||
}
|
||||
}, threadName);
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
package manage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
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;
|
||||
import com.google.api.client.http.HttpRequestFactory;
|
||||
import com.google.api.client.http.HttpResponse;
|
||||
import com.google.api.client.http.HttpTransport;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
import com.google.api.client.json.GenericJson;
|
||||
import com.google.api.client.json.JsonFactory;
|
||||
import com.google.api.client.json.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;
|
||||
|
||||
|
||||
public class AuthFITBIT {
|
||||
|
||||
private final HttpRequestFactory requestFactory;
|
||||
|
||||
public AuthFITBIT() throws Exception {
|
||||
DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
|
||||
final Credential credential = authorize();
|
||||
|
||||
this.requestFactory = HTTP_TRANSPORT.createRequestFactory( request -> {
|
||||
credential.initialize(request);
|
||||
request.setParser(new JsonObjectParser(JSON_FACTORY));
|
||||
});
|
||||
}
|
||||
|
||||
private static ObjectMapper mapper = new ObjectMapper();
|
||||
/** Directory to store user credentials. */
|
||||
/* Throw a Warning when change permission: they said it's a google bug 'cause is meant to run in linux/unix
|
||||
*
|
||||
* https://stackoverflow.com/questions/30634827/warning-unable-to-change-permissions-for-everybody
|
||||
* https://github.com/google/google-http-java-client/issues/315
|
||||
*/
|
||||
private static final java.io.File DATA_STORE_DIR =
|
||||
new java.io.File(System.getProperty("user.home"), ".store/seniorAssistant");
|
||||
|
||||
/**
|
||||
* Global instance of the {@link DataStoreFactory}. The best practice is to make it a single
|
||||
* globally shared instance across your application.
|
||||
*/
|
||||
private static FileDataStoreFactory DATA_STORE_FACTORY;
|
||||
|
||||
/** OAuth 2 scope. */
|
||||
private static final String SCOPE[] = new String[]{"activity","heartrate","sleep","settings"};
|
||||
/** Global instance of the HTTP transport. */
|
||||
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
|
||||
|
||||
/** Global instance of the JSON factory. */
|
||||
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
|
||||
|
||||
private static final String TOKEN_SERVER_URL = " https://api.fitbit.com/oauth2/token";
|
||||
private static final String AUTHORIZATION_SERVER_URL = "https://www.fitbit.com/oauth2/authorize";
|
||||
|
||||
/** Authorizes the installed application to access user's protected data. */
|
||||
private Credential authorize() throws Exception {
|
||||
// set up authorization code flow
|
||||
AuthorizationCodeFlow flow = new AuthorizationCodeFlow.Builder(BearerToken
|
||||
.authorizationHeaderAccessMethod(),
|
||||
HTTP_TRANSPORT,
|
||||
JSON_FACTORY,
|
||||
new GenericUrl(TOKEN_SERVER_URL),
|
||||
new BasicAuthentication (
|
||||
OAuth2ClientCredentials.API_KEY, OAuth2ClientCredentials.API_SECRET),
|
||||
OAuth2ClientCredentials.API_KEY,
|
||||
AUTHORIZATION_SERVER_URL).setScopes(Arrays.asList(SCOPE))
|
||||
.setDataStoreFactory(DATA_STORE_FACTORY).build();
|
||||
// authorize
|
||||
LocalServerReceiver receiver = new LocalServerReceiver.Builder().setHost(
|
||||
OAuth2ClientCredentials.DOMAIN).setPort(OAuth2ClientCredentials.PORT).build();
|
||||
|
||||
return new AuthorizationCodeInstalledApp(flow, receiver).authorize( "user" );
|
||||
}
|
||||
|
||||
public <O> O run(String url, Class<O> returnClass) throws IOException {
|
||||
return run(url, returnClass, false);
|
||||
}
|
||||
|
||||
public <O> O run(String url, Class<O> returnClass, boolean useAsParse) throws IOException {
|
||||
FITBITUrl fitbitUrl = new FITBITUrl(url);
|
||||
fitbitUrl.setFields("");
|
||||
|
||||
HttpRequest request = requestFactory.buildGetRequest(fitbitUrl);
|
||||
HttpResponse response = request.execute();
|
||||
|
||||
/*
|
||||
GenericJson json = response.parseAs(GenericJson.class);
|
||||
response.disconnect();
|
||||
|
||||
System.out.println(returnClass.getSimpleName());
|
||||
System.out.println(url);
|
||||
System.out.println(json.toPrettyString());
|
||||
|
||||
return mapper.readValue(json.toString(), returnClass);
|
||||
*/
|
||||
|
||||
/**/
|
||||
O ret = ( useAsParse ?
|
||||
response.parseAs(returnClass) :
|
||||
mapper.readValue(response.parseAs(GenericJson.class).toString(), returnClass)
|
||||
);
|
||||
|
||||
response.disconnect();
|
||||
return ret;
|
||||
/**/
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package manage.FITBITData;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Device {
|
||||
|
||||
public String lastSyncTime;
|
||||
|
||||
// @JsonProperty ("lastSyncTime")
|
||||
public String getLastSyncTime(List<Map<String,String>> sync){
|
||||
lastSyncTime = null;
|
||||
for(Map<String, String > d: sync) {
|
||||
String temp = d.get("lastSyncTime");
|
||||
if ((lastSyncTime == null) || (lastSyncTime.compareTo(temp) < 0))
|
||||
lastSyncTime = temp;
|
||||
}
|
||||
return lastSyncTime;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package manage.FITBITData;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import manage.AuthFITBIT;
|
||||
|
||||
public class FitBit {
|
||||
|
||||
public static final String BASIC_URL = "https://api.fitbit.com/";
|
||||
public static final String USER = "/user/-/";
|
||||
private static final long MINUTE = 60000; /* 5 minutes in millisec */
|
||||
|
||||
private final AuthFITBIT auth;
|
||||
private final Map<Class<?>, Long> latestRequest = new HashMap<>();
|
||||
private final Calendar calendar = Calendar.getInstance();
|
||||
|
||||
private HeartRate heart = null;
|
||||
private Sleep sleep = null;
|
||||
private Steps steps = null;
|
||||
|
||||
public FitBit() throws Exception {
|
||||
this(new AuthFITBIT());
|
||||
}
|
||||
|
||||
public FitBit(AuthFITBIT auth) {
|
||||
if(auth == null)
|
||||
throw new NullPointerException("I must have an Auth for the FitBit");
|
||||
this.auth = auth;
|
||||
}
|
||||
|
||||
/* passi */
|
||||
public int getSteps() throws IOException {
|
||||
if(shouldUpdateFor(Steps.class)) {
|
||||
long currentMillisec = System.currentTimeMillis();
|
||||
|
||||
steps = auth.run(BASIC_URL + "1" + USER + "activities/steps/date/today/1w.json", Steps.class);
|
||||
latestRequest.put(steps.getClass(), currentMillisec);
|
||||
}
|
||||
return steps.getSteps();
|
||||
}
|
||||
|
||||
/* battito */
|
||||
public double getHeartRate() throws IOException {
|
||||
if(shouldUpdateFor(HeartRate.class)) {
|
||||
long currentMillisec = System.currentTimeMillis();
|
||||
|
||||
String now = getHourMinutes(currentMillisec);
|
||||
String ago = getHourMinutes(currentMillisec-(MINUTE*15));
|
||||
|
||||
if(now.compareTo(ago) < 0)
|
||||
ago = "00:00";
|
||||
|
||||
heart = auth.run(BASIC_URL + "1" + USER + "activities/heart/date/today/1d/1sec/time/"+ago+"/"+now+".json", HeartRate.class);
|
||||
latestRequest.put(heart.getClass(), currentMillisec);
|
||||
}
|
||||
return heart.getAverage();
|
||||
}
|
||||
|
||||
/* sonno */
|
||||
public Object getHoursSleep() throws IOException {
|
||||
if(shouldUpdateFor(Sleep.class)) {
|
||||
long currentMillisec = System.currentTimeMillis();
|
||||
|
||||
sleep = auth.run(BASIC_URL + "1.2" + USER + "sleep/date/today.json", Sleep.class);
|
||||
latestRequest.put(sleep.getClass(), currentMillisec);
|
||||
}
|
||||
return sleep.getMinutesAsleep();
|
||||
}
|
||||
|
||||
private boolean shouldUpdateFor(Class<?> type) {
|
||||
try {
|
||||
long current = System.currentTimeMillis();
|
||||
long latest = latestRequest.get(type);
|
||||
|
||||
if (current - latest > MINUTE * 5)
|
||||
return true;
|
||||
return false;
|
||||
} catch (NullPointerException e) {}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getHourMinutes(long milliseconds) {
|
||||
calendar.setTimeInMillis(milliseconds);
|
||||
|
||||
int hour = calendar.get(Calendar.HOUR_OF_DAY);
|
||||
int minu = calendar.get(Calendar.MINUTE);
|
||||
return String.format("%02d:%02d", hour, minu);
|
||||
}
|
||||
|
||||
// Device dev = auth.run(BASIC_URL + "devices.json", Device.class);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package manage.FITBITData;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Sleep {
|
||||
|
||||
private int minutesAsleep;
|
||||
|
||||
public int getMinutesAsleep() {
|
||||
return minutesAsleep;
|
||||
}
|
||||
|
||||
@JsonProperty("summary")
|
||||
public void setMinutesAsleep(Map<String, Object> map) {
|
||||
minutesAsleep = (int) map.get("totalMinutesAsleep");
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package manage.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;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Steps {
|
||||
|
||||
private int steps=0;
|
||||
|
||||
@JsonProperty("activities-steps")
|
||||
public void setSteps(Map<String, String>[] array) {
|
||||
SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Date now = new Date();
|
||||
String strDate = sdfDate.format(now);
|
||||
|
||||
for(Map<String, String> map : array)
|
||||
if(map.get("dateTime").equals(strDate))
|
||||
steps = Integer.parseInt(map.get("value"))+1;
|
||||
}
|
||||
|
||||
public int getSteps() {
|
||||
return steps;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package manage;
|
||||
|
||||
import com.google.api.client.http.GenericUrl;
|
||||
import com.google.api.client.util.Key;
|
||||
|
||||
public class FITBITUrl extends GenericUrl {
|
||||
|
||||
@Key
|
||||
private String fields;
|
||||
|
||||
public FITBITUrl(String encodedUrl) {
|
||||
super(encodedUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the fields
|
||||
*/
|
||||
public String getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fields the fields to set
|
||||
*/
|
||||
public void setFields(String fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package manage;
|
||||
|
||||
public class OAuth2ClientCredentials {
|
||||
|
||||
/** Value of the "API Key". */
|
||||
public static final String API_KEY = "22CSTL"; //maybe togliere le virgolette
|
||||
|
||||
/** Value of the "API Secret". */
|
||||
public static final String API_SECRET = "ea2452013abd35609940ce5601960a08"; //maybe togliere le virgolette
|
||||
|
||||
/** Port in the "Callback URL". */
|
||||
public static final int PORT = 8080;
|
||||
|
||||
/** Domain name in the "Callback URL". */
|
||||
public static final String DOMAIN = "127.0.0.1";
|
||||
}
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
package manage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
/**
|
||||
* A generic class to perform HTTP calls and parse the resulting JSON.
|
||||
*
|
||||
* @author <a href="mailto:luigi.derussis@uniupo.it">Luigi De Russis</a>
|
||||
* @version 1.0 (18/05/2017)
|
||||
*/
|
||||
public class Rest {
|
||||
|
||||
// init gson
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
/**
|
||||
* Perform a GET request towards a server
|
||||
*
|
||||
* @param URL the @link{URL} to call
|
||||
* @return the response, parsed from JSON
|
||||
*/
|
||||
public static Map<String, ?> get(String URL) {
|
||||
// init
|
||||
Map<String, ?> response = new HashMap<>();
|
||||
|
||||
CloseableHttpClient httpclient = HttpClients.createDefault();
|
||||
HttpGet request = new HttpGet(URL);
|
||||
|
||||
CloseableHttpResponse result = null;
|
||||
|
||||
try {
|
||||
result = httpclient.execute(request);
|
||||
String json = EntityUtils.toString(result.getEntity());
|
||||
// do something useful with the response body
|
||||
response = gson.fromJson(json, Map.class);
|
||||
// should be inside a finally...
|
||||
result.close();
|
||||
httpclient.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a PUT request towards a server
|
||||
*
|
||||
* @param URL the @link{URL} to call
|
||||
* @param contentBody the content body of the request
|
||||
* @param contentType the content type of the request
|
||||
*/
|
||||
public static void put(String URL, String contentBody, String contentType) {
|
||||
// init
|
||||
CloseableHttpClient httpclient = HttpClients.createDefault();
|
||||
HttpPut request = new HttpPut(URL);
|
||||
StringEntity params = null;
|
||||
|
||||
try {
|
||||
params = new StringEntity(contentBody);
|
||||
request.addHeader("content-type", contentType);
|
||||
request.setEntity(params);
|
||||
// I don't really care about the response
|
||||
HttpResponse result = httpclient.execute(request);
|
||||
// should be in finally...
|
||||
httpclient.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
158
src/main/java/oauth/AuthFitbit.java
Normal file
158
src/main/java/oauth/AuthFitbit.java
Normal file
@@ -0,0 +1,158 @@
|
||||
package oauth;
|
||||
|
||||
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.http.*;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
import com.google.api.client.json.JsonFactory;
|
||||
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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Classe piu' importante per la connessione al fitbit
|
||||
*/
|
||||
public class AuthFitbit {
|
||||
|
||||
/**
|
||||
* Un logger per rendere più semplice il debug
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger("Fitbit Auth");
|
||||
|
||||
/**
|
||||
* Un mapper per trasformare i json in mappe.
|
||||
*/
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* Directory dove vengono messi i dati utente (Token)<br>
|
||||
* <br>
|
||||
* Throw a Warning when change permission: they said it's a google bug 'cause is meant to run in linux/unix<br>
|
||||
* https://stackoverflow.com/questions/30634827/warning-unable-to-change-permissions-for-everybody<br>
|
||||
* https://github.com/google/google-http-java-client/issues/315<br>
|
||||
*/
|
||||
private static final java.io.File DATA_STORE_DIR = new java.io.File(System.getProperty("user.home"), ".store/seniorAssistant");
|
||||
|
||||
/**
|
||||
* OAuth2 scope.<br>
|
||||
* Nel nostro caso sono le varie categorie dove si trovano le informazioni di cui abbiamo bisogno
|
||||
*/
|
||||
private static final String SCOPE[] = new String[]{"activity","heartrate","sleep","settings"};
|
||||
|
||||
/**
|
||||
* Instanza globale di HttpTranspot necessaria per l'autorizzazione e per le richieste
|
||||
*/
|
||||
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
|
||||
|
||||
/**
|
||||
* Istanza globale di una Json Factory
|
||||
*/
|
||||
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
|
||||
|
||||
/**
|
||||
* Url dove e' possiblie richiedere il token
|
||||
*/
|
||||
private static final String TOKEN_SERVER_URL = " https://api.fitbit.com/oauth2/token";
|
||||
|
||||
/**
|
||||
* Pagina dove si richiede l'autorizzazione a tutti i campi richiesti
|
||||
*/
|
||||
private static final String AUTHORIZATION_SERVER_URL = "https://www.fitbit.com/oauth2/authorize";
|
||||
|
||||
/**
|
||||
* Istanza globale del {@link DataStoreFactory}. Il miglior metodo e' creare una singola
|
||||
* istanza globale condivisa attraverso tutta l'applicazione.
|
||||
*/
|
||||
private static FileDataStoreFactory DATA_STORE_FACTORY;
|
||||
|
||||
/**
|
||||
* Un HttpRequestFactory che serve per creare una richiesta http
|
||||
*/
|
||||
private final HttpRequestFactory requestFactory;
|
||||
|
||||
/**
|
||||
* Prova a connettersi al sito di Fitbit per controllare l'autorizzazione<br>
|
||||
* Se la richiesta inviata e' sbagliata lancia una eccezione.<br>
|
||||
* Se l'utente non ha ancora autorizzato l'applicazioe, allora una pagina sul browser (o un link in console) appare<br>
|
||||
* ci si logga e si lascia l'autorizzazione a questa applicazione.
|
||||
* @throws Exception per qualunque cosa apparentemente (e noi ci siamo riusciti)
|
||||
*/
|
||||
public AuthFitbit() throws Exception {
|
||||
DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
|
||||
final Credential credential = authorize();
|
||||
|
||||
this.requestFactory = HTTP_TRANSPORT.createRequestFactory( request -> {
|
||||
credential.initialize(request);
|
||||
request.setParser(new JsonObjectParser(JSON_FACTORY));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Autorizza l'applicazione ad accedere ai dati utente richiesti
|
||||
*/
|
||||
private Credential authorize() throws Exception {
|
||||
// set up authorization code flow
|
||||
AuthorizationCodeFlow flow = new AuthorizationCodeFlow.Builder(BearerToken
|
||||
.authorizationHeaderAccessMethod(),
|
||||
HTTP_TRANSPORT,
|
||||
JSON_FACTORY,
|
||||
new GenericUrl(TOKEN_SERVER_URL),
|
||||
new BasicAuthentication (
|
||||
OAuth2ClientCredentials.API_KEY, OAuth2ClientCredentials.API_SECRET),
|
||||
OAuth2ClientCredentials.API_KEY,
|
||||
AUTHORIZATION_SERVER_URL).setScopes(Arrays.asList(SCOPE))
|
||||
.setDataStoreFactory(DATA_STORE_FACTORY).build();
|
||||
// authorize
|
||||
LocalServerReceiver receiver = new LocalServerReceiver.Builder().setHost(
|
||||
OAuth2ClientCredentials.DOMAIN).setPort(OAuth2ClientCredentials.PORT).build();
|
||||
|
||||
return new AuthorizationCodeInstalledApp(flow, receiver).authorize( "user" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Effettua una chiamata al server fitbit richiedendo i dati indicati dall'url
|
||||
*
|
||||
* @param url l'url in cui effettuare la richiesta
|
||||
* @return una stringa in formato Json, che e' il risultato
|
||||
* @throws IOException nel caso ci sia un errore con la richiesta
|
||||
*/
|
||||
public String run(String url) throws IOException {
|
||||
GenericUrl genericUrl = new GenericUrl(url);
|
||||
HttpRequest request = requestFactory.buildGetRequest(genericUrl);
|
||||
HttpResponse response = request.execute();
|
||||
|
||||
String content = response.parseAsString();
|
||||
response.disconnect();
|
||||
LOG.debug("Recived: " + content);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fa una chiamata al server fitbit richiedendo i dati indicati dall'url<br>
|
||||
* La classe e' richiesta se si vuole fare il parsing diretto e ricevere la classe parsificata con un mapper<br>
|
||||
* in questo modo non tutti i campi devono esistere
|
||||
*
|
||||
* @param url l'url in cui effettuare la richiesta
|
||||
* @param returnClass la classe da ritornare
|
||||
* @param <O> la classe che ritorna
|
||||
* @return un oggetto rappresentante la richiesta fatta all'url
|
||||
* @throws IOException nel caso ci sia un errore con la richiesta o con il parsing di quest'ultima
|
||||
*/
|
||||
public <O> O run(String url, Class<O> returnClass) throws IOException {
|
||||
O ret = MAPPER.readValue(this.run(url), returnClass);
|
||||
LOG.debug("Saved in class: " + JSON_FACTORY.toString(ret));
|
||||
|
||||
return ret;
|
||||
/**/
|
||||
}
|
||||
}
|
||||
296
src/main/java/oauth/LocalServerReceiver.java
Normal file
296
src/main/java/oauth/LocalServerReceiver.java
Normal file
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/main/java/oauth/OAuth2ClientCredentials.java
Normal file
28
src/main/java/oauth/OAuth2ClientCredentials.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package oauth;
|
||||
|
||||
/**
|
||||
* Classe supporto per l'oauth contenente tutti i dati dell'applicazione
|
||||
*/
|
||||
public class OAuth2ClientCredentials {
|
||||
|
||||
/**
|
||||
* Il valore dell' "API Key".
|
||||
*/
|
||||
public static final String API_KEY = "22CSTL";
|
||||
|
||||
/**
|
||||
* Il valore dell' "API Secret"
|
||||
*/
|
||||
public static final String API_SECRET = "ea2452013abd35609940ce5601960a08";
|
||||
|
||||
/**
|
||||
* La porta per il "Callback URL".
|
||||
*/
|
||||
public static final int PORT = 8080;
|
||||
|
||||
/**
|
||||
* Il dominio del "Callback URL"
|
||||
*/
|
||||
public static final String DOMAIN = "127.0.0.1";
|
||||
}
|
||||
|
||||
94
src/main/java/support/Rest.java
Normal file
94
src/main/java/support/Rest.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package support;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Una classe generica che invia delle richieste Rest e le parsifica nel JSON corrispondente
|
||||
*/
|
||||
public class Rest {
|
||||
|
||||
/**
|
||||
* Un logger per vedere quando le richieste falliscono
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger("Rest");
|
||||
|
||||
/**
|
||||
* Un GSON utile per il parsing della risposta
|
||||
*/
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
/**
|
||||
* Perform a GET request towards a server
|
||||
*
|
||||
* @param URL the @link{URL} to call
|
||||
* @return the response, parsed from JSON
|
||||
*/
|
||||
public static Map<String, ?> get(String URL) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
String json = "";
|
||||
|
||||
try {
|
||||
CloseableHttpClient httpclient = HttpClients.createDefault();
|
||||
HttpGet request = new HttpGet(URL);
|
||||
CloseableHttpResponse result = httpclient.execute(request);
|
||||
json = EntityUtils.toString(result.getEntity());
|
||||
|
||||
try {
|
||||
response = gson.fromJson(json, Map.class);
|
||||
} catch (JsonSyntaxException e) {
|
||||
response.put("list", gson.fromJson(json, List.class));
|
||||
}
|
||||
|
||||
result.close();
|
||||
httpclient.close();
|
||||
LOG.debug("GET response: " + json);
|
||||
} catch (Exception e) {
|
||||
LOG.error("GET: " + URL + " " + e.getMessage() + " " + json);
|
||||
}
|
||||
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a PUT request towards a server
|
||||
*
|
||||
* @param URL the @link{URL} to call
|
||||
* @param contentBody the content body of the request
|
||||
* @param contentType the content type of the request
|
||||
*/
|
||||
public static void put(String URL, String contentBody, String contentType) {
|
||||
try {
|
||||
CloseableHttpClient httpclient = HttpClients.createDefault();
|
||||
HttpPut request = new HttpPut(URL);
|
||||
StringEntity params = new StringEntity(contentBody);
|
||||
|
||||
request.addHeader("content-type", contentType);
|
||||
request.setEntity(params);
|
||||
|
||||
HttpResponse result = httpclient.execute(request);
|
||||
String json = EntityUtils.toString(result.getEntity());
|
||||
httpclient.close();
|
||||
|
||||
LOG.debug("PUT response: " + json);
|
||||
} catch (Exception e) {
|
||||
LOG.error("PUT: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,233 +1,234 @@
|
||||
package device;
|
||||
|
||||
import de.fh_zwickau.informatik.sensor.IZWayApiCallbacks;
|
||||
import de.fh_zwickau.informatik.sensor.model.devicehistory.DeviceHistory;
|
||||
import de.fh_zwickau.informatik.sensor.model.devicehistory.DeviceHistoryList;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.Device;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.DeviceList;
|
||||
import de.fh_zwickau.informatik.sensor.model.instances.Instance;
|
||||
import de.fh_zwickau.informatik.sensor.model.instances.InstanceList;
|
||||
import de.fh_zwickau.informatik.sensor.model.locations.Location;
|
||||
import de.fh_zwickau.informatik.sensor.model.locations.LocationList;
|
||||
import de.fh_zwickau.informatik.sensor.model.modules.ModuleList;
|
||||
import de.fh_zwickau.informatik.sensor.model.namespaces.NamespaceList;
|
||||
import de.fh_zwickau.informatik.sensor.model.notifications.Notification;
|
||||
import de.fh_zwickau.informatik.sensor.model.notifications.NotificationList;
|
||||
import de.fh_zwickau.informatik.sensor.model.profiles.Profile;
|
||||
import de.fh_zwickau.informatik.sensor.model.profiles.ProfileList;
|
||||
import de.fh_zwickau.informatik.sensor.model.zwaveapi.controller.ZWaveController;
|
||||
import de.fh_zwickau.informatik.sensor.model.zwaveapi.devices.ZWaveDevice;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Mandatory callback class for the Z-Way Library in use.
|
||||
* Really trivial implementation: log all the responses at debug level.
|
||||
*
|
||||
* @author <a href="mailto:luigi.derussis@uniupo.it">Luigi De Russis</a>
|
||||
* @version 1.0 (24/05/2017)
|
||||
* @see <a href="https://github.com/pathec/ZWay-library-for-Java">Z-Way Library on GitHub</a> for documentation about the used library
|
||||
*/
|
||||
public class ZWaySimpleCallback implements IZWayApiCallbacks {
|
||||
|
||||
@Override
|
||||
public void getStatusResponse(String s) {
|
||||
this.logMessage("Status response: " + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getRestartResponse(Boolean aBoolean) {
|
||||
this.logMessage("Restart response: " + aBoolean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getLoginResponse(String s) {
|
||||
this.logMessage("Login response: " + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getNamespacesResponse(NamespaceList namespaceList) {
|
||||
this.logMessage("Namespaces are: " + namespaceList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getModulesResponse(ModuleList moduleList) {
|
||||
this.logMessage("Modules are: " + moduleList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getInstancesResponse(InstanceList instanceList) {
|
||||
this.logMessage("Instances are: " + instanceList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInstanceResponse(Instance instance) {
|
||||
this.logMessage("Received a POST for the instance: " + instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getInstanceResponse(Instance instance) {
|
||||
this.logMessage("The instance is: " + instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putInstanceResponse(Instance instance) {
|
||||
this.logMessage("Received a PUT for the instance: " + instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteInstanceResponse(boolean b) {
|
||||
this.logMessage("The instance has been deleted? " + String.valueOf(b));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDevicesResponse(DeviceList deviceList) {
|
||||
this.logMessage("Devices are: " + deviceList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putDeviceResponse(Device device) {
|
||||
this.logMessage("Received a PUT for device: " + device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDeviceResponse(Device device) {
|
||||
this.logMessage("The device is: " + device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDeviceCommandResponse(String s) {
|
||||
this.logMessage("The device command is: " + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getLocationsResponse(LocationList locationList) {
|
||||
this.logMessage("Locations are: " + locationList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postLocationResponse(Location location) {
|
||||
this.logMessage("Received a POST for location: " + location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getLocationResponse(Location location) {
|
||||
this.logMessage("The location is: " + location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putLocationResponse(Location location) {
|
||||
this.logMessage("Received a PUT for location: " + location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteLocationResponse(boolean b) {
|
||||
this.logMessage("Location has been deleted? " + b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getProfilesResponse(ProfileList profileList) {
|
||||
this.logMessage("Profiles are: " + profileList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProfileResponse(Profile profile) {
|
||||
this.logMessage("Received a POST for profile: " + profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getProfileResponse(Profile profile) {
|
||||
this.logMessage("The profile is: " + profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putProfileResponse(Profile profile) {
|
||||
this.logMessage("Received a PUT for profile: " + profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteProfileResponse(boolean b) {
|
||||
this.logMessage("Profile has been deleted? " + b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getNotificationsResponse(NotificationList notificationList) {
|
||||
this.logMessage("Notifications are: " + notificationList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getNotificationResponse(Notification notification) {
|
||||
this.logMessage("The notification is: " + notification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putNotificationResponse(Notification notification) {
|
||||
this.logMessage("Received a PUT for notification: " + notification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDeviceHistoriesResponse(DeviceHistoryList deviceHistoryList) {
|
||||
this.logMessage("Device histories are: " + deviceHistoryList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDeviceHistoryResponse(DeviceHistory deviceHistory) {
|
||||
this.logMessage("The device history is: " + deviceHistory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getZWaveDeviceResponse(ZWaveDevice zWaveDevice) {
|
||||
this.logMessage("The Z-Wave device is: " + zWaveDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getZWaveControllerResponse(ZWaveController zWaveController) {
|
||||
this.logMessage("The Z-Wave controller is: " + zWaveController);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apiError(String s, boolean b) {
|
||||
this.logError("API Error: " + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void httpStatusError(int i, String s, boolean b) {
|
||||
this.logError("HTTP Status Error: " + i + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authenticationError() {
|
||||
this.logError("Authentication Error");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void responseFormatError(String s, boolean b) {
|
||||
this.logError("Wrong format: " + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void message(int i, String s) {
|
||||
this.logMessage("You've got a message: " + i + " " + s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to print the log messages of this class.
|
||||
*
|
||||
* @param message the {@link String} to print
|
||||
*/
|
||||
private void logMessage(String message) {
|
||||
Logger logger = LoggerFactory.getLogger(Sensor.class);
|
||||
logger.debug(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to print the error messages of this class.
|
||||
*
|
||||
* @param error the {@link String} to print
|
||||
*/
|
||||
private void logError(String error) {
|
||||
Logger logger = LoggerFactory.getLogger(Sensor.class);
|
||||
logger.error(error);
|
||||
}
|
||||
package support;
|
||||
|
||||
import de.fh_zwickau.informatik.sensor.IZWayApiCallbacks;
|
||||
import de.fh_zwickau.informatik.sensor.model.devicehistory.DeviceHistory;
|
||||
import de.fh_zwickau.informatik.sensor.model.devicehistory.DeviceHistoryList;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.Device;
|
||||
import de.fh_zwickau.informatik.sensor.model.devices.DeviceList;
|
||||
import de.fh_zwickau.informatik.sensor.model.instances.Instance;
|
||||
import de.fh_zwickau.informatik.sensor.model.instances.InstanceList;
|
||||
import de.fh_zwickau.informatik.sensor.model.locations.Location;
|
||||
import de.fh_zwickau.informatik.sensor.model.locations.LocationList;
|
||||
import de.fh_zwickau.informatik.sensor.model.modules.ModuleList;
|
||||
import de.fh_zwickau.informatik.sensor.model.namespaces.NamespaceList;
|
||||
import de.fh_zwickau.informatik.sensor.model.notifications.Notification;
|
||||
import de.fh_zwickau.informatik.sensor.model.notifications.NotificationList;
|
||||
import de.fh_zwickau.informatik.sensor.model.profiles.Profile;
|
||||
import de.fh_zwickau.informatik.sensor.model.profiles.ProfileList;
|
||||
import de.fh_zwickau.informatik.sensor.model.zwaveapi.controller.ZWaveController;
|
||||
import de.fh_zwickau.informatik.sensor.model.zwaveapi.devices.ZWaveDevice;
|
||||
import device.Sensor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Mandatory callback class for the Z-Way Library in use.
|
||||
* Really trivial implementation: log all the responses at debug level.
|
||||
*
|
||||
* @author <a href="mailto:luigi.derussis@uniupo.it">Luigi De Russis</a>
|
||||
* @version 1.0 (24/05/2017)
|
||||
* @see <a href="https://github.com/pathec/ZWay-library-for-Java">Z-Way Library on GitHub</a> for documentation about the used library
|
||||
*/
|
||||
public class ZWaySimpleCallback implements IZWayApiCallbacks {
|
||||
|
||||
@Override
|
||||
public void getStatusResponse(String s) {
|
||||
this.logMessage("Status response: " + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getRestartResponse(Boolean aBoolean) {
|
||||
this.logMessage("Restart response: " + aBoolean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getLoginResponse(String s) {
|
||||
this.logMessage("Login response: " + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getNamespacesResponse(NamespaceList namespaceList) {
|
||||
this.logMessage("Namespaces are: " + namespaceList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getModulesResponse(ModuleList moduleList) {
|
||||
this.logMessage("Modules are: " + moduleList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getInstancesResponse(InstanceList instanceList) {
|
||||
this.logMessage("Instances are: " + instanceList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInstanceResponse(Instance instance) {
|
||||
this.logMessage("Received a POST for the instance: " + instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getInstanceResponse(Instance instance) {
|
||||
this.logMessage("The instance is: " + instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putInstanceResponse(Instance instance) {
|
||||
this.logMessage("Received a PUT for the instance: " + instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteInstanceResponse(boolean b) {
|
||||
this.logMessage("The instance has been deleted? " + String.valueOf(b));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDevicesResponse(DeviceList deviceList) {
|
||||
this.logMessage("Devices are: " + deviceList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putDeviceResponse(Device device) {
|
||||
this.logMessage("Received a PUT for device: " + device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDeviceResponse(Device device) {
|
||||
this.logMessage("The device is: " + device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDeviceCommandResponse(String s) {
|
||||
this.logMessage("The device command is: " + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getLocationsResponse(LocationList locationList) {
|
||||
this.logMessage("Locations are: " + locationList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postLocationResponse(Location location) {
|
||||
this.logMessage("Received a POST for location: " + location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getLocationResponse(Location location) {
|
||||
this.logMessage("The location is: " + location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putLocationResponse(Location location) {
|
||||
this.logMessage("Received a PUT for location: " + location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteLocationResponse(boolean b) {
|
||||
this.logMessage("Location has been deleted? " + b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getProfilesResponse(ProfileList profileList) {
|
||||
this.logMessage("Profiles are: " + profileList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProfileResponse(Profile profile) {
|
||||
this.logMessage("Received a POST for profile: " + profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getProfileResponse(Profile profile) {
|
||||
this.logMessage("The profile is: " + profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putProfileResponse(Profile profile) {
|
||||
this.logMessage("Received a PUT for profile: " + profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteProfileResponse(boolean b) {
|
||||
this.logMessage("Profile has been deleted? " + b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getNotificationsResponse(NotificationList notificationList) {
|
||||
this.logMessage("Notifications are: " + notificationList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getNotificationResponse(Notification notification) {
|
||||
this.logMessage("The notification is: " + notification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putNotificationResponse(Notification notification) {
|
||||
this.logMessage("Received a PUT for notification: " + notification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDeviceHistoriesResponse(DeviceHistoryList deviceHistoryList) {
|
||||
this.logMessage("Device histories are: " + deviceHistoryList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDeviceHistoryResponse(DeviceHistory deviceHistory) {
|
||||
this.logMessage("The device history is: " + deviceHistory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getZWaveDeviceResponse(ZWaveDevice zWaveDevice) {
|
||||
this.logMessage("The Z-Wave device is: " + zWaveDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getZWaveControllerResponse(ZWaveController zWaveController) {
|
||||
this.logMessage("The Z-Wave controller is: " + zWaveController);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apiError(String s, boolean b) {
|
||||
this.logError("API Error: " + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void httpStatusError(int i, String s, boolean b) {
|
||||
this.logError("HTTP Status Error: " + i + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authenticationError() {
|
||||
this.logError("Authentication Error");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void responseFormatError(String s, boolean b) {
|
||||
this.logError("Wrong format: " + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void message(int i, String s) {
|
||||
this.logMessage("You've got a message: " + i + " " + s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to print the log messages of this class.
|
||||
*
|
||||
* @param message the {@link String} to print
|
||||
*/
|
||||
private void logMessage(String message) {
|
||||
Logger logger = LoggerFactory.getLogger(Sensor.class);
|
||||
logger.debug(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to print the error messages of this class.
|
||||
*
|
||||
* @param error the {@link String} to print
|
||||
*/
|
||||
private void logError(String error) {
|
||||
Logger logger = LoggerFactory.getLogger(Sensor.class);
|
||||
logger.error(error);
|
||||
}
|
||||
}
|
||||
28
src/main/java/support/audio/Audio.java
Normal file
28
src/main/java/support/audio/Audio.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package support.audio;
|
||||
|
||||
/**
|
||||
* Classe che serve ad aiutare a far partire la musica
|
||||
*/
|
||||
public interface Audio {
|
||||
|
||||
/**
|
||||
* Fa partire una traccia audio in base al nome di essa.<br>
|
||||
* Se un audio era gia' stato fatto partire esso viene fermato<br>
|
||||
* Il nome puo' variare in base all'implementazione: nome di file o nome da cercare su internet...
|
||||
* @param name la stringa per far partire la canzone
|
||||
*/
|
||||
void play(String name);
|
||||
|
||||
/**
|
||||
* Fa' partire un audio a caso fra quelli selezionati dalla stringa<br>
|
||||
* Se un audio era gia' stato fatto partire esso viene fermato<br>
|
||||
* In base all'implementazione puo' essere un nome che appartiene a vari file,<br>
|
||||
* il nome di una cartella contenente i file, o una stringa di ricerca...
|
||||
*/
|
||||
void playRandom(String name);
|
||||
|
||||
/**
|
||||
* Ferma l'ultimo audio che e' stato fatto partire
|
||||
*/
|
||||
void stop();
|
||||
}
|
||||
138
src/main/java/support/audio/AudioFile.java
Normal file
138
src/main/java/support/audio/AudioFile.java
Normal file
@@ -0,0 +1,138 @@
|
||||
package support.audio;
|
||||
|
||||
import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.Clip;
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Usa i file nella cartella resources/audio/ per riprodurre i suoni
|
||||
*/
|
||||
public class AudioFile implements Audio {
|
||||
|
||||
/**
|
||||
* La path dove si trovano gli audio
|
||||
*/
|
||||
public static final String PATH_AUDIO = "src/main/resources/audio/";
|
||||
|
||||
/**
|
||||
* L'ultimo audio fatto partire
|
||||
*/
|
||||
private final Clip clip;
|
||||
|
||||
/**
|
||||
* Serve per crearsi una mappa di tutte le canzoni
|
||||
*/
|
||||
private final static Map<String, File> files = getAllFiles(PATH_AUDIO);
|
||||
|
||||
/**
|
||||
* Mappa che serve ad avere per ogni sotto-dir di audio una lista di ogni file audio che c'e'
|
||||
*/
|
||||
private final static Map<String, List<File>> dirs = getAllDirs(PATH_AUDIO);
|
||||
|
||||
/**
|
||||
* Crea un oggetto audio che si puo' poi far riprodurre e stoppare
|
||||
*/
|
||||
public AudioFile() {
|
||||
Clip clip = null;
|
||||
try {
|
||||
clip = AudioSystem.getClip();
|
||||
} catch (LineUnavailableException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.clip = clip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilizzando questo costruttore si fa partire in automatico il file scelto
|
||||
* @param file il nome del file scelto da far partire subito
|
||||
*/
|
||||
public AudioFile(String file) {
|
||||
this();
|
||||
this.play(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fa partire una canzone che si trova nella cartella audio o in una delle sue sottocartelle
|
||||
* @param name la stringa per far partire la canzone
|
||||
*/
|
||||
@Override
|
||||
public void play(String name) {
|
||||
stop();
|
||||
try {
|
||||
File file = files.get(name);
|
||||
AudioInputStream audioIn = AudioSystem.getAudioInputStream(file);
|
||||
clip.open(audioIn);
|
||||
clip.start();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fa' partire una canzone a caso nella cartella selezionata
|
||||
* @param name il nome della cartella
|
||||
*/
|
||||
@Override
|
||||
public void playRandom(String name) {
|
||||
List<File> songs = dirs.get(name);
|
||||
play(songs.get((int)(Math.random()*songs.size())).getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
try {
|
||||
clip.stop();
|
||||
clip.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fa in modo di mappare tutti i file in una directory e in tutte le sue sub-dir
|
||||
* @param path la path iniziale
|
||||
* @return una mappa di NomeFile -> File
|
||||
*/
|
||||
private static Map<String, File> getAllFiles(String path) {
|
||||
File folder = new File(path);
|
||||
File[] listOfFiles = folder.listFiles();
|
||||
Map<String, File> map = new HashMap<>();
|
||||
|
||||
for (File file : listOfFiles) {
|
||||
if(file.isFile())
|
||||
map.put(file.getName(), file);
|
||||
else
|
||||
map.putAll(getAllFiles(file.getPath()));
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crea una mappa contenente tutti i file della cartella scelta associati con ogni dir corrente<br>
|
||||
* @param path la path iniziale
|
||||
* @return una mappa di directory con i loro file
|
||||
*/
|
||||
private static Map<String, List<File>> getAllDirs(String path) {
|
||||
File folder = new File(path);
|
||||
File[] listOfFiles = folder.listFiles();
|
||||
List<File> list = new LinkedList<>();
|
||||
Map<String, List<File>> map = new HashMap<>();
|
||||
|
||||
for (File file : listOfFiles) {
|
||||
if(file.isFile())
|
||||
list.add(file);
|
||||
else
|
||||
map.putAll(getAllDirs(file.getPath()));
|
||||
}
|
||||
map.put(folder.getName(), list);
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
179
src/main/java/support/audio/Musich.java
Normal file
179
src/main/java/support/audio/Musich.java
Normal file
@@ -0,0 +1,179 @@
|
||||
package support.audio;
|
||||
|
||||
import chrriis.dj.nativeswing.swtimpl.NativeInterface;
|
||||
import chrriis.dj.nativeswing.swtimpl.components.JWebBrowser;
|
||||
import org.apache.http.annotation.Obsolete;
|
||||
import support.Rest;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Classe che serve a far partire un video di youtube in una frame
|
||||
*/
|
||||
@Deprecated
|
||||
public class Musich implements Audio {
|
||||
|
||||
/**
|
||||
* L'url da dove possiamo pescare i dati di youtube dei video
|
||||
*/
|
||||
private static final String API_URL = "https://www.googleapis.com/youtube/v3/search?";
|
||||
/**
|
||||
* La key necessaria per prendere i dati da youtube<br>
|
||||
*/
|
||||
private static final String KEY = "AIzaSyAYcQcX9P5btBTfgdwWwETNh_7jV20cQp0";
|
||||
|
||||
/**
|
||||
* IL thread che ha fatto partire il frame corrente
|
||||
*/
|
||||
private Thread currentThread;
|
||||
/**
|
||||
* Il frame (ovvero la windows) che e' attualmente attiva
|
||||
*/
|
||||
private JFrame currentFrame;
|
||||
|
||||
/**
|
||||
* Serve ad inizializzare la libreria SWT di eclipse e chrriis.dj
|
||||
*/
|
||||
public Musich() {
|
||||
/*
|
||||
* Viene mandato questo errore in console:
|
||||
* Exception "java.lang.ClassNotFoundException: com/intellij/codeInsight/editorActions/FoldingData"while constructing DataFlavor for: application/x-java-jvm-local-objectref; class=com.intellij.codeInsight.editorActions.FoldingData
|
||||
* Sta' di fatto che per quanto ne so non compromette il funzionamento.
|
||||
*/
|
||||
NativeInterface.initialize();
|
||||
NativeInterface.open();
|
||||
NativeInterface.runEventPump(); // this should be called at the end of main (it said this but who cares)
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve ad avere una lista di id dei video che corrispondono alle keywords indicate nella ricerca
|
||||
* @param search la ricerca da fare su youtube
|
||||
* @param maxResult quanti risultati si vuole avere
|
||||
* @return una lista di id dei video che corrispondono alla ricerca
|
||||
*/
|
||||
public static List<String> getVideosId(String search, final int maxResult) {
|
||||
try {
|
||||
search = URLEncoder.encode(search, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Map<String, ?> response = Rest.get(API_URL +
|
||||
"maxResults=" + maxResult +
|
||||
"&part=snippet" +
|
||||
"&type=video" +
|
||||
"&q=" + search +
|
||||
"&key=" + KEY);
|
||||
|
||||
List<Map<String, ?>> items = (List<Map<String, ?>>)response.get("items");
|
||||
List<String> videosId = new ArrayList<>(maxResult);
|
||||
|
||||
for(Map<String, ?> obj: items) {
|
||||
Map<String, String> id = (Map<String, String>)obj.get("id");
|
||||
videosId.add(id.get("videoId"));
|
||||
}
|
||||
|
||||
return videosId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve ad avere una lista di id dei video che corrispondono alle keywords indicate nella ricerca<br>
|
||||
* La lista conterra' i primi 5 id dei video trovati.
|
||||
* @param search la ricerca da fare su youtube
|
||||
* @return una lista di id dei video che corrispondono alla ricerca
|
||||
*/
|
||||
public List<String> getVideosId(String search) {
|
||||
return Musich.getVideosId(search, 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dato l'id di un video viene fatto partire un frame contenente il video richiesto<br>
|
||||
* Come al solito, youtube potrebbe far partire una pubblicita'.
|
||||
* @param videoId l'id del video da far vedere
|
||||
* @param seconds da quanti secondi bisogna far partire il video
|
||||
*/
|
||||
public void play(final String videoId, int seconds) {
|
||||
this.stop();
|
||||
currentThread = new Thread( () -> {
|
||||
currentFrame = new JFrame("YouTube Viewer");
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JWebBrowser browser = new JWebBrowser();
|
||||
browser.setBarsVisible(false);
|
||||
browser.navigate(getYoutubeLink(videoId, seconds));
|
||||
|
||||
currentFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
currentFrame.getContentPane().add(browser, BorderLayout.CENTER);
|
||||
currentFrame.setSize(800, 600);
|
||||
currentFrame.setLocationByPlatform(true);
|
||||
currentFrame.setVisible(true);
|
||||
});
|
||||
|
||||
// don't forget to properly close native components
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> NativeInterface.close()));
|
||||
}, "NativeInterface");
|
||||
|
||||
currentThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dato l'id di un video viene fatto partire un frame contenente il video richiesto<br>
|
||||
* Come al solito, youtube potrebbe far partire una pubblicita'.
|
||||
* @param videoId l'id del video da far vedere
|
||||
*/
|
||||
public void play(final String videoId) {
|
||||
play(videoId, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve a far partire un video a caso tra quelli che corrispondono alle keywords indicate nella ricerca<br>
|
||||
* Come al solito, youtube potrebbe far partire una pubblicita'.
|
||||
* @param search la ricerca da fare su youtube
|
||||
* @param maxResult fra quanti risultati deve scegliere
|
||||
*/
|
||||
public void playRandom(String search, int maxResult) {
|
||||
this.play(getVideosId(search, maxResult).get((int)(Math.random()*maxResult)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve a far partire un video a caso tra quelli che corrispondono alle keywords indicate nella ricerca<br>
|
||||
* Esso scegliera' fra i primi 5 risultati<br>
|
||||
* Come al solito, youtube potrebbe far partire una pubblicita'.
|
||||
* @param search la ricerca da fare su youtube
|
||||
*/
|
||||
public void playRandom(String search) {
|
||||
this.playRandom(search, 5);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ferma il video che e' in riprduzione in questo momento.<br>
|
||||
* Se non ce ne sono non fa nulla.
|
||||
*/
|
||||
public void stop() {
|
||||
if(currentThread != null) {
|
||||
currentThread.interrupt();
|
||||
currentFrame.dispatchEvent(new WindowEvent(currentFrame, WindowEvent.WINDOW_CLOSING));
|
||||
|
||||
currentThread = null;
|
||||
currentFrame = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ricevi il link di youtube del video a partire dal suo ID
|
||||
* @param videoId l'id del video
|
||||
* @param seconds i secondi dall'inizio del video (0 o negativi e viene ignorato)
|
||||
* @return una stringa
|
||||
*/
|
||||
public static String getYoutubeLink(String videoId, int seconds) {
|
||||
return videoId==null? "":"https://www.youtube.com/watch?v=" + videoId + (seconds>0? "&t="+seconds:"");
|
||||
}
|
||||
}
|
||||
130
src/main/java/support/database/Database.java
Normal file
130
src/main/java/support/database/Database.java
Normal file
@@ -0,0 +1,130 @@
|
||||
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.*;
|
||||
|
||||
/**
|
||||
* Interfaccia per collegarsi al database
|
||||
*/
|
||||
public interface Database {
|
||||
|
||||
/**
|
||||
* Un logger per scrivere a console eventuali errori o informazioni
|
||||
*/
|
||||
Logger LOG = LoggerFactory.getLogger("DB");
|
||||
|
||||
/**
|
||||
* Dice solamente se e' possibile collegarsi al database e se si possono fare delle query
|
||||
* @return vero se si pu' falso in altri casi.
|
||||
*/
|
||||
boolean isReachable();
|
||||
|
||||
/**
|
||||
* Inserisce nuovi dati del paziente nel database.
|
||||
* @param dateMilliSec la data che si vuole inserire in millisec
|
||||
* @param heartRate il battito cardiaco
|
||||
* @return vero se ha inserito o i dati esistevano gia', falso se non ce l'ha fatta
|
||||
*/
|
||||
boolean updateHeart(long dateMilliSec, double heartRate);
|
||||
/**
|
||||
* Inserisce nuovi dati del paziente nel database.
|
||||
* @param dateStartSleep la data che si vuole inserire in millisec
|
||||
* @param duration per quanto e' durato il sonno
|
||||
* @return vero se ha inserito o i dati esistevano gia' falso se non ce l'ha fatta
|
||||
*/
|
||||
boolean updateSleep(long dateStartSleep, long duration);
|
||||
/**
|
||||
* Inserisce nuovi dati del paziente nel database.
|
||||
* @param dateMilliSec la data che si vuole inserire in millisec
|
||||
* @param steps i passi fatti
|
||||
* @return vero se ha inserito o i dati esistevano gia' falso se non ce l'ha fatta
|
||||
*/
|
||||
boolean updateSteps(long dateMilliSec, long steps);
|
||||
|
||||
/**
|
||||
* Riceve i dati del cuore dal giorno selezionato fino ad oggi
|
||||
* @param days quanti giorni devono esser considerati
|
||||
* @return una lista dei battiti cardiaci degli ultimi X giorni (ordinati da oggi al giorno X)
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Prende 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>
|
||||
* @param database il database in cui inserire i dati
|
||||
* @param fitbit la classe che contiene i dati aggiornati
|
||||
* @param retryMinutes ogni quanti minuti deve riprovare ad inviare la richiesta
|
||||
* @return un Thread
|
||||
*/
|
||||
static Thread insertHourlyDataIn(Database database, Fitbit fitbit, int retryMinutes) {
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public synchronized void run() {
|
||||
try {
|
||||
boolean retry;
|
||||
long now = System.currentTimeMillis();
|
||||
double heartRate = fitbit.getHeartRate(60);
|
||||
int steps = fitbit.getSteps(1);
|
||||
do {
|
||||
retry = !database.updateHeart(now, heartRate);
|
||||
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 orario interrotto");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return getThreadStartingEach(runnable, 60, "update-hourly-data");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* @param database il database in cui inserire i dati
|
||||
* @param fitbit la classe che contiene i dati aggiornati
|
||||
* @param retryMinutes ogni quanti minuti deve riprovare ad inviare la richiesta
|
||||
* @return un Thread da far partire
|
||||
*/
|
||||
static Thread insertDailyData(Database database, Fitbit fitbit, int retryMinutes) {
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public synchronized void run() {
|
||||
try {
|
||||
List<Sleep.SleepData> sleepDatas = fitbit.getDetailedSleep();
|
||||
boolean retry = !sleepDatas.isEmpty();
|
||||
do {
|
||||
for (Sleep.SleepData data : sleepDatas)
|
||||
retry = retry && !database.updateSleep(data.start_date, data.duration);
|
||||
|
||||
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 giornaliero interrotto");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return getThreadStartingAt(runnable, 23, "update-daily-data");
|
||||
}
|
||||
}
|
||||
144
src/main/java/support/database/LocalDB.java
Normal file
144
src/main/java/support/database/LocalDB.java
Normal file
@@ -0,0 +1,144 @@
|
||||
package support.database;
|
||||
|
||||
import device.fitbitdata.HeartRate;
|
||||
import device.fitbitdata.Steps;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Classe che utilizza un database sqlite che contiene le seguenti tabelle:<br>
|
||||
* <ul>
|
||||
* <li>'heart' battito + orario</li>
|
||||
* <li>'sleep' inizio + durata</li>
|
||||
* <li>'steps' data + passi.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class LocalDB implements Database {
|
||||
|
||||
/**
|
||||
* Il percorso dove trovare il database, strutturato in: <interfaccia>:<implementazione>:<percorso vero e proprio>
|
||||
*/
|
||||
public static final String DB_LOCATION = "jdbc:sqlite:src/main/resources/";
|
||||
|
||||
/**
|
||||
* Il nome del database (aka il nome del file)
|
||||
*/
|
||||
public static final String DB_NAME = "user_data.db";
|
||||
|
||||
/**
|
||||
* La connessione al database
|
||||
*/
|
||||
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>'heart' battito + orario</li>
|
||||
* <li>'sleep' inizio + durata</li>
|
||||
* <li>'step' data + passi.</li>
|
||||
* </ul>
|
||||
* @throws SQLException se qualcosa e' andato storto
|
||||
*/
|
||||
public LocalDB() throws SQLException {
|
||||
conn = DriverManager.getConnection(DB_LOCATION + DB_NAME);
|
||||
Statement statement = conn.createStatement();
|
||||
statement.execute("CREATE TABLE IF NOT EXISTS heart (day DATE PRIMARY KEY, rate DOUBLE)");
|
||||
statement.execute("CREATE TABLE IF NOT EXISTS sleep (sleep_start DATE PRIMARY KEY, duration INTEGER)");
|
||||
statement.execute("CREATE TABLE IF NOT EXISTS steps (day DATE PRIMARY KEY, steps INTEGER)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReachable() { return conn!=null; }
|
||||
|
||||
@Override
|
||||
public boolean updateHeart(long dateMilliSec, double heartRate) {
|
||||
Timestamp time = new Timestamp(dateMilliSec);
|
||||
return query("IF NOT EXISTS (" +
|
||||
"SELECT * " +
|
||||
"FROM heart " +
|
||||
"WHERE day = '" + time + "') " +
|
||||
"BEGIN INSERT INTO heart (day, rate) VALUES ( ' " + time + " ', '" + heartRate + "') " +
|
||||
"END;");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateSleep(long dateStartSleep, long duration) {
|
||||
Timestamp time = new Timestamp(dateStartSleep);
|
||||
return query("IF NOT EXISTS (" +
|
||||
"SELECT * " +
|
||||
"FROM sleep " +
|
||||
"WHERE sleep_start = '" + time + "') " +
|
||||
"BEGIN INSERT INTO sleep (sleep_start, duration) VALUES ( ' " + time + " ', '" + duration + "') " +
|
||||
"END;");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateSteps(long dateMilliSec, long steps) {
|
||||
Timestamp time = new Timestamp(dateMilliSec);
|
||||
return query("IF NOT EXISTS (" +
|
||||
"SELECT * " +
|
||||
"FROM steps " +
|
||||
"WHERE day = '" + time + "') " +
|
||||
"INSERT INTO steps (day, steps) VALUES ( ' " + time + " ', '" + steps + "') " +
|
||||
"END;");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HeartRate> getHeartDataOfLast(int days) {
|
||||
try {
|
||||
int dayToSubtract = 15;
|
||||
long time = System.currentTimeMillis() - (dayToSubtract * 24 * 60 * 1000); // meno 24 ore per 60 secondi per 100 millisec
|
||||
|
||||
ResultSet result = conn.createStatement().executeQuery("SELECT * FROM heart WHERE day>='" + new Timestamp(time) + "'");
|
||||
List<HeartRate> list = new LinkedList<>();
|
||||
|
||||
while(result.next()) {
|
||||
HeartRate rate = new HeartRate();
|
||||
rate.setAverage(result.getDouble("rate"));
|
||||
rate.setDate(result.getDate("day").getTime());
|
||||
|
||||
list.add(rate);
|
||||
}
|
||||
return list;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
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) {
|
||||
try {
|
||||
conn.createStatement().execute(sql);
|
||||
return true;
|
||||
} catch (SQLException e) {
|
||||
LOG.error(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
143
src/main/java/support/database/RemoteDB.java
Normal file
143
src/main/java/support/database/RemoteDB.java
Normal file
@@ -0,0 +1,143 @@
|
||||
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;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Classe che si connette al server del progetto di C#
|
||||
*/
|
||||
public class RemoteDB implements Database {
|
||||
|
||||
/**
|
||||
* Serve per mandare i messaggi, convertendo le classi in Json
|
||||
*/
|
||||
private static final Gson GSON = GsonFactory.getDefaultFactory().getGson();
|
||||
|
||||
private static final DateFormat STD_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH");
|
||||
|
||||
/**
|
||||
* L'url base che verra' usato per inviare/ricevere i dati
|
||||
*/
|
||||
public final String base_url;
|
||||
/**
|
||||
* L'username dell'utente
|
||||
*/
|
||||
public final String username;
|
||||
|
||||
/**
|
||||
* Inseredo lo username e basta l'indirizzo a cui tentera' la connesione e' <a href="http://127.0.0.1:5000/api/">http://127.0.0.1:5000/api/</a>
|
||||
* @param username il nome utente assiociato per aggiornare i dati
|
||||
*/
|
||||
public RemoteDB(String username) {
|
||||
this(username, "http://127.0.0.1:5000/api/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Costruttore che ha bisogno sia dello username che dell'url per la connesione
|
||||
* @param username il nome utente assiociato per aggiornare i dati
|
||||
* @param base_url l'url a cui si punta per aggiornare/leggere i dati
|
||||
*/
|
||||
public RemoteDB(String username, String base_url) {
|
||||
this.username = username;
|
||||
this.base_url = base_url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReachable() {
|
||||
return !Rest.get(base_url+"user/"+username).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateHeart(long dateMilliSec, double heartRate) {
|
||||
return sendData("heartbeat", dateMilliSec, heartRate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateSleep(long dateStartSleep, long duration) {
|
||||
return sendData("sleep", dateStartSleep, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateSteps(long dateMilliSec, long steps) {
|
||||
return sendData("step", dateMilliSec, steps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HeartRate> getHeartDataOfLast(int days) {
|
||||
try {
|
||||
String url = base_url+"heartbeat/"+username+"/last/"+(days*24);
|
||||
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());
|
||||
for(Map<String, Object> data: map.get("list")) {
|
||||
HeartRate heart = new HeartRate();
|
||||
heart.setAverage((double)data.get("value"));
|
||||
heart.setDate(STD_FORMAT.parse((String)data.get("time")).getTime());
|
||||
|
||||
list.add(heart);
|
||||
}
|
||||
return list;
|
||||
} catch (ParseException e) {
|
||||
LOG.error(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
* @param type il tipo di dato che deve esser aggiornato
|
||||
* @param date la data da inserire in millisecondi
|
||||
* @param value l'oggetto da inviare
|
||||
* @return vero se dopo la PUT si e' riusciti ad inserire il valore
|
||||
*/
|
||||
private boolean sendData(String type, long date, Object value) {
|
||||
String url = base_url+type+"/";
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("username", username);
|
||||
map.put("time", new Timestamp(date));
|
||||
map.put("value", value);
|
||||
|
||||
Rest.put(url, GSON.toJson(map), "application/json");
|
||||
DateFormat format = new SimpleDateFormat("yyyy-MM-dd/HH");
|
||||
|
||||
url = url+username+"/"+format.format(new Date(date));
|
||||
List<Map<String, Object>> list = (List<Map<String, Object>>) Rest.get(url).get("list");
|
||||
for(Map<String, Object> obj: list)
|
||||
try {
|
||||
if (STD_FORMAT.parse((String) obj.get("time")).getTime() == date)
|
||||
return true;
|
||||
} catch (Exception e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
BIN
src/main/resources/Relazione progetto SENIORASSISTANT.pdf
Normal file
BIN
src/main/resources/Relazione progetto SENIORASSISTANT.pdf
Normal file
Binary file not shown.
BIN
src/main/resources/audio/random/Godzilla.wav
Normal file
BIN
src/main/resources/audio/random/Godzilla.wav
Normal file
Binary file not shown.
BIN
src/main/resources/audio/random/LeeroyJenkins.wav
Normal file
BIN
src/main/resources/audio/random/LeeroyJenkins.wav
Normal file
Binary file not shown.
BIN
src/main/resources/audio/random/Tullio.wav
Normal file
BIN
src/main/resources/audio/random/Tullio.wav
Normal file
Binary file not shown.
BIN
src/main/resources/audio/relax/Marconi Union - Weightless.wav
Normal file
BIN
src/main/resources/audio/relax/Marconi Union - Weightless.wav
Normal file
Binary file not shown.
Binary file not shown.
BIN
src/main/resources/audio/relax/Slow down - Paul Collier.wav
Normal file
BIN
src/main/resources/audio/relax/Slow down - Paul Collier.wav
Normal file
Binary file not shown.
BIN
src/main/resources/audio/relax/the xx - Intro.wav
Normal file
BIN
src/main/resources/audio/relax/the xx - Intro.wav
Normal file
Binary file not shown.
BIN
src/main/resources/audio/warnings/molti battiti.wav
Normal file
BIN
src/main/resources/audio/warnings/molti battiti.wav
Normal file
Binary file not shown.
BIN
src/main/resources/audio/warnings/molti passi.wav
Normal file
BIN
src/main/resources/audio/warnings/molti passi.wav
Normal file
Binary file not shown.
BIN
src/main/resources/audio/warnings/pochi battiti.wav
Normal file
BIN
src/main/resources/audio/warnings/pochi battiti.wav
Normal file
Binary file not shown.
BIN
src/main/resources/audio/warnings/pochi passi.wav
Normal file
BIN
src/main/resources/audio/warnings/pochi passi.wav
Normal file
Binary file not shown.
2
src/main/resources/simplelogger.properties
Normal file
2
src/main/resources/simplelogger.properties
Normal file
@@ -0,0 +1,2 @@
|
||||
org.slf4j.simpleLogger.defaultLogLevel=info
|
||||
org.slf4j.simpleLogger.logFile=System.out
|
||||
BIN
src/main/resources/user_data.db
Normal file
BIN
src/main/resources/user_data.db
Normal file
Binary file not shown.
@@ -1,43 +0,0 @@
|
||||
import device.Hue;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class TestLights {
|
||||
|
||||
@Test
|
||||
synchronized public void firstTestLights() throws InterruptedException {
|
||||
Hue lights = new Hue();
|
||||
|
||||
Set<String> toRemove = new HashSet<>();
|
||||
for(String str: lights.getNameLights())
|
||||
if(!str.equals("4"))
|
||||
toRemove.add(str);
|
||||
lights.removeLights(toRemove);
|
||||
|
||||
for(int i=0; i<10; i++) {
|
||||
lights.turnOn();
|
||||
this.wait(0b11001000); // 200
|
||||
lights.turnOff();
|
||||
this.wait(0b11001000); // 200
|
||||
}
|
||||
|
||||
lights.turnOn();
|
||||
for(int i=256; i>=0; i--) {
|
||||
lights.setBrightness(i);
|
||||
this.wait(25);
|
||||
}
|
||||
|
||||
for(int i=0; i<256; i++) {
|
||||
lights.setBrightness(i);
|
||||
this.wait(25);
|
||||
}
|
||||
|
||||
lights.setBrightness(150);
|
||||
lights.colorLoop();
|
||||
this.wait(20000); // 10 sec
|
||||
lights.turnOff();
|
||||
}
|
||||
|
||||
}
|
||||
18
src/test/java/test/TestDialogFlow.java
Normal file
18
src/test/java/test/TestDialogFlow.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package test;
|
||||
|
||||
import device.DialogFlowWebHook;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
public class TestDialogFlow {
|
||||
|
||||
@Test
|
||||
public void test01() {
|
||||
DialogFlowWebHook webHook = new DialogFlowWebHook();
|
||||
|
||||
webHook.addOnAction("LightsON", (param) -> {return "Luci accese";});
|
||||
webHook.addOnAction("LightsOFF", (param) -> {return "Luci spente";});
|
||||
|
||||
webHook.startServer();
|
||||
}
|
||||
}
|
||||
27
src/test/java/test/TestFitbit.java
Normal file
27
src/test/java/test/TestFitbit.java
Normal file
@@ -0,0 +1,27 @@
|
||||
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();
|
||||
fitBit.getHoursSleep();
|
||||
|
||||
System.out.println("Today's average heart-rate: "+fitBit.getHeartRate());
|
||||
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));
|
||||
}
|
||||
}
|
||||
77
src/test/java/test/TestLights.java
Normal file
77
src/test/java/test/TestLights.java
Normal file
@@ -0,0 +1,77 @@
|
||||
package test;
|
||||
|
||||
import device.Hue;
|
||||
import main.VariousThreads;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestLights {
|
||||
|
||||
private static final int TIMEOUT = 200;
|
||||
private static final int MAX = 100;
|
||||
private Hue lights;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
lights = new Hue();
|
||||
lights.turnOn();
|
||||
lights.setBrightness(100);
|
||||
lights.changeColor("bianco");
|
||||
}
|
||||
|
||||
@Test
|
||||
synchronized public void firstTestLights() throws InterruptedException {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
lights.turnOn();
|
||||
this.wait(TIMEOUT);
|
||||
lights.turnOff();
|
||||
this.wait(TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
synchronized public void testBrightness() throws InterruptedException {
|
||||
for (int i = MAX; i > 0; i -= 10) {
|
||||
lights.setBrightness(i);
|
||||
this.wait(TIMEOUT);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX; i += 10) {
|
||||
lights.setBrightness(i);
|
||||
this.wait(TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
synchronized public void testColorLoop() throws InterruptedException {
|
||||
lights.colorLoop();
|
||||
this.wait(TIMEOUT * 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
synchronized public void testColor() throws InterruptedException {
|
||||
String[] colors = {"rosso", "giallo", "verde", "blu", "bianco", "azzurro", "arancio"};
|
||||
|
||||
for (String color : colors) {
|
||||
lights.changeColor(color);
|
||||
this.wait(TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
synchronized public void testAutoBright() throws InterruptedException {
|
||||
lights.setBrightness(MAX);
|
||||
|
||||
for(int hour=0; hour<24; hour++)
|
||||
for(int minutes=0; minutes<60; minutes++) {
|
||||
final double hueBrightnes = lights.getCurrentBrightness();
|
||||
final double brightFactor = VariousThreads.calculateBrightFactor(hour, minutes, 100, 20);
|
||||
final double bright = brightFactor*100;
|
||||
|
||||
System.out.printf("%2d:%02d: %+.3f {bri:%3.0f -> add:%+4.0f}\n", hour, minutes, brightFactor, hueBrightnes, bright);
|
||||
lights.addBrightness(bright);
|
||||
wait(TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
75
src/test/java/test/TestMusich.java
Normal file
75
src/test/java/test/TestMusich.java
Normal file
@@ -0,0 +1,75 @@
|
||||
package test;
|
||||
|
||||
import org.junit.Test;
|
||||
import support.audio.AudioFile;
|
||||
import support.audio.Musich;
|
||||
|
||||
public class TestMusich {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
Musich musich = new Musich();
|
||||
musich.playRandom("fairy tail motivational soundtrack", 10);
|
||||
waitAndPrint(20);
|
||||
musich.play("X9di06iCmuw", 114);
|
||||
waitAndPrint(60);
|
||||
musich.stop();
|
||||
waitAndPrint(10);
|
||||
musich.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2() {
|
||||
AudioFile audio = new AudioFile();
|
||||
audio.play("Godzilla.wav");
|
||||
waitAndPrint(3);
|
||||
audio.play("Tullio.wav");
|
||||
waitAndPrint(10);
|
||||
audio.stop();
|
||||
waitAndPrint(2);
|
||||
audio.play("LeeroyJenkins.wav");
|
||||
waitAndPrint(5);
|
||||
audio.playRandom("random");
|
||||
waitAndPrint(10);
|
||||
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();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test4() {
|
||||
AudioFile audio = new AudioFile();
|
||||
|
||||
new AudioFile("pochi passi.wav");
|
||||
waitAndPrint(3);
|
||||
audio.play("molti battiti.wav");
|
||||
waitAndPrint(3);
|
||||
audio.stop();
|
||||
}
|
||||
|
||||
|
||||
public void waitAndPrint(Integer seconds) {
|
||||
if(seconds != null) synchronized (seconds) {
|
||||
try {
|
||||
for(int i=seconds; i>0; i--) {
|
||||
System.out.println("Tempo rimanente: " + i);
|
||||
seconds.wait(1000); // 1 sec
|
||||
}
|
||||
System.out.println("Finito");
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("INTERRUPTED " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/test/java/test/TestRemoteDB.java
Normal file
24
src/test/java/test/TestRemoteDB.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package test;
|
||||
|
||||
import org.junit.Test;
|
||||
import support.database.Database;
|
||||
import support.database.RemoteDB;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TestRemoteDB {
|
||||
|
||||
private static final String REMOTE_URL = "http://127.0.0.1:5000/api/";
|
||||
private static final String USERNAME = "vecchio1";
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
Database database = new RemoteDB(USERNAME, REMOTE_URL);
|
||||
assertTrue(database.isReachable());
|
||||
|
||||
//assertTrue(database.updateHeart(System.currentTimeMillis(), Math.random()*70 + 50));
|
||||
//assertTrue(database.updateSleep(System.currentTimeMillis(), (long) (Math.random()*7200000) + 0));
|
||||
//assertTrue(database.updateSteps(System.currentTimeMillis(), (long) (Math.random()*100) + 100));
|
||||
//database.getHeartDataOfLast(10);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
package test;
|
||||
|
||||
import device.Hue;
|
||||
import device.Sensor;
|
||||
import org.junit.Test;
|
||||
@@ -6,17 +8,16 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class TestSensor {
|
||||
Sensor sensor = new Sensor(2);
|
||||
Hue lights;
|
||||
private Sensor sensor = new Sensor(2);
|
||||
private Hue lights;
|
||||
|
||||
@Test
|
||||
synchronized public void firstTestSensor() throws InterruptedException {
|
||||
sensor.update(2);
|
||||
System.out.println(sensor.luminiscenceLevel());
|
||||
synchronized public void firstTestSensor() {
|
||||
System.out.println(sensor.getBrightnessLevel());
|
||||
}
|
||||
|
||||
@Test
|
||||
synchronized public void secondTestSensor() throws InterruptedException {
|
||||
synchronized public void secondTestSensor() {
|
||||
int i=0;
|
||||
lights = new Hue();
|
||||
Set<String> toRemove = new HashSet<>();
|
||||
@@ -28,16 +29,15 @@ import java.util.Set;
|
||||
lights.turnOn();
|
||||
|
||||
while(i<999999) {
|
||||
if (sensor.luminiscenceLevel() < 200) {
|
||||
lights.getCurrentLuminiscence();
|
||||
if (sensor.getBrightnessLevel() < 200) {
|
||||
lights.getCurrentBrightness();
|
||||
lights.setBrightness(200);
|
||||
}
|
||||
|
||||
if (sensor.luminiscenceLevel() > 600) {
|
||||
if (sensor.getBrightnessLevel() > 600) {
|
||||
lights.setBrightness(0);
|
||||
}
|
||||
System.out.println(i+"-"+sensor.luminiscenceLevel());
|
||||
sensor.update(100);
|
||||
System.out.println(i+"-"+sensor.getBrightnessLevel());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user