├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── pom.xml └── src └── main ├── appengine ├── META-INF │ └── MANIFEST.MF └── WEB-INF │ ├── appengine-web.xml │ └── web.xml ├── java └── com │ └── google │ └── homegraph │ └── dashboard │ ├── DashboardApplication.java │ ├── ServletInitializer.java │ ├── controller │ ├── HomeGraphController.java │ └── UploadController.java │ ├── model │ ├── Device.java │ └── Response.java │ └── service │ └── StorageService.java ├── ngapp ├── .angular-cli.json ├── e2e │ ├── app.e2e-spec.ts │ ├── app.po.ts │ └── tsconfig.e2e.json ├── karma.conf.js ├── package.json ├── protractor.conf.js ├── src │ ├── app │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── device-list │ │ │ ├── device-list.component.css │ │ │ ├── device-list.component.html │ │ │ ├── device-list.component.spec.ts │ │ │ └── device-list.component.ts │ │ ├── device.icon.ts │ │ ├── home │ │ │ ├── home.component.css │ │ │ ├── home.component.html │ │ │ ├── home.component.spec.ts │ │ │ └── home.component.ts │ │ └── shared │ │ │ ├── common-functions.util.ts │ │ │ ├── device │ │ │ ├── device.service.spec.ts │ │ │ └── device.service.ts │ │ │ ├── giphy │ │ │ └── giphy.service.ts │ │ │ └── upload │ │ │ ├── upload-file.service.spec.ts │ │ │ └── upload-file.service.ts │ ├── assets │ │ └── .gitkeep │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.css │ ├── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── typings.d.ts ├── tsconfig.json └── tslint.json └── resources └── application.properties /.gitignore: -------------------------------------------------------------------------------- 1 | # Deployment files 2 | /target 3 | # Compiled files 4 | /src/main/webapp 5 | src/main/ngapp/node/ 6 | # Pulled-in dependencies 7 | /src/main/ngapp/node_modules 8 | /src/main/ngapp/package-lock.json 9 | .gradle/ 10 | .idea/ 11 | .settings/ -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | ## Contributor License Agreements 4 | 5 | We'd love to accept your sample apps and patches! Before we can take them, we 6 | have to jump a couple of legal hurdles. 7 | 8 | Please fill out either the individual or corporate Contributor License Agreement 9 | (CLA). 10 | 11 | * If you are an individual writing original source code and you're sure you 12 | own the intellectual property, then you'll need to sign an [individual CLA](https://developers.google.com/open-source/cla/individual). 13 | * If you work for a company that wants to allow you to contribute your work, 14 | then you'll need to sign a [corporate CLA](https://developers.google.com/open-source/cla/corporate). 15 | 16 | Follow either of the two links above to access the appropriate CLA and 17 | instructions for how to sign and return it. Once we receive it, we'll be able to 18 | accept your pull requests. 19 | 20 | ## Contributing A Patch 21 | 22 | 1. Submit an issue describing your proposed change to the repo in question. 23 | 1. The repo owner will respond to your issue promptly. 24 | 1. If your proposed change is accepted, and you haven't already done so, sign a 25 | Contributor License Agreement (see details above). 26 | 1. Fork the desired repo, develop and test your code changes. 27 | 1. Ensure that your code adheres to the existing style in the sample to which 28 | you are contributing. Refer to the 29 | [Google Cloud Platform Samples Style Guide](https://github.com/GoogleCloudPlatform/Template/wiki/style.html) for the 30 | recommended coding standards for this organization. 31 | 1. Ensure that your code has an appropriate set of unit tests which all pass. 32 | 1. Submit a pull request. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright [yyyy] [name of copyright owner] 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Report State Dashboard 2 | This is a dashboard that developers can use to help them debug their smart home actions. 3 | It will obtain data from the HomeGraph and present it to you, allowing you to verify the 4 | data that is stored in it. 5 | 6 | The app can be installed to Google App Engine or any Java Web Server. 7 | 8 | Make sure to follow [this guide](https://developers.google.com/assistant/smarthome/develop/report-state) to get a service account key 9 | 10 | You can add a Giphy [API key](https://developers.giphy.com/docs/) to `giphy.service.ts` to get animated images for each device type. Otherwise, a basic icon will show shown. 11 | 12 | ## Setup 13 | 14 | 1. Run `mvn compile`. This will compile both the Java project and the web 15 | frontend. 16 | 17 | ### Build client (separately) 18 | 19 | First you need to build the web frontend that you will interact with: 20 | 21 | ``` 22 | cd src/main/ngapp 23 | npm install 24 | npm run build 25 | # Built files will be added in src/main/webapp 26 | ``` 27 | 28 | ### Build and deploy server 29 | 30 | #### Using Google App Engine 31 | To set up your instance: 32 | 33 | 1. Use the Google Cloud Console to create a [new App Engine application](https://console.cloud.google.com/projectselector/appengine/create?lang=java&st=true&_ga=2.118087439.-783977692.1527806171) 34 | 1. When prompted, select the region where you want your App Engine application located. 35 | 1. Download and install the [Google Cloud SDK](https://cloud.google.com/sdk/docs/) if you haven't already 36 | 1. Download and install [Maven](https://cloud.google.com/appengine/docs/standard/java/tools/using-maven) if you haven't already 37 | 1. Configure the gcloud command-line environment: 38 | 39 | ```bash 40 | gcloud init 41 | gcloud auth application-default login 42 | ``` 43 | 44 | 1. Install the App Engine component 45 | 46 | ``` 47 | gcloud components install app-engine-java 48 | ``` 49 | 50 | 1. Verify that you are using the latest version of the Google Cloud SDK 51 | 52 | ``` 53 | gcloud components update 54 | ``` 55 | 56 | 1. Deploy the sample with `mvn appengine:deploy` 57 | 1. It will be available at `http://.appspot.com` 58 | 1. You can stream logs from the command line by running: `gcloud app logs tail -s default` 59 | 60 | **Note**: You may have connectivity issues if you try to run the sample locally 61 | 62 | Read the documentation on [App Engine](https://cloud.google.com/appengine/docs/standard/java/quickstart) to learn more. 63 | 64 | #### Other Java Web Servers 65 | To install on any other Java Web Server: 66 | 67 | `mvn clean package` 68 | 69 | Deploy the war artifact from `target/` 70 | 71 | To run locally: 72 | 73 | `mvn clean package spring-boot:repackage` 74 | 75 | Then run `java -jar target/[artifact_name].war` 76 | 77 | ## References & Issues 78 | + Questions? Go to [StackOverflow](https://stackoverflow.com/questions/tagged/actions-on-google), [Assistant Developer Community on Reddit](https://www.reddit.com/r/GoogleAssistantDev/) or [Support](https://developers.google.com/assistant/support). 79 | + For bugs, please report an issue on Github. 80 | + Actions on Google [Documentation](https://developers.google.com/assistant) 81 | 82 | ## Make Contributions 83 | Please read and follow the steps in the [CONTRIBUTING.md](CONTRIBUTING.md). 84 | 85 | ## License 86 | See [LICENSE](LICENSE). 87 | 88 | ## Terms 89 | Your use of this sample is subject to, and by using or downloading the sample files you agree to comply with, the [Google APIs Terms of Service](https://developers.google.com/terms/). 90 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/actions-on-google/smart-home-dashboard/247cc4633260013ba1317a2dbaa76cd14b001eed/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | war 8 | 0.1.0-SNAPSHOT 9 | 10 | com.google.homegraph 11 | dashboard 12 | 13 | 14 | 15 | 1.3.2 16 | 1.9.62 17 | 1.0.0 18 | UTF-8 19 | UTF-8 20 | 1.5.10.RELEASE 21 | 1.8 22 | 1.8 23 | true 24 | 25 | 26 | 27 | 3.3.9 28 | 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-web 34 | ${spring.boot.version} 35 | 36 | 37 | 38 | 39 | io.grpc 40 | grpc-netty 41 | 1.9.0 42 | 43 | 44 | io.grpc 45 | grpc-protobuf 46 | 1.9.0 47 | 48 | 49 | io.grpc 50 | grpc-stub 51 | 1.9.0 52 | 53 | 54 | io.grpc 55 | grpc-auth 56 | 1.9.0 57 | 58 | 59 | 60 | com.google.apis 61 | google-api-services-homegraph 62 | v1-rev20191106-1.30.3 63 | 64 | 65 | 66 | 67 | io.netty 68 | netty-tcnative-boringssl-static 69 | 2.0.7.Final 70 | 71 | 72 | 73 | io.netty 74 | netty-handler 75 | 4.1.45.Final 76 | 77 | 78 | 79 | 80 | org.slf4j 81 | jul-to-slf4j 82 | provided 83 | 1.7.25 84 | 85 | 86 | 87 | com.google.api-client 88 | google-api-client 89 | 1.23.0 90 | 91 | 92 | com.google.guava 93 | guava-jdk5 94 | 95 | 96 | 97 | 98 | 99 | 100 | org.springframework.boot 101 | spring-boot-starter-test 102 | ${spring.boot.version} 103 | test 104 | 105 | 106 | 107 | 108 | io.jsonwebtoken 109 | jjwt 110 | 0.6.0 111 | 112 | 113 | 114 | com.google.auth 115 | google-auth-library-oauth2-http 116 | 0.9.0 117 | 118 | 119 | 120 | 121 | com.googlecode.objectify 122 | objectify 123 | 5.1.22 124 | 125 | 126 | 127 | com.google.appengine 128 | appengine-endpoints 129 | 1.9.28 130 | 131 | 132 | 133 | 134 | javax.servlet 135 | javax.servlet-api 136 | 3.1.0 137 | provided 138 | 139 | 140 | javax.servlet.jsp 141 | javax.servlet.jsp-api 142 | 2.3.1 143 | provided 144 | 145 | 146 | com.google.appengine 147 | appengine-api-1.0-sdk 148 | 1.9.62 149 | compile 150 | 151 | 152 | jstl 153 | jstl 154 | 1.2 155 | 156 | 157 | 158 | 159 | junit 160 | junit 161 | 4.12 162 | test 163 | 164 | 165 | 166 | org.apache.oltu.oauth2 167 | org.apache.oltu.oauth2.common 168 | ${oltu.version} 169 | 170 | 171 | 172 | org.apache.oltu.oauth2 173 | org.apache.oltu.oauth2.client 174 | ${oltu.version} 175 | 176 | 177 | 178 | org.apache.oltu.oauth2 179 | org.apache.oltu.oauth2.authzserver 180 | ${oltu.version} 181 | 182 | 183 | 184 | org.apache.oltu.oauth2 185 | org.apache.oltu.oauth2.httpclient4 186 | ${oltu.version} 187 | 188 | 189 | 190 | org.apache.oltu.oauth2 191 | org.apache.oltu.oauth2.resourceserver 192 | ${oltu.version} 193 | 194 | 195 | 196 | org.apache.oltu.oauth2 197 | org.apache.oltu.oauth2.dynamicreg.client 198 | ${oltu.version} 199 | 200 | 201 | 202 | org.apache.oltu.oauth2 203 | org.apache.oltu.oauth2.dynamicreg.server 204 | ${oltu.version} 205 | 206 | 207 | 208 | org.projectlombok 209 | lombok 210 | true 211 | 1.18.2 212 | 213 | 214 | 215 | 216 | 217 | 218 | org.springframework.boot 219 | spring-boot-dependencies 220 | ${spring.boot.version} 221 | pom 222 | import 223 | 224 | 225 | 226 | 227 | 228 | 229 | ${project.build.directory}/${project.build.finalName}/WEB-INF/classes 230 | 231 | 232 | org.springframework.boot 233 | spring-boot-maven-plugin 234 | ${spring.boot.version} 235 | 236 | 237 | org.codehaus.mojo 238 | versions-maven-plugin 239 | 2.3 240 | 241 | 242 | compile 243 | 244 | display-dependency-updates 245 | display-plugin-updates 246 | 247 | 248 | 249 | 250 | 251 | javax.servlet:javax.servlet-api 252 | com.google.guava:guava 253 | 254 | 255 | 256 | 257 | 258 | com.google.cloud.tools 259 | appengine-maven-plugin 260 | ${appengine.maven.plugin.version} 261 | 262 | 263 | 264 | org.xolstice.maven.plugins 265 | protobuf-maven-plugin 266 | 0.5.0 267 | 268 | com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier} 269 | grpc-java 270 | io.grpc:protoc-gen-grpc-java:1.10.0:exe:${os.detected.classifier} 271 | 272 | 273 | 274 | 275 | compile 276 | compile-custom 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | org.apache.maven.plugins 285 | maven-compiler-plugin 286 | 3.3 287 | 288 | 289 | ngapp/** 290 | 291 | 292 | 293 | 294 | org.apache.maven.plugins 295 | maven-war-plugin 296 | 3.1.0 297 | 298 | 299 | ngapp/** 300 | 301 | 302 | 303 | 304 | org.codehaus.mojo 305 | exec-maven-plugin 306 | 1.5.0 307 | 308 | 309 | com.github.eirslett 310 | frontend-maven-plugin 311 | 313 | 1.8.0 314 | 315 | v10.16.3 316 | src/main/ngapp 317 | 318 | 319 | 320 | install node and npm 321 | 322 | install-node-and-npm 323 | 324 | 325 | 326 | NPM install 327 | 328 | npm 329 | 330 | 331 | install 332 | 333 | 334 | 335 | NPM build 336 | 337 | npm 338 | 339 | 340 | run build 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | kr.motd.maven 349 | os-maven-plugin 350 | 1.5.0.Final 351 | 352 | 353 | 354 | 355 | 356 | 357 | -------------------------------------------------------------------------------- /src/main/appengine/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /src/main/appengine/WEB-INF/appengine-web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | true 6 | java8 7 | 8 | -------------------------------------------------------------------------------- /src/main/appengine/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | index.html 8 | index.jsp 9 | 10 | -------------------------------------------------------------------------------- /src/main/java/com/google/homegraph/dashboard/DashboardApplication.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.google.homegraph.dashboard; 17 | 18 | import java.util.Collections; 19 | 20 | import org.springframework.boot.SpringApplication; 21 | import org.springframework.boot.autoconfigure.SpringBootApplication; 22 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 23 | import org.springframework.context.annotation.Bean; 24 | import org.springframework.core.Ordered; 25 | import org.springframework.web.cors.CorsConfiguration; 26 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 27 | import org.springframework.web.filter.CorsFilter; 28 | 29 | @SpringBootApplication 30 | public class DashboardApplication { 31 | 32 | public static void main(String[] args) { 33 | SpringApplication.run(DashboardApplication.class, args); 34 | } 35 | 36 | @Bean 37 | public FilterRegistrationBean simpleCorsFilter() { 38 | UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 39 | CorsConfiguration config = new CorsConfiguration(); 40 | config.setAllowCredentials(true); 41 | config.setAllowedOrigins(Collections.singletonList("http://localhost:4200")); 42 | config.setAllowedMethods(Collections.singletonList("*")); 43 | config.setAllowedHeaders(Collections.singletonList("*")); 44 | source.registerCorsConfiguration("/**", config); 45 | FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); 46 | bean.setOrder(Ordered.HIGHEST_PRECEDENCE); 47 | return bean; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/google/homegraph/dashboard/ServletInitializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.google.homegraph.dashboard; 17 | 18 | import org.springframework.boot.builder.SpringApplicationBuilder; 19 | import org.springframework.boot.web.support.SpringBootServletInitializer; 20 | 21 | public class ServletInitializer extends SpringBootServletInitializer { 22 | 23 | @Override 24 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 25 | return application.sources(DashboardApplication.class); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/google/homegraph/dashboard/controller/HomeGraphController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.google.homegraph.dashboard.controller; 17 | 18 | import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; 19 | import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; 20 | import com.google.api.client.http.HttpRequestInitializer; 21 | import com.google.api.client.http.HttpTransport; 22 | import com.google.api.client.json.JsonFactory; 23 | import com.google.api.client.json.jackson2.JacksonFactory; 24 | import com.google.api.services.homegraph.v1.HomeGraphService; 25 | import com.google.api.services.homegraph.v1.HomeGraphServiceRequestInitializer; 26 | import com.google.api.services.homegraph.v1.model.AgentDeviceId; 27 | import com.google.api.services.homegraph.v1.model.QueryRequest; 28 | import com.google.api.services.homegraph.v1.model.QueryRequestInput; 29 | import com.google.api.services.homegraph.v1.model.QueryRequestPayload; 30 | import com.google.api.services.homegraph.v1.model.QueryResponse; 31 | import com.google.api.services.homegraph.v1.model.SyncRequest; 32 | import com.google.api.services.homegraph.v1.model.SyncResponse; 33 | import com.google.homegraph.dashboard.model.Device; 34 | import com.google.homegraph.dashboard.model.Response; 35 | import com.google.homegraph.dashboard.service.StorageService; 36 | import java.io.IOException; 37 | import java.io.InputStream; 38 | import java.io.Serializable; 39 | import java.security.GeneralSecurityException; 40 | import java.util.ArrayList; 41 | import java.util.Collections; 42 | import java.util.HashMap; 43 | import java.util.List; 44 | import java.util.Map; 45 | import java.util.UUID; 46 | import org.json.JSONObject; 47 | import org.slf4j.Logger; 48 | import org.slf4j.LoggerFactory; 49 | import org.springframework.beans.factory.annotation.Autowired; 50 | import org.springframework.context.annotation.Scope; 51 | import org.springframework.web.bind.annotation.CrossOrigin; 52 | import org.springframework.web.bind.annotation.GetMapping; 53 | import org.springframework.web.bind.annotation.RequestParam; 54 | import org.springframework.web.bind.annotation.RestController; 55 | 56 | @RestController 57 | @Scope(value = "session") 58 | public class HomeGraphController implements Serializable { 59 | 60 | private static final long serialVersionUID = -7587398671809190089L; 61 | private static final String HOMEGRAPH_SCOPE = "https://www.googleapis.com/auth/homegraph"; 62 | 63 | private static Logger log = LoggerFactory.getLogger("HomeGraphController"); 64 | 65 | private String file; 66 | 67 | private static Map> deviceCache = new HashMap<>(); 68 | 69 | @GetMapping("/homegraph") 70 | @CrossOrigin(origins = "http://localhost:4200") // for testing && development, should be safe to remove 71 | public List getHomegraph(@RequestParam(value = "file") final String file, 72 | @RequestParam(value = "agentUserId") final String agentUserId) { 73 | this.file = file; 74 | 75 | try { 76 | HomeGraphService homeGraphService = createConnection(); 77 | /* 78 | * Get device list 79 | */ 80 | log.debug("Calling SYNC for " + agentUserId); 81 | syncHomeGraph(agentUserId, homeGraphService); 82 | /* 83 | * Iterate devices and request states 84 | */ 85 | log.debug("Calling QUERY for " + agentUserId); 86 | return queryHomeGraph(agentUserId, homeGraphService).getDevices(); 87 | } catch (IOException | GeneralSecurityException e) { 88 | throw new RuntimeException("Cannot connect to Home Graph service: " + e.getMessage(), e); 89 | } 90 | } 91 | 92 | private HomeGraphService createConnection() throws IOException, GeneralSecurityException { 93 | InputStream stream = StorageService.getInstance().loadFile(file); 94 | GoogleCredential creds = GoogleCredential.fromStream(stream) 95 | .createScoped(Collections.singletonList(HOMEGRAPH_SCOPE)); 96 | HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport(); 97 | JsonFactory jsonFactory = new JacksonFactory(); 98 | return new HomeGraphService.Builder(httpTransport, jsonFactory, creds).build(); 99 | } 100 | 101 | private List syncHomeGraph(String agentUserId, HomeGraphService homeGraphService) 102 | throws IOException { 103 | SyncRequest reqSync = new SyncRequest(); 104 | reqSync.setRequestId(UUID.randomUUID().toString()); 105 | reqSync.setAgentUserId(agentUserId); 106 | 107 | SyncResponse resSync = homeGraphService.devices().sync(reqSync).execute(); 108 | 109 | List devices = resSync.getPayload().getDevices(); 110 | 111 | List deviceList = new ArrayList<>(); 112 | for (com.google.api.services.homegraph.v1.model.Device d: devices) { 113 | Device device = new Device(); 114 | device.setId(d.getId()); 115 | device.setType(d.getType().substring(d.getType().lastIndexOf('.') + 1)); 116 | device.setName(d.getName().getName()); 117 | if (d.getName().getNicknames() != null) { 118 | device.setNicknames(d.getName().getNicknames()); 119 | } 120 | deviceList.add(device); 121 | } 122 | deviceCache.put(agentUserId, deviceList); 123 | return deviceList; 124 | } 125 | 126 | @GetMapping("/refresh") 127 | @CrossOrigin(origins = "http://localhost:4200") // for testing && development, should be safe to remove 128 | public Response refresh(@RequestParam(value = "agentUserId") final String agentUserId) { 129 | try { 130 | return queryHomeGraph(agentUserId, createConnection()); 131 | // Response r = new Response(); 132 | // return r; 133 | } catch (IOException | GeneralSecurityException e) { 134 | throw new RuntimeException("Cannot refresh devices", e); 135 | } 136 | } 137 | 138 | private Response queryHomeGraph(String agentUserId, HomeGraphService homeGraphService) 139 | throws IOException { 140 | Response response = new Response(); 141 | 142 | QueryRequest reqQuery = new QueryRequest(); 143 | reqQuery.setRequestId(UUID.randomUUID().toString()); 144 | reqQuery.setAgentUserId(agentUserId); 145 | 146 | List deviceList = deviceCache.get(agentUserId); 147 | 148 | List deviceIdList = new ArrayList<>(); 149 | for (Device device : deviceList) { 150 | AgentDeviceId agentDeviceId = new AgentDeviceId().setId(device.getId()); 151 | deviceIdList.add(agentDeviceId); 152 | } 153 | 154 | QueryRequestInput queryRequestInput = new QueryRequestInput() 155 | .setPayload(new QueryRequestPayload() 156 | .setDevices(deviceIdList)); 157 | reqQuery.setInputs(Collections.singletonList(queryRequestInput)); 158 | 159 | response.setDebugRequest(reqQuery.toString()); 160 | QueryResponse resQuery = homeGraphService.devices().query(reqQuery).execute(); 161 | response.setDebugResponse(resQuery.toString()); 162 | Map> responseDevices = resQuery.getPayload().getDevices(); 163 | 164 | StringBuilder delta = new StringBuilder(); 165 | for (Device device : deviceList) { 166 | Map deviceStates = responseDevices.get(device.getId()); 167 | for (Map.Entry state : deviceStates.entrySet()) { 168 | if (device.getStatesCache().containsKey(state.getKey()) && 169 | !device.getStatesCache().get(state.getKey()).equals(state.getValue())) { 170 | // This state has changed! 171 | device.setUpdated(true); 172 | delta 173 | .append(state.getKey()) 174 | .append(": ") 175 | .append(state.getValue().toString()) 176 | .append(" | "); 177 | } 178 | device.getStatesCache().put(state.getKey(), state.getValue()); 179 | } 180 | } 181 | response.setDebugDelta(delta.toString()); 182 | response.setDevices(deviceList); 183 | return response; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/main/java/com/google/homegraph/dashboard/controller/UploadController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.google.homegraph.dashboard.controller; 17 | 18 | import java.io.Serializable; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | import org.springframework.beans.factory.annotation.Autowired; 24 | import org.springframework.context.annotation.Scope; 25 | import org.springframework.http.HttpStatus; 26 | import org.springframework.http.ResponseEntity; 27 | import org.springframework.stereotype.Controller; 28 | import org.springframework.web.bind.annotation.CrossOrigin; 29 | import org.springframework.web.bind.annotation.PostMapping; 30 | import org.springframework.web.bind.annotation.RequestParam; 31 | import org.springframework.web.multipart.MultipartFile; 32 | 33 | import com.google.homegraph.dashboard.service.StorageService; 34 | 35 | @Controller 36 | @Scope(value="session") 37 | public class UploadController implements Serializable { 38 | 39 | private static final long serialVersionUID = 5546148932202075489L; 40 | private static Logger log = LoggerFactory.getLogger("UploadController"); 41 | 42 | @PostMapping("/upload") 43 | @CrossOrigin 44 | public ResponseEntity handleFileUpload(@RequestParam("file") final MultipartFile file) { 45 | StorageService storageService = StorageService.getInstance(); 46 | log.debug("Uploading file"); 47 | try { 48 | log.debug("storing file " + file.getOriginalFilename()); 49 | storageService.storeInMemory(file); 50 | return ResponseEntity.status(HttpStatus.OK).build(); 51 | } catch (Exception e) { 52 | String message = "Failed to upload " + file.getOriginalFilename() + "!"; 53 | log.error(message + e.getMessage()); 54 | return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(message); 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/google/homegraph/dashboard/model/Device.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.google.homegraph.dashboard.model; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collection; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.function.Function; 24 | import java.util.stream.Collectors; 25 | 26 | public class Device { 27 | private String id; 28 | private String type; 29 | private String name; 30 | private List nicknames = new ArrayList<>(); 31 | private Map statesCache = new HashMap<>(); 32 | private boolean updated; 33 | 34 | public boolean isUpdated() { 35 | return updated; 36 | } 37 | 38 | public void setUpdated(boolean updated) { 39 | this.updated = updated; 40 | } 41 | 42 | public String getId() { 43 | return id; 44 | } 45 | 46 | public void setId(String id) { 47 | this.id = id; 48 | } 49 | 50 | public String getType() { 51 | return type; 52 | } 53 | 54 | public void setType(String type) { 55 | this.type = type; 56 | } 57 | 58 | public Map getStatesCache() { 59 | return statesCache; 60 | } 61 | 62 | public void setStatesCache(Map states) { 63 | this.statesCache = states; 64 | } 65 | 66 | public String getName() { 67 | return name; 68 | } 69 | 70 | public void setName(String name) { 71 | this.name = name; 72 | } 73 | 74 | public List getNicknames() { 75 | return nicknames; 76 | } 77 | 78 | public void setNicknames(List nicknames) { 79 | this.nicknames = nicknames; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/google/homegraph/dashboard/model/Response.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.google.homegraph.dashboard.model; 17 | 18 | import java.util.List; 19 | 20 | public class Response { 21 | private List devices; 22 | private String debugRequest; 23 | private String debugResponse; 24 | private String debugDelta; 25 | public List getDevices() { 26 | return devices; 27 | } 28 | public void setDevices(List devices) { 29 | this.devices = devices; 30 | } 31 | public String getDebugRequest() { 32 | return debugRequest; 33 | } 34 | public void setDebugRequest(String debugRequest) { 35 | this.debugRequest = debugRequest; 36 | } 37 | public String getDebugResponse() { 38 | return debugResponse; 39 | } 40 | public void setDebugResponse(String debugResponse) { 41 | this.debugResponse = debugResponse; 42 | } 43 | public String getDebugDelta() { 44 | return debugDelta; 45 | } 46 | public void setDebugDelta(String debugDelta) { 47 | this.debugDelta = debugDelta; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/google/homegraph/dashboard/service/StorageService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.google.homegraph.dashboard.service; 17 | 18 | import java.io.ByteArrayInputStream; 19 | import java.io.ByteArrayOutputStream; 20 | import java.io.FileInputStream; 21 | import java.io.IOException; 22 | import java.io.InputStream; 23 | import java.io.Serializable; 24 | import java.net.MalformedURLException; 25 | import java.nio.file.Files; 26 | import java.nio.file.Path; 27 | import java.nio.file.Paths; 28 | import java.nio.file.StandardCopyOption; 29 | import java.util.HashMap; 30 | import java.util.Map; 31 | 32 | import org.slf4j.Logger; 33 | import org.slf4j.LoggerFactory; 34 | import org.springframework.context.annotation.Scope; 35 | import org.springframework.core.io.Resource; 36 | import org.springframework.core.io.UrlResource; 37 | import org.springframework.stereotype.Service; 38 | import org.springframework.util.FileSystemUtils; 39 | import org.springframework.web.multipart.MultipartFile; 40 | 41 | @Service 42 | @Scope(value="session") 43 | public class StorageService implements Serializable{ 44 | 45 | private static final long serialVersionUID = 1L; 46 | private static Logger log = LoggerFactory.getLogger("StorageService"); 47 | private static StorageService instance; 48 | 49 | private Map inMemoryStore = new HashMap<>(); 50 | 51 | private StorageService() {} 52 | 53 | public static StorageService getInstance() { 54 | if (instance == null) { 55 | instance = new StorageService(); 56 | } 57 | return instance; 58 | } 59 | 60 | public void storeInMemory(MultipartFile file) { 61 | try { 62 | inMemoryStore.put(file.getOriginalFilename(), convert(file)); 63 | } catch (IOException e) { 64 | log.error(e.getMessage() + ", Failed to convert the file and store in memory, will try disk instead"); 65 | } 66 | } 67 | 68 | public InputStream loadFile(String filename) { 69 | // check if it is in memory 70 | log.debug("Storing file: " + filename); 71 | if (inMemoryStore.containsKey(filename)) { 72 | log.debug("Found file in memory"); 73 | return new ByteArrayInputStream(inMemoryStore.get(filename)); 74 | } else { 75 | log.debug("File is not in memory"); 76 | return null; 77 | } 78 | } 79 | 80 | private byte[] convert(MultipartFile file) throws IOException { 81 | InputStream is = file.getInputStream(); 82 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 83 | 84 | int nRead; 85 | byte[] data = new byte[16384]; 86 | while ((nRead = is.read(data, 0, data.length)) != -1) { 87 | buffer.write(data, 0, nRead); 88 | } 89 | 90 | buffer.flush(); 91 | return buffer.toByteArray(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/ngapp/.angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "project": { 4 | "name": "report-state-dashboard" 5 | }, 6 | "apps": [ 7 | { 8 | "root": "src", 9 | "outDir": "../webapp", 10 | "assets": [ 11 | "assets", 12 | "favicon.ico" 13 | ], 14 | "index": "index.html", 15 | "main": "main.ts", 16 | "polyfills": "polyfills.ts", 17 | "test": "test.ts", 18 | "tsconfig": "tsconfig.app.json", 19 | "testTsconfig": "tsconfig.spec.json", 20 | "prefix": "app", 21 | "styles": [ 22 | "styles.css" 23 | ], 24 | "scripts": [], 25 | "environmentSource": "environments/environment.ts", 26 | "environments": { 27 | "dev": "environments/environment.ts", 28 | "prod": "environments/environment.prod.ts" 29 | } 30 | } 31 | ], 32 | "e2e": { 33 | "protractor": { 34 | "config": "./protractor.conf.js" 35 | } 36 | }, 37 | "lint": [ 38 | { 39 | "project": "src/tsconfig.app.json", 40 | "exclude": "**/node_modules/**" 41 | }, 42 | { 43 | "project": "src/tsconfig.spec.json", 44 | "exclude": "**/node_modules/**" 45 | }, 46 | { 47 | "project": "e2e/tsconfig.e2e.json", 48 | "exclude": "**/node_modules/**" 49 | } 50 | ], 51 | "test": { 52 | "karma": { 53 | "config": "./karma.conf.js" 54 | } 55 | }, 56 | "defaults": { 57 | "styleExt": "css", 58 | "component": {} 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/ngapp/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { AppPage } from './app.po'; 17 | 18 | describe('client App', () => { 19 | let page: AppPage; 20 | 21 | beforeEach(() => { 22 | page = new AppPage(); 23 | }); 24 | 25 | it('should display welcome message', () => { 26 | page.navigateTo(); 27 | expect(page.getParagraphText()).toEqual('Welcome to app!'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/main/ngapp/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { browser, by, element } from 'protractor'; 17 | 18 | export class AppPage { 19 | navigateTo() { 20 | return browser.get('/'); 21 | } 22 | 23 | getParagraphText() { 24 | return element(by.css('app-root h1')).getText(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/ngapp/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/ngapp/karma.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // Karma configuration file, see link for more information 18 | // https://karma-runner.github.io/1.0/config/configuration-file.html 19 | 20 | module.exports = function (config) { 21 | config.set({ 22 | basePath: '', 23 | frameworks: ['jasmine', '@angular/cli'], 24 | plugins: [ 25 | require('karma-jasmine'), 26 | require('karma-chrome-launcher'), 27 | require('karma-jasmine-html-reporter'), 28 | require('karma-coverage-istanbul-reporter'), 29 | require('@angular/cli/plugins/karma') 30 | ], 31 | client: { 32 | clearContext: false // leave Jasmine Spec Runner output visible in browser 33 | }, 34 | coverageIstanbulReporter: { 35 | reports: ['html', 'lcovonly'], 36 | fixWebpackSourcePaths: true 37 | }, 38 | angularCli: { 39 | environment: 'dev' 40 | }, 41 | reporters: ['progress', 'kjhtml'], 42 | port: 9876, 43 | colors: true, 44 | logLevel: config.LOG_INFO, 45 | autoWatch: true, 46 | browsers: ['Chrome'], 47 | singleRun: false 48 | }); 49 | }; 50 | -------------------------------------------------------------------------------- /src/main/ngapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build": "ng build --prod && cp -r ../appengine/* ../webapp/", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/animations": "^5.2.0", 16 | "@angular/cdk": "^5.2.4", 17 | "@angular/common": "^5.2.0", 18 | "@angular/compiler": "^5.2.0", 19 | "@angular/core": "^5.2.0", 20 | "@angular/forms": "^5.2.0", 21 | "@angular/http": "^5.2.0", 22 | "@angular/material": "^5.2.4", 23 | "@angular/platform-browser": "^5.2.0", 24 | "@angular/platform-browser-dynamic": "^5.2.0", 25 | "@angular/router": "^5.2.0", 26 | "core-js": "^2.4.1", 27 | "rxjs": "^5.5.6", 28 | "zone.js": "^0.8.19" 29 | }, 30 | "devDependencies": { 31 | "@angular/cli": "~1.7.4", 32 | "@angular/compiler-cli": "^5.2.0", 33 | "@angular/language-service": "^5.2.0", 34 | "@types/jasmine": "~2.8.3", 35 | "@types/jasminewd2": "~2.0.2", 36 | "@types/node": "~6.0.60", 37 | "codelyzer": "^4.0.1", 38 | "jasmine-core": "~2.8.0", 39 | "jasmine-spec-reporter": "~4.2.1", 40 | "karma": "~2.0.0", 41 | "karma-chrome-launcher": "~2.2.0", 42 | "karma-coverage-istanbul-reporter": "^1.2.1", 43 | "karma-jasmine": "~1.1.0", 44 | "karma-jasmine-html-reporter": "^0.2.2", 45 | "protractor": "~5.1.2", 46 | "ts-node": "~4.1.0", 47 | "tslint": "~5.9.1", 48 | "typescript": "~2.5.3" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/ngapp/protractor.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // Protractor configuration file, see link for more information 18 | // https://github.com/angular/protractor/blob/master/lib/config.ts 19 | 20 | const { SpecReporter } = require('jasmine-spec-reporter'); 21 | 22 | exports.config = { 23 | allScriptsTimeout: 11000, 24 | specs: [ 25 | './e2e/**/*.e2e-spec.ts' 26 | ], 27 | capabilities: { 28 | 'browserName': 'chrome' 29 | }, 30 | directConnect: true, 31 | baseUrl: 'http://localhost:4200/', 32 | framework: 'jasmine', 33 | jasmineNodeOpts: { 34 | showColors: true, 35 | defaultTimeoutInterval: 30000, 36 | print: function () { } 37 | }, 38 | onPrepare() { 39 | require('ts-node').register({ 40 | project: 'e2e/tsconfig.e2e.json' 41 | }); 42 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /src/main/ngapp/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | .toolbar-spacer { 17 | flex: 1 1 auto; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/ngapp/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | Welcome to {{title}}! 18 | 19 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/ngapp/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { TestBed, async } from '@angular/core/testing'; 17 | import { AppComponent } from './app.component'; 18 | describe('AppComponent', () => { 19 | beforeEach(async(() => { 20 | TestBed.configureTestingModule({ 21 | declarations: [ 22 | AppComponent 23 | ], 24 | }).compileComponents(); 25 | })); 26 | it('should create the app', async(() => { 27 | const fixture = TestBed.createComponent(AppComponent); 28 | const app = fixture.debugElement.componentInstance; 29 | expect(app).toBeTruthy(); 30 | })); 31 | it(`should have as title 'app'`, async(() => { 32 | const fixture = TestBed.createComponent(AppComponent); 33 | const app = fixture.debugElement.componentInstance; 34 | expect(app.title).toEqual('app'); 35 | })); 36 | it('should render title in a h1 tag', async(() => { 37 | const fixture = TestBed.createComponent(AppComponent); 38 | fixture.detectChanges(); 39 | const compiled = fixture.debugElement.nativeElement; 40 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!'); 41 | })); 42 | }); 43 | -------------------------------------------------------------------------------- /src/main/ngapp/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { Component, OnInit } from '@angular/core'; 17 | 18 | @Component({ 19 | selector: 'app-root', 20 | templateUrl: './app.component.html', 21 | styleUrls: ['./app.component.css'] 22 | }) 23 | export class AppComponent implements OnInit { 24 | title = 'Smart Home Report State Dashboard'; 25 | 26 | constructor() { 27 | } 28 | 29 | async ngOnInit() { 30 | 31 | } 32 | 33 | logout() { 34 | 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/ngapp/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { BrowserModule } from '@angular/platform-browser'; 17 | import { NgModule } from '@angular/core'; 18 | 19 | import { AppComponent } from './app.component'; 20 | import { DeviceService } from './shared/device/device.service'; 21 | import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; 22 | import { DeviceListComponent } from './device-list/device-list.component'; 23 | import { MatButtonModule, MatCardModule, MatInputModule, MatListModule, MatToolbarModule, MatExpansionModule } from '@angular/material'; 24 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 25 | import { GiphyService } from './shared/giphy/giphy.service'; 26 | import { RouterModule, Routes } from '@angular/router'; 27 | import { FormsModule } from '@angular/forms'; 28 | import { HomeComponent } from './home/home.component'; 29 | import { UploadFileService } from './shared/upload/upload-file.service'; 30 | 31 | import { APP_BASE_HREF, Location } from '@angular/common'; 32 | import { getBaseLocation } from './shared/common-functions.util'; 33 | 34 | const appRoutes: Routes = [ 35 | { path: '', redirectTo: '/', pathMatch: 'full' }, 36 | { 37 | path: '', 38 | component: HomeComponent 39 | }, 40 | { 41 | path: 'device-list/:file/:id', 42 | component: DeviceListComponent 43 | } 44 | ]; 45 | 46 | 47 | @NgModule({ 48 | declarations: [ 49 | AppComponent, 50 | DeviceListComponent, 51 | HomeComponent 52 | ], 53 | imports: [ 54 | BrowserModule, 55 | HttpClientModule, 56 | BrowserAnimationsModule, 57 | MatButtonModule, 58 | MatCardModule, 59 | MatInputModule, 60 | MatListModule, 61 | MatExpansionModule, 62 | MatToolbarModule, 63 | FormsModule, 64 | RouterModule.forRoot(appRoutes), 65 | ], 66 | providers: [DeviceService, UploadFileService, GiphyService, { 67 | provide: APP_BASE_HREF, 68 | useFactory: getBaseLocation 69 | }, ], 70 | bootstrap: [AppComponent] 71 | }) 72 | export class AppModule { } 73 | -------------------------------------------------------------------------------- /src/main/ngapp/src/app/device-list/device-list.component.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | .color-green { 17 | color: green; 18 | } 19 | .color-white { 20 | color: white; 21 | } 22 | img.mat-list-avatar { 23 | width: 40px; 24 | height: 40px; 25 | min-width: inherit; 26 | border-radius: 100%; 27 | } -------------------------------------------------------------------------------- /src/main/ngapp/src/app/device-list/device-list.component.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | Device List 18 | 19 | 20 | 22 | 23 | {{device.name}} 24 | {{device.icon}} 25 |

{{device.name}} ({{device.nicknames.join(',')}}) ({{device.id}})

26 | 27 | 28 | 29 | 30 |

{{ state }}

31 |
32 | 33 |
34 |
35 |
36 | 37 | 38 | 39 | 40 |
Delta:
41 |
{{debugDelta}}
42 |
Request:
43 |
{{debugRequest}}
44 |
Response:
45 |
{{debugResponse}}
46 |
47 |
-------------------------------------------------------------------------------- /src/main/ngapp/src/app/device-list/device-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 17 | 18 | import { DeviceListComponent } from './device-list.component'; 19 | 20 | describe('DeviceListComponent', () => { 21 | let component: DeviceListComponent; 22 | let fixture: ComponentFixture; 23 | 24 | beforeEach(async(() => { 25 | TestBed.configureTestingModule({ 26 | declarations: [DeviceListComponent] 27 | }) 28 | .compileComponents(); 29 | })); 30 | 31 | beforeEach(() => { 32 | fixture = TestBed.createComponent(DeviceListComponent); 33 | component = fixture.componentInstance; 34 | fixture.detectChanges(); 35 | }); 36 | 37 | it('should create', () => { 38 | expect(component).toBeTruthy(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/main/ngapp/src/app/device-list/device-list.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { Component, OnDestroy, OnInit } from '@angular/core'; 17 | import { DeviceService } from '../shared/device/device.service'; 18 | import { GiphyService } from '../shared/giphy/giphy.service'; 19 | import { Subscription } from 'rxjs/Subscription'; 20 | import { ActivatedRoute, Router } from '@angular/router'; 21 | import { getDeviceIcon } from '../device.icon'; 22 | 23 | @Component({ 24 | selector: 'app-device-list', 25 | templateUrl: './device-list.component.html', 26 | styleUrls: ['./device-list.component.css'] 27 | }) 28 | export class DeviceListComponent implements OnInit, OnDestroy { 29 | devices: Array; 30 | sub: Subscription; 31 | debugRequest: string; 32 | debugResponse: string; 33 | debugDelta: string; 34 | file; 35 | id; 36 | constructor(private route: ActivatedRoute, 37 | private router: Router, private deviceService: DeviceService, private giphyService: GiphyService) { } 38 | 39 | ngOnInit() { 40 | this.sub = this.route.params.subscribe(params => { 41 | this.file = params['file']; 42 | this.id = params['id']; 43 | this.changeRefreshState(false); 44 | console.log('calling backend with ' + this.id); 45 | if (this.id) { 46 | this.deviceService.getAll(this.file, this.id).subscribe(data => { 47 | this.changeRefreshState(true); 48 | this.devices = data; 49 | console.log('Data response', data); 50 | for (const device of this.devices) { 51 | device.state = Object.entries(device.statesCache).map(([k, v]) => { 52 | return `${k}: ${JSON.stringify(v)}` 53 | }) 54 | const giphyService = this.giphyService.get(device.name); 55 | if (giphyService) { 56 | giphyService.subscribe(url => { 57 | device.giphyUrl = url; 58 | }); 59 | } else { 60 | device.icon = getDeviceIcon(device.type); 61 | } 62 | } 63 | }); 64 | } 65 | }); 66 | } 67 | ngOnDestroy() { 68 | this.sub.unsubscribe(); 69 | } 70 | refresh() { 71 | if (this.id) { 72 | this.changeRefreshState(false); 73 | this.deviceService.refresh(this.id).subscribe(data => { 74 | this.changeRefreshState(true); 75 | this.devices = data.devices; 76 | this.debugRequest = data.debugRequest; 77 | this.debugResponse = data.debugResponse; 78 | this.debugDelta = data.debugDelta; 79 | console.log('Data response', data); 80 | for (const device of this.devices) { 81 | device.state = Object.entries(device.statesCache).map(([k, v]) => { 82 | return `${k}: ${JSON.stringify(v)}` 83 | }) 84 | const giphyService = this.giphyService.get(device.name); 85 | if (giphyService) { 86 | giphyService.subscribe(url => { 87 | device.giphyUrl = url; 88 | }); 89 | } else { 90 | device.icon = getDeviceIcon(device.type); 91 | } 92 | } 93 | }); 94 | } 95 | } 96 | changeRefreshState(enable) { 97 | const refreshButton: HTMLButtonElement = document.querySelector('button.mat-fab'); 98 | refreshButton.innerHTML = enable ? 'Refresh' : '...'; 99 | refreshButton.disabled = !enable; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/ngapp/src/app/device.icon.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const deviceIconMap = { 18 | 'AC_UNIT': 'ac_unit', 19 | 'AIRFRESHENER': 'hourglass_full', 20 | 'AIRPURIFIER': 'sim_card', 21 | 'AWNING': 'store_mall_directory', 22 | 'BLINDS': 'view_week', 23 | 'BOILER': 'invert_colors', 24 | 'CAMERA': 'camera_alt', 25 | 'COFFEEMAKER': 'local_cafe', 26 | 'CURTAIN': 'flag', 27 | 'DISHWASHER': 'restaurant', 28 | 'DOOR': 'open_in_new', 29 | 'DRYER': 'casino', 30 | 'FAN': 'toys', 31 | 'FIREPLACE': 'whatshot', 32 | 'GARAGEDOOR': 'drive_eta', 33 | 'GATE': 'storage', 34 | 'HEATER': 'account_balance_wallet', 35 | 'HOOD': 'view_day', 36 | 'KETTLE': 'filter_frames', 37 | 'LIGHT': 'wb_incandescent', 38 | 'LOCK': 'lock', 39 | 'MICROWAVE': 'nfc', 40 | 'OUTLET': 'power', 41 | 'OVEN': 'web', 42 | 'PERGOLA': 'layers', 43 | 'REFRIGERATOR': 'kitchen', 44 | 'SCENE': 'slideshow', 45 | 'SECURITYSYSTEM': 'verified_user', 46 | 'SHOWER': 'local_car_wash', 47 | 'SHUTTER': 'map', 48 | 'SPRINKLER': 'filter_vintage', 49 | 'SWITCH': 'call_merge', 50 | 'THERMOSTAT': 'brightness_7', 51 | 'VACUUM': 'router', 52 | 'VALVE': 'settings_input_component', 53 | 'WASHER': 'local_laundry_service', 54 | 'WATERHEATER': 'local_drink', 55 | 'WINDOW': 'wallpaper' 56 | } 57 | 58 | /** 59 | * Wrap the icon name in an HTML element to display 60 | * 61 | * @param deviceType The full namespace of the device type 62 | * @returns The HTML element that will show that icon 63 | */ 64 | export const getDeviceIcon = (deviceType: string) => { 65 | return deviceIconMap[deviceType] || 'code' /* `code` is default icon */; 66 | } -------------------------------------------------------------------------------- /src/main/ngapp/src/app/home/home.component.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ -------------------------------------------------------------------------------- /src/main/ngapp/src/app/home/home.component.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 |
19 |
20 |
23 | {{progress.percentage}}%
24 |
25 | 26 | 30 | 31 | 32 | 33 |

agentUserId: 34 | 36 |

37 | 38 | 40 |
41 | 42 |
43 |
-------------------------------------------------------------------------------- /src/main/ngapp/src/app/home/home.component.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 17 | 18 | import { HomeComponent } from './home.component'; 19 | 20 | describe('HomeComponent', () => { 21 | let component: HomeComponent; 22 | let fixture: ComponentFixture; 23 | 24 | beforeEach(async(() => { 25 | TestBed.configureTestingModule({ 26 | declarations: [HomeComponent] 27 | }) 28 | .compileComponents(); 29 | })); 30 | 31 | beforeEach(() => { 32 | fixture = TestBed.createComponent(HomeComponent); 33 | component = fixture.componentInstance; 34 | fixture.detectChanges(); 35 | }); 36 | 37 | it('should create', () => { 38 | expect(component).toBeTruthy(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/main/ngapp/src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { Component, OnInit } from '@angular/core'; 17 | import { HttpClient, HttpResponse, HttpEventType } from '@angular/common/http'; 18 | import { UploadFileService } from '../shared/upload/upload-file.service'; 19 | import { Router, ActivatedRoute } from '@angular/router'; 20 | 21 | @Component({ 22 | selector: 'app-home', 23 | templateUrl: './home.component.html', 24 | styleUrls: ['./home.component.css'] 25 | }) 26 | export class HomeComponent implements OnInit { 27 | selectedFiles: FileList; 28 | currentFileUpload: File; 29 | agentUserId: String; 30 | progress: { percentage: number } = { percentage: 0 }; 31 | 32 | constructor(private uploadService: UploadFileService, private router: Router, private route: ActivatedRoute) { } 33 | 34 | ngOnInit() { 35 | } 36 | 37 | selectFile(event) { 38 | this.selectedFiles = event.target.files; 39 | } 40 | 41 | setAgentId(event) { 42 | this.agentUserId = event.target.value; 43 | } 44 | 45 | upload() { 46 | this.progress.percentage = 0; 47 | 48 | this.currentFileUpload = this.selectedFiles.item(0); 49 | this.uploadService.pushFileToStorage(this.currentFileUpload).subscribe(event => { 50 | if (event.type === HttpEventType.UploadProgress) { 51 | this.progress.percentage = Math.round(100 * event.loaded / event.total); 52 | } else if (event instanceof HttpResponse) { 53 | console.log('File is completely uploaded!'); 54 | } 55 | }); 56 | 57 | this.selectedFiles = undefined; 58 | } 59 | 60 | go() { 61 | if (!this.currentFileUpload || !this.agentUserId) { 62 | alert('Missing data'); 63 | return; 64 | } 65 | this.router.navigate([`../device-list/${this.currentFileUpload.name}/${encodeURIComponent(String(this.agentUserId))}`], 66 | { relativeTo: this.route }); 67 | } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /src/main/ngapp/src/app/shared/common-functions.util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export function getBaseLocation() { 17 | const paths: string[] = location.pathname.split('/').splice(1, 1); 18 | const basePath: string = (paths && paths[0]) || ''; // Default: empty, may need to edit this if server appends project nam to url! 19 | return ''; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/ngapp/src/app/shared/device/device.service.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { TestBed, inject } from '@angular/core/testing'; 17 | 18 | import { DeviceService } from './device.service'; 19 | 20 | describe('DeviceService', () => { 21 | beforeEach(() => { 22 | TestBed.configureTestingModule({ 23 | providers: [DeviceService] 24 | }); 25 | }); 26 | 27 | it('should be created', inject([DeviceService], (service: DeviceService) => { 28 | expect(service).toBeTruthy(); 29 | })); 30 | }); 31 | -------------------------------------------------------------------------------- /src/main/ngapp/src/app/shared/device/device.service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { environment } from '../../../environments/environment'; 17 | import { getBaseLocation } from '../common-functions.util'; 18 | import { Injectable } from '@angular/core'; 19 | import { HttpClient } from '@angular/common/http'; 20 | import { Observable } from 'rxjs/Observable'; 21 | 22 | @Injectable() 23 | export class DeviceService { 24 | 25 | public DEVICE_API = environment.API + '/devices'; 26 | 27 | constructor(private http: HttpClient) { 28 | } 29 | 30 | getAll(file: string, id: string): Observable { 31 | return this.http.get(getBaseLocation() + '/homegraph?agentUserId=' + id + '&file=' + file); 32 | } 33 | 34 | refresh(id: string): Observable { 35 | return this.http.get(getBaseLocation() + '/refresh?agentUserId=' + id); 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/ngapp/src/app/shared/giphy/giphy.service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { Injectable } from '@angular/core'; 17 | import { HttpClient } from '@angular/common/http'; 18 | import 'rxjs/add/operator/map'; 19 | 20 | @Injectable() 21 | export class GiphyService { 22 | 23 | apiKey = 'api-key'; 24 | giphyApi = `//api.giphy.com/v1/gifs/search?api_key=${this.apiKey}&limit=1&rating=g&q=`; 25 | 26 | 27 | constructor(public http: HttpClient) { 28 | } 29 | 30 | get(searchTerm) { 31 | const apiLink = this.giphyApi + searchTerm; 32 | if (this.apiKey === 'api-key') { 33 | // Use undefined image 34 | return undefined; 35 | } 36 | return this.http.get(apiLink).map((response: any) => { 37 | if (response.data.length > 0) { 38 | return response.data[0].images.original.url; 39 | } else { 40 | return 'https://media.giphy.com/media/YaOxRsmrv9IeA/giphy.gif'; // dancing cat for 404 41 | } 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/ngapp/src/app/shared/upload/upload-file.service.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { TestBed, inject } from '@angular/core/testing'; 17 | 18 | import { UploadFileService } from './upload-file.service'; 19 | 20 | describe('UploadFileService', () => { 21 | beforeEach(() => { 22 | TestBed.configureTestingModule({ 23 | providers: [UploadFileService] 24 | }); 25 | }); 26 | 27 | it('should be created', inject([UploadFileService], (service: UploadFileService) => { 28 | expect(service).toBeTruthy(); 29 | })); 30 | }); 31 | -------------------------------------------------------------------------------- /src/main/ngapp/src/app/shared/upload/upload-file.service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { environment } from '../../../environments/environment'; 17 | import { getBaseLocation } from '../common-functions.util'; 18 | import { Injectable } from '@angular/core'; 19 | import { HttpClient, HttpRequest, HttpEvent } from '@angular/common/http'; 20 | import { Observable } from 'rxjs/Observable'; 21 | 22 | @Injectable() 23 | export class UploadFileService { 24 | 25 | constructor(private http: HttpClient) { } 26 | 27 | pushFileToStorage(file: File): Observable> { 28 | const formdata: FormData = new FormData(); 29 | 30 | formdata.append('file', file); 31 | 32 | return this.http.post(getBaseLocation() + '/upload', formdata, { 33 | reportProgress: true, 34 | observe: 'events' 35 | }); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/ngapp/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/actions-on-google/smart-home-dashboard/247cc4633260013ba1317a2dbaa76cd14b001eed/src/main/ngapp/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/main/ngapp/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export const environment = { 17 | production: true, 18 | API: '//reportstate-dashboard.appspot.com' 19 | }; 20 | -------------------------------------------------------------------------------- /src/main/ngapp/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // The file contents for the current environment will overwrite these during build. 18 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 19 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 20 | // The list of which env maps to which file can be found in `.angular-cli.json`. 21 | 22 | export const environment = { 23 | production: false, 24 | API: '//localhost:8080' 25 | }; 26 | -------------------------------------------------------------------------------- /src/main/ngapp/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/actions-on-google/smart-home-dashboard/247cc4633260013ba1317a2dbaa76cd14b001eed/src/main/ngapp/src/favicon.ico -------------------------------------------------------------------------------- /src/main/ngapp/src/index.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | Report State Dashboard 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/main/ngapp/src/main.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { enableProdMode } from '@angular/core'; 17 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 18 | 19 | import { AppModule } from './app/app.module'; 20 | import { environment } from './environments/environment'; 21 | 22 | if (environment.production) { 23 | enableProdMode(); 24 | } 25 | 26 | platformBrowserDynamic().bootstrapModule(AppModule) 27 | .catch(err => console.log(err)); 28 | -------------------------------------------------------------------------------- /src/main/ngapp/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * This file includes polyfills needed by Angular and is loaded before the app. 19 | * You can add your own extra polyfills to this file. 20 | * 21 | * This file is divided into 2 sections: 22 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 23 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 24 | * file. 25 | * 26 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 27 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 28 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 29 | * 30 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 31 | */ 32 | 33 | /*************************************************************************************************** 34 | * BROWSER POLYFILLS 35 | */ 36 | 37 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 38 | // import 'core-js/es6/symbol'; 39 | // import 'core-js/es6/object'; 40 | // import 'core-js/es6/function'; 41 | // import 'core-js/es6/parse-int'; 42 | // import 'core-js/es6/parse-float'; 43 | // import 'core-js/es6/number'; 44 | // import 'core-js/es6/math'; 45 | // import 'core-js/es6/string'; 46 | // import 'core-js/es6/date'; 47 | // import 'core-js/es6/array'; 48 | // import 'core-js/es6/regexp'; 49 | // import 'core-js/es6/map'; 50 | // import 'core-js/es6/weak-map'; 51 | // import 'core-js/es6/set'; 52 | 53 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 54 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 55 | 56 | /** IE10 and IE11 requires the following for the Reflect API. */ 57 | // import 'core-js/es6/reflect'; 58 | 59 | 60 | /** Evergreen browsers require these. **/ 61 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 62 | import 'core-js/es7/reflect'; 63 | 64 | 65 | /** 66 | * Required to support Web Animations `@angular/platform-browser/animations`. 67 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 68 | **/ 69 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 70 | 71 | /** 72 | * By default, zone.js will patch all possible macroTask and DomEvents 73 | * user can disable parts of macroTask/DomEvents patch by setting following flags 74 | */ 75 | 76 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 77 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 78 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 79 | 80 | /* 81 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 82 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 83 | */ 84 | // (window as any).__Zone_enable_cross_context_check = true; 85 | 86 | /*************************************************************************************************** 87 | * Zone JS is required by default for Angular itself. 88 | */ 89 | import 'zone.js/dist/zone'; // Included with Angular CLI. 90 | 91 | 92 | 93 | /*************************************************************************************************** 94 | * APPLICATION IMPORTS 95 | */ 96 | -------------------------------------------------------------------------------- /src/main/ngapp/src/styles.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | @import "~@angular/material/prebuilt-themes/pink-bluegrey.css"; 18 | @import 'https://fonts.googleapis.com/icon?family=Material+Icons'; 19 | 20 | body { 21 | margin: 0; 22 | font-family: Roboto, sans-serif; 23 | } 24 | 25 | .material-icons.md-light { 26 | color: rgba(255, 255, 255, 1); 27 | } 28 | 29 | .material-icons.md-48 { 30 | font-size: 48px 31 | } -------------------------------------------------------------------------------- /src/main/ngapp/src/test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 18 | 19 | import 'zone.js/dist/zone-testing'; 20 | import { getTestBed } from '@angular/core/testing'; 21 | import { 22 | BrowserDynamicTestingModule, 23 | platformBrowserDynamicTesting 24 | } from '@angular/platform-browser-dynamic/testing'; 25 | 26 | declare const require: any; 27 | 28 | // First, initialize the Angular testing environment. 29 | getTestBed().initTestEnvironment( 30 | BrowserDynamicTestingModule, 31 | platformBrowserDynamicTesting() 32 | ); 33 | // Then we find all the tests. 34 | const context = require.context('./', true, /\.spec\.ts$/); 35 | // And load the modules. 36 | context.keys().map(context); 37 | -------------------------------------------------------------------------------- /src/main/ngapp/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "es2015", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /src/main/ngapp/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "types": [ 8 | "jasmine", 9 | "node" 10 | ] 11 | }, 12 | "files": [ 13 | "test.ts" 14 | ], 15 | "include": [ 16 | "**/*.spec.ts", 17 | "**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/main/ngapp/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* SystemJS module definition */ 18 | declare var module: NodeModule; 19 | interface NodeModule { 20 | id: string; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/ngapp/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "../dist/out-tsc", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es5", 11 | "typeRoots": [ 12 | "node_modules/@types" 13 | ], 14 | "lib": [ 15 | "es2017", 16 | "dom" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/ngapp/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs", 22 | "rxjs/Rx" 23 | ], 24 | "import-spacing": true, 25 | "indent": [ 26 | true, 27 | "spaces" 28 | ], 29 | "interface-over-type-literal": true, 30 | "label-position": true, 31 | "max-line-length": [ 32 | true, 33 | 140 34 | ], 35 | "member-access": false, 36 | "member-ordering": [ 37 | true, 38 | { 39 | "order": [ 40 | "static-field", 41 | "instance-field", 42 | "static-method", 43 | "instance-method" 44 | ] 45 | } 46 | ], 47 | "no-arg": true, 48 | "no-bitwise": true, 49 | "no-console": [ 50 | true, 51 | "debug", 52 | "info", 53 | "time", 54 | "timeEnd", 55 | "trace" 56 | ], 57 | "no-construct": true, 58 | "no-debugger": true, 59 | "no-duplicate-super": true, 60 | "no-empty": false, 61 | "no-empty-interface": true, 62 | "no-eval": true, 63 | "no-inferrable-types": [ 64 | true, 65 | "ignore-params" 66 | ], 67 | "no-misused-new": true, 68 | "no-non-null-assertion": true, 69 | "no-shadowed-variable": true, 70 | "no-string-literal": false, 71 | "no-string-throw": true, 72 | "no-switch-case-fall-through": true, 73 | "no-trailing-whitespace": true, 74 | "no-unnecessary-initializer": true, 75 | "no-unused-expression": true, 76 | "no-use-before-declare": true, 77 | "no-var-keyword": true, 78 | "object-literal-sort-keys": false, 79 | "one-line": [ 80 | true, 81 | "check-open-brace", 82 | "check-catch", 83 | "check-else", 84 | "check-whitespace" 85 | ], 86 | "prefer-const": true, 87 | "quotemark": [ 88 | true, 89 | "single" 90 | ], 91 | "radix": true, 92 | "semicolon": [ 93 | true, 94 | "always" 95 | ], 96 | "triple-equals": [ 97 | true, 98 | "allow-null-check" 99 | ], 100 | "typedef-whitespace": [ 101 | true, 102 | { 103 | "call-signature": "nospace", 104 | "index-signature": "nospace", 105 | "parameter": "nospace", 106 | "property-declaration": "nospace", 107 | "variable-declaration": "nospace" 108 | } 109 | ], 110 | "unified-signatures": true, 111 | "variable-name": false, 112 | "whitespace": [ 113 | true, 114 | "check-branch", 115 | "check-decl", 116 | "check-operator", 117 | "check-separator", 118 | "check-type" 119 | ], 120 | "directive-selector": [ 121 | true, 122 | "attribute", 123 | "app", 124 | "camelCase" 125 | ], 126 | "component-selector": [ 127 | true, 128 | "element", 129 | "app", 130 | "kebab-case" 131 | ], 132 | "no-output-on-prefix": true, 133 | "use-input-property-decorator": true, 134 | "use-output-property-decorator": true, 135 | "use-host-property-decorator": true, 136 | "no-input-rename": true, 137 | "no-output-rename": true, 138 | "use-life-cycle-interface": true, 139 | "use-pipe-transform-interface": true, 140 | "component-class-suffix": true, 141 | "directive-class-suffix": true 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | 3 | spring.http.multipart.max-file-size=2500KB 4 | spring.http.multipart.max-request-size=2500KB 5 | --------------------------------------------------------------------------------