diff --git a/AlexaClientSDKConfig_default.json b/AlexaClientSDKConfig_default.json new file mode 100644 index 0000000..4484d1b --- /dev/null +++ b/AlexaClientSDKConfig_default.json @@ -0,0 +1,80 @@ + { + "authDelegate":{ + // The Client Secret of the Product from developer.amazon.com + "clientSecret":"70c955eaa6b998bb3eff97f051b18a5f72b21d38e3ba102da1e83aafdd0efa1f", + // Unique device serial number. e.g. 123456 + "deviceSerialNumber":"CXNK000ABCDE", + // Refresh Token populated by running AuthServer.py + "refreshToken":"", + // The Client ID of the Product from developer.amazon.com + "clientId":"amzn1.application-oa2-client.a308f3dc39a54349b970f19b13c86f5d", + // Product ID from developer.amazon.com + "productId":"GC4026E" + }, + "alertsCapabilityAgent":{ + // Path to Alerts database file. e.g. /home/ubuntu/Build/alerts.db + // Note: The directory specified must be valid. + // The database file (alerts.db) will be created by SampleApp, do not create it yourself. + // The database file should only be used for alerts (don't use it for other components of SDK) + "databaseFilePath":"", + // Path to default Alarm sound file. e.g. /home/ubuntu/alert_sounds/alarm_normal.mp3 + // Note: The audio file must exist and be a valid file. + "alarmSoundFilePath":"", + // Path to short Alarm sound file. e.g. /home/ubuntu/alert_sounds/alarm_short.wav + // Note: The audio file must exist and be a valid file. + "alarmShortSoundFilePath":"", + // Path to default timer sound file. e.g. /home/ubuntu/alert_sounds/timer_normal.mp3 + // Note: The audio file must exist and be a valid file. + "timerSoundFilePath":"", + // Path to short timer sound file. e.g. /home/ubuntu/alert_sounds/timer_short.wav + // Note: The audio file must exist and be a valid file. + "timerShortSoundFilePath":"" + }, + "settings":{ + // Path to Settings database file. e.g. /home/ubuntu/Build/settings.db + // Note: The directory specified must be valid. + // The database file (settings.db) will be created by SampleApp, do not create it yourself. + // The database file should only be used for settings (don't use it for other components of SDK) + "databaseFilePath":"", + "defaultAVSClientSettings":{ + // Default language for Alexa. + // See https://developer.amazon.com/docs/alexa-voice-service/settings.html#settingsupdated for valid values. + "locale":"" + } + }, + "certifiedSender":{ + // Path to Certified Sender database file. e.g. /home/ubuntu/Build/certifiedsender.db + // Note: The directory specified must be valid. + // The database file (certifiedsender.db) will be created by SampleApp, do not create it yourself. + // The database file should only be used for certifiedSender (don't use it for other components of SDK) + "databaseFilePath":"" + } + } + + +// Notes for logging +// The log levels are supported to debug when SampleApp is not working as expected. +// There are 14 levels of logging with DEBUG9 providing the highest level of logging and CRITICAL providing +// the lowest level of logging i.e. if DEBUG9 is specified while running the SampleApp, all the logs at DEBUG9 and +// below are displayed, whereas if CRITICAL is specified, only logs of CRITICAL are displayed. +// The 14 levels are: +// DEBUG9, DEBUG8, DEBUG7, DEBUG6, DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, DEBUG0, INFO, WARN, ERROR, CRTITICAL. + +// To selectively see the logging for a particular module, you can specify logging level in this json file. +// Some examples are: +// To only see logs of level INFO and below for ACL and MediaPlayer modules, +// - grep for ACSDK_LOG_MODULE in source folder. Find the log module for ACL and MediaPlayer. +// - Put the following in json: + +// "acl":{ +// "logLevel":"INFO" +// }, +// "mediaPlayer":{ +// "logLevel":"INFO" +// } + +// To enable DEBUG, build with cmake option -DCMAKE_BUILD_TYPE=DEBUG. By default it is built with RELEASE build. +// And run the SampleApp similar to the following command. +// e.g. TZ=UTC ./SampleApp /home/ubuntu/.../AlexaClientSDKConfig.json /home/ubuntu/KittAiModels/ DEBUG9" + + diff --git a/config.c b/config.c index fb3c193..01a4daa 100644 --- a/config.c +++ b/config.c @@ -67,7 +67,8 @@ json_object* getCleanConfig(char *filename) { //printf(cJSON_Print(json)); //printf("\n"); }else{ - printf("Failed to parse response\n"); + printf("Failed to parse config\n"); + return NULL; } return json; @@ -123,11 +124,20 @@ char *get_config_param_value(json_object *config, char *name){ } int update_config_param(json_object *config, char *name, char *value) { - json_object_object_del(config, name); + json_object *authDelegate = json_object_object_get(config, "authDelegate"); + if(authDelegate == NULL) { + return -1; + } + if (strcmp(name, "refreshToken") == 0){ + json_object_object_del(authDelegate, "refreshToken"); - json_object_object_add(config, name, json_object_new_string(value)); + json_object_object_add(authDelegate, "refreshToken", json_object_new_string(value)); + + return 0; + } - return 0; + return -1; } + diff --git a/lwa.c b/lwa.c index 19df30f..16abe6c 100644 --- a/lwa.c +++ b/lwa.c @@ -22,7 +22,7 @@ char * clientSecret = "70c955eaa6b998bb3eff97f051b18a5f72b21d38e3ba102da1e83aafd #endif char * scope = "alexa:all"; char * responseType = "code"; -char * urlencode(char *json_string) +static char * urlencode(char *json_string) { static char output[1024]; memset(output, 0, sizeof(output)); @@ -45,7 +45,7 @@ char * urlencode(char *json_string) } -char* getRedirectUrl(){ +static char* getRedirectUrl(json_object *config){ static char lwaUrl[1000]; char *scopeData; json_object *alexa_all, *attr; @@ -54,13 +54,13 @@ char* getRedirectUrl(){ #ifdef HARDCODE_CONFIG json_object_object_add(alexa_all, "productID", json_object_new_string(productId)); #else - json_object_object_add(alexa_all, "productID",json_object_new_string(get_config_param_value(json_config, "productId"))); + json_object_object_add(alexa_all, "productID",json_object_new_string(get_config_param_value(config, "productId"))); #endif json_object_object_add(alexa_all, "productInstanceAttributes", attr=json_object_new_object()); #ifdef HARDCODE_CONFIG json_object_object_add(attr, "deviceSerialNumber", json_object_new_string(deviceSerialNumber)); #else - json_object_object_add(attr, "deviceSerialNumber", json_object_new_string(get_config_param_value(json_config, "deviceSerialNumber"))); + json_object_object_add(attr, "deviceSerialNumber", json_object_new_string(get_config_param_value(config, "deviceSerialNumber"))); #endif scopeData = json_object_to_json_string(root); json_object_put(root); @@ -73,7 +73,7 @@ char* getRedirectUrl(){ #ifdef HARDCODE_CONFIG sprintf(param, "client_id=%s", clientId); #else - sprintf(param, "client_id=%s", get_config_param_value(json_config, "clientId")); + sprintf(param, "client_id=%s", get_config_param_value(config, "clientId")); #endif printf("param:%s\n",param); strcat(lwaUrl, param); @@ -103,7 +103,7 @@ char* getRedirectUrl(){ } -int parseResponse(char *response) { +static int parseResponse(char *response, json_object *config) { json_object *json = NULL; int ret; json = json_tokener_parse(response); @@ -113,15 +113,15 @@ int parseResponse(char *response) { char *refreshToken = json_object_get_string(refreshTokenObject); if(refreshToken) { printf("\n==============>Get Refresh Token: [%s]\n", refreshToken); - ret = update_config_param(json_config, "refreshToken", refreshToken); + ret = update_config_param(config, "refreshToken", refreshToken); if (ret != 0){ printf("\nERROR: Failed to update refresh token\n"); json_object_put(json); return -1; } - ret = writeConfig(json_config, config_out_path); + ret = writeConfig(config, config_path); if (ret != 0){ - printf("\nERROR: Failed to write config to [%s]\n", config_out_path); + printf("\nERROR: Failed to write config to [%s]\n", config_path); json_object_put(json); return -1; } @@ -139,14 +139,14 @@ int parseResponse(char *response) { } } char resp[10240]={0}; -size_t write_data(void* buffer, size_t size, size_t nmemb, void* response) { +static size_t write_data(void* buffer, size_t size, size_t nmemb, void* response) { int len = size*nmemb; memset(resp, 0, sizeof(resp)); memcpy(response, buffer, len); return len; } -int requestRefreshToken(char *code) { +static int requestRefreshToken(char *code, json_object *config) { CURL *curl; CURLcode res; int ret; @@ -170,12 +170,12 @@ int requestRefreshToken(char *code) { #ifdef HARDCODE_CONFIG clientId, #else - get_config_param_value(json_config, "clientId"), + get_config_param_value(config, "clientId"), #endif #ifdef HARDCODE_CONFIG clientSecret, #else - get_config_param_value(json_config, "clientSecret"), + get_config_param_value(config, "clientSecret"), #endif urlencode(redirect_uri) ); @@ -205,7 +205,7 @@ int requestRefreshToken(char *code) { printf("OK\n"); } printf("\n==============>Get RESPONSE: %s\n", resp); - ret = parseResponse(resp); + ret = parseResponse(resp, config); if (ret != 0){ printf("ERROR: parseResponse failed\n"); curl_easy_cleanup(curl); @@ -226,9 +226,20 @@ int requestRefreshToken(char *code) { } } -int handleUserRequest(int client) { +static int returnResult(int client, int status, char *info) { + char content[1024]={0}; + write(client, "HTTP/1.0 200\n",13); + write(client, "\n", 1); + if (status == 0){ + sprintf(content, "AVS LWA SuccessSuccess\n"); + }else{ + sprintf(content, "AVS LWA FailureFailed.%s\n", info); + } + write(client, content, strlen(content)); +} +int handleUserRequest(int client, json_object *config) { printf("\n==============>Receive User Request"); - char *redirectUrl = getRedirectUrl(); + char *redirectUrl = getRedirectUrl(config); if (redirectUrl == NULL) { return -1; } @@ -240,18 +251,7 @@ int handleUserRequest(int client) { write(client, "\n", 1); return 0; } -int returnResult(int client, int status, char *info) { - char content[1024]={0}; - write(client, "HTTP/1.0 200\n",13); - write(client, "\n", 1); - if (status == 0){ - sprintf(content, "AVS LWA SuccessSuccess\n"); - }else{ - sprintf(content, "AVS LWA FailureFailed.%s\n", info); - } - write(client, content, strlen(content)); -} -int handleAuthCodeGrant(int client, char *request) { +int handleAuthCodeGrant(int client, char *request, json_object *config) { char *tag, *code; int ret; printf("\n==============>Receive authresponse"); @@ -265,7 +265,7 @@ int handleAuthCodeGrant(int client, char *request) { returnResult(client, -1, "Failed to extract auth code"); return -1; } - ret = requestRefreshToken(code); + ret = requestRefreshToken(code, config); if (ret != 0){ printf("\nERROR: Failed to get refresh token\n"); returnResult(client, -1, "Failed to get refresh token"); diff --git a/lwa.h b/lwa.h index 0f4a469..fd7c07d 100644 --- a/lwa.h +++ b/lwa.h @@ -1,4 +1,4 @@ -extern char config_out_path[256]; +extern char config_path[256]; char redirect_uri[256]; -int handleUserRequest(int client); -int handleAuthCodeGrant(int client, char *request); +int handleUserRequest(int client, json_object *config); +int handleAuthCodeGrant(int client, char *request, json_object *config); diff --git a/main.c b/main.c index 773621e..f8ba872 100644 --- a/main.c +++ b/main.c @@ -17,16 +17,18 @@ #define CONNMAX 1000 #define BYTES 1024 #define PORT "3000" -#define CONFIG_IN "AlexaClientSDKConfig.json" -#define CONFIG_OUT "AlexaClientSDKConfig_out.json" +#define CONFIG "AlexaClientSDKConfig.json" +#define CONFIG_DEFAULT "AlexaClientSDKConfig_default.json" #define REDIRECT_URI "http://localhost:3000/authresponse" +#define WEBROOT "/etc/alexa/web/" -char config_in_path[256]={0}; -char config_out_path[256]={0}; +char config_path[256]={0}; +char config_default_path[256]={0}; char redirect_uri[256]={0}; char port[16]={0}; +char web_root[256]={0}; int listenfd, clients[CONNMAX]; -json_object *json_config = NULL; + void error(char *); @@ -46,20 +48,19 @@ int main(int argc, char** argv) int slot=0; - strcpy(config_in_path, CONFIG_IN); - strcpy(config_out_path, CONFIG_OUT); + strcpy(config_path, CONFIG); + strcpy(config_default_path, CONFIG_DEFAULT); strcpy(port, PORT); strcpy(redirect_uri, REDIRECT_URI); + strcpy(web_root, WEBROOT); + //Parsing the command line arguments - while ((c = getopt (argc, argv, "p:i:o:r:")) != -1) { + while ((c = getopt (argc, argv, "p:c:r:w:d:")) != -1) { switch (c) { - case 'i': - strcpy(config_in_path, optarg); - break; - case 'o': - strcpy(config_out_path, optarg); + case 'c': + strcpy(config_path, optarg); break; case 'p': strcpy(port,optarg); @@ -67,24 +68,25 @@ int main(int argc, char** argv) case 'r': strcpy(redirect_uri,optarg); break; + case 'w': + strcpy(web_root, optarg); + break; + case 'd': + strcpy(config_default_path, optarg); + break; default: printf("\nERROR: Unknown parameter\n"); exit(-1); } } - printf("AVS Auth Server started.\nPort: %s%s%s \nConfig in: %s%s%s \nConfig out: %s%s%s\nRedirect URI: %s%s%s\n", + printf("AVS Auth Server started.\nWebroot:%s%s%s \nPort: %s%s%s \nConfig: %s%s%s \nConfig default: %s%s%s \nRedirect URI: %s%s%s\n", + "\033[92m",web_root,"\033[0m", "\033[92m",port,"\033[0m", - "\033[92m",config_in_path,"\033[0m", - "\033[92m",config_out_path,"\033[0m", + "\033[92m",config_path,"\033[0m", + "\033[92m",config_default_path,"\033[0m", "\033[92m",redirect_uri,"\033[0m"); - json_config = getCleanConfig(config_in_path); - if (json_config == NULL) { - printf("ERROR: failed to parse the input config"); - exit(-1); - } - // Setting all elements to -1: signifies there is no client connected int i; for (i=0; i 0){ + printf("Get %d data\n", nread); + write(client, data_to_send, nread); + + } + close(fd); + +} + +int sendRedirect(int client, char *url) +{ + char content[1000]; + sprintf(content, "Location: %s\n",url); + write(client, "HTTP/1.0 307\n",13); + write(client, content, strlen(content)+1); + write(client, "\n", 1); + return 0; +} + +int copyFile(char *from, char *to) +{ + FILE *fpFrom, *fpTo; + char buffer[4096]; + int bytes; + fpFrom = fopen(from, "r"); + fpTo = fopen(to, "w+"); + + printf("\nCopy File from %s to %s\n", from , to); + + if(fpFrom == NULL){ + printf("\nFailed to open file %s", from); + return -1; + } + if(fpTo == NULL) { + printf("\nFailed to open file %s", to); + fclose(fpFrom); + return -1; + } + while((bytes = fread(buffer, 1, sizeof(buffer), fpFrom)) > 0){ + printf("#####get %d bytes\n", bytes); + fwrite(buffer, 1, bytes, fpTo); + } + fclose(fpFrom); + fclose(fpTo); + +} + +json_object* get_config(){ + json_object *config = getCleanConfig(config_path); + if (config == NULL) { + printf("\nINFO: Failed to open config \"%s\", use default config", config_path); + config = getCleanConfig(config_default_path); + if(config == NULL) + { + printf("\nERROR: Failed to open default config\"%s\". Exit.", config_default_path); + } + } + return config; +} + + //client connection void respond(int n) { @@ -194,17 +277,46 @@ void respond(int n) } else { + printf("\nRequest: %s", reqline[1]); + + json_object *json_config = NULL; + json_config = get_config(); + if(json_config == NULL) + { + printf("\nERROR: Failed to get config. Exit."); + exit(-1); + } if ( strncmp(reqline[1], "/\0", 2)==0 ) { - ret = handleUserRequest(clients[n]); + + char * refresh_token = get_config_param_value(json_config, "refreshToken"); + if (refresh_token == NULL || strlen(refresh_token) == 0){ + ret = sendFromDirectory(clients[n], web_root, "splash_screen.html"); + } + else + { + ret = sendFromDirectory(clients[n], web_root, "logout.html"); + } + }else if (strncmp(reqline[1], "/authresponse", 13)==0) { - ret = handleAuthCodeGrant(clients[n], reqline[1]); + ret = handleAuthCodeGrant(clients[n], reqline[1], json_config); if (ret == 0) { printf("\nGOOD: successfully generated the config with refresh token. Exit.\n"); - exit(0); + ret = sendFromDirectory(clients[n], web_root, "things_to_try.html"); } + }else if(strncmp(reqline[1], "/login", 6)==0) { + ret = handleUserRequest(clients[n], json_config); + }else if(strncmp(reqline[1], "/logout", 7)==0) { + copyFile(config_default_path, config_path); + ret = sendRedirect(clients[n], "/splash_screen.html"); } + else{ + ret = sendFromDirectory(clients[n], web_root, reqline[1]); + } + json_object_put(json_config); } + + } } diff --git a/web/AuthServer.py b/web/AuthServer.py new file mode 100644 index 0000000..3017ea5 --- /dev/null +++ b/web/AuthServer.py @@ -0,0 +1,215 @@ +# +# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. +# + +from flask import Flask, redirect, request, send_from_directory +import requests +import json +import commentjson +import re + +from os.path import abspath, isfile, dirname +import sys +from urllib import urlencode +from shutil import copyfile + +# Shuts down the web-server. +def shutdown(): + func = request.environ.get('werkzeug.server.shutdown') + if func is None: + return 'You can close this window and terminate the script.' + else: + func() + return 'Server is shutting down, so you can close this window.' + +app = Flask(__name__) + +# Static redirect_uri. +redirectUri = 'http://localalexa.lan:3000/authresponse' + +# Static JSON key for the config file. +authDelegateKey = 'authDelegate' + +# Amazon LWA API URL. +amazonLwaApiUrl = 'https://api.amazon.com/auth/o2/token' + +# Amazon LWA API Request header. +amazonLwaApiHeaders = {'Content-Type': 'application/x-www-form-urlencoded'} + +# Default configuration filename, to be filled by CMake +defaultConfigFilename = "/etc/alexa/AlexaClientSDKConfig.json" + +# JSON keys for config file +CLIENT_ID = 'clientId' +CLIENT_SECRET = 'clientSecret' +PRODUCT_ID = 'productId' +DEVICE_SERIAL_NUMBER = 'deviceSerialNumber' +REFRESH_TOKEN = 'refreshToken' + +# Read the configuration filename from the command line arguments -if it exists. +if 2 == len(sys.argv): + configFilename = abspath(sys.argv[1]) +else: + # Assuming the script hasn't moved from the original location + # use the default location of SDKConfig.json using relative paths + configFilename = defaultConfigFilename + +# Check if the configuration file exists. +if not isfile(configFilename): + print 'The file "' + \ + configFilename + \ + '" does not exists. Please create this file and fill required data.' + sys.exit(1) + +try: + configFile = open(configFilename,'r') + +except IOError: + print 'File "' + configFilename + '" not found!' + sys.exit(1) +else: + with configFile: + configData = commentjson.load(configFile) + if not configData.has_key(authDelegateKey): + print 'The config file "' + \ + configFilename + \ + '" is missing the field "' + \ + authDelegateKey + \ + '".' + sys.exit(1) + else: + authDelegateDict = configData[authDelegateKey] + + +# Check if all required keys are parsed. +requiredKeys = [CLIENT_ID, CLIENT_SECRET, PRODUCT_ID, DEVICE_SERIAL_NUMBER] +try: + missingKey = requiredKeys[map(authDelegateDict.has_key,requiredKeys).index(False)]; + print 'Missing key: "' + missingKey + '". The list of required keys are:' + print ' * ' + '\n * '.join(requiredKeys) + print 'Exiting.' + sys.exit(1) +except ValueError: + pass +tokenIsValid=0; +# Refresh the refresh token to check if it really is a refresh token. +if authDelegateDict.has_key(REFRESH_TOKEN): + postData = { + 'grant_type': 'refresh_token', + 'refresh_token': authDelegateDict[REFRESH_TOKEN], + 'client_id': authDelegateDict[CLIENT_ID], + 'client_secret': authDelegateDict[CLIENT_SECRET]} + tokenRefreshRequest = requests.post( + amazonLwaApiUrl, + data=urlencode(postData), + headers=amazonLwaApiHeaders) + defaultRefreshTokenString = authDelegateDict[REFRESH_TOKEN]; + if 200 == tokenRefreshRequest.status_code: + print 'You have a valid refresh token already in the file.' +# sys.exit(0) + tokenIsValid=1; + else: + print 'The refresh request failed with the response code ' + \ + str(tokenRefreshRequest.status_code) + \ + ('. This might be due to a bad refresh token or bad client data. ' + 'We will continue with getting a refresh token, discarding the one in the file.\n') +else: + print 'Missing key: "' + REFRESH_TOKEN + sys.exit(0) +# The top page redirects to LWA page. + +root="/etc/alexa/web/" + +@app.route('/') +def index(): + if tokenIsValid: + return send_from_directory(root, "logout.html") + else: + return send_from_directory(root, "splash_screen.html") + + +@app.route("/") +def getfile(filename): + return send_from_directory(root, filename); + +@app.route('/logout') +def logout(): + copyfile("/etc/alexa/AlexaClientSDKConfigDefault.json", configFilename); + return redirect("/splash_screen.html"); + +@app.route('/login') +def login(): + scopeData = ('{{"alexa:all":' + '{{"productID":"{productId}",' + '"productInstanceAttributes":' + '{{"deviceSerialNumber":"{deviceSerialNumber}"}}}}}}').format( + productId=authDelegateDict[PRODUCT_ID], + deviceSerialNumber=authDelegateDict[DEVICE_SERIAL_NUMBER]) + lwaUrl = 'https://www.amazon.com/ap/oa/?' + urlencode({ + 'scope': 'alexa:all', + 'scope_data': scopeData, + 'client_id': authDelegateDict[CLIENT_ID], + 'response_type': 'code', + 'redirect_uri': redirectUri, + }) + return redirect(lwaUrl) + + +# The `authresponse` received from the redirect through LWA makes a POST to LWA API to get the token. +@app.route('/authresponse') +def get_refresh_token(): + postData = { + 'grant_type': 'authorization_code', + 'code': request.args.get('code',''), + 'client_id': authDelegateDict[CLIENT_ID], + 'client_secret': authDelegateDict[CLIENT_SECRET], + 'redirect_uri': redirectUri, + } + tokenRequest = requests.post( + amazonLwaApiUrl, + data=urlencode(postData), + headers=amazonLwaApiHeaders) + if not tokenRequest.json().has_key('refresh_token'): + return send_from_directory(root, "authentication_failed.html") + + authDelegateDict['refreshToken'] = tokenRequest.json()['refresh_token'] + try: + configFile = open(configFilename,'r') + except IOError: + print 'File "' + configFilename + '" cannot be opened!' + return '

The file "' + \ + configFilename + \ + '" cannot be opened, please check if the file is open elsewhere.
' + \ + shutdown() + \ + '

' + else: + fileContent = configFile.read() + try: + configFile = open(configFilename,'w') + except IOError: + print 'File "' + configFilename + '" cannot be opened!' + return '

The file "' + \ + configFilename + \ + '" cannot be opened, please check if the file is open elsewhere.
' + \ + shutdown() + \ + '

' + replaceWith = '\n\t\t\"refreshToken\":' + '\"' + tokenRequest.json()['refresh_token'] + '\",' + stringToFind = r"\s*\"\s*{}\s*\"\s*:\s*\"\s*".format(REFRESH_TOKEN) + \ + re.escape(defaultRefreshTokenString) + r"\s*\"\s*," + fileContent = re.sub(stringToFind, replaceWith, fileContent) + configFile.write(fileContent) + tokenIsValid=1 + + return send_from_directory(root, "things_to_try.html") +app.run(host='localalexa.lan',port=3000) diff --git a/web/authentication_failed.html b/web/authentication_failed.html new file mode 100755 index 0000000..25e9a97 --- /dev/null +++ b/web/authentication_failed.html @@ -0,0 +1,63 @@ + + + + + + + + + + Alexa + + + + +
+ +
+ +
amazon alexa
+
+
Something's gone wrong. Please try again.
+
+ + + diff --git a/web/img/logo.png b/web/img/logo.png new file mode 100755 index 0000000..24a7448 Binary files /dev/null and b/web/img/logo.png differ diff --git a/web/img/logo_1.png b/web/img/logo_1.png new file mode 100755 index 0000000..3a76429 Binary files /dev/null and b/web/img/logo_1.png differ diff --git a/web/img/logo_2.png b/web/img/logo_2.png new file mode 100755 index 0000000..77c76ee Binary files /dev/null and b/web/img/logo_2.png differ diff --git a/web/img/logo_3.png b/web/img/logo_3.png new file mode 100755 index 0000000..28123d1 Binary files /dev/null and b/web/img/logo_3.png differ diff --git a/web/logout.html b/web/logout.html new file mode 100755 index 0000000..d3d5cb8 --- /dev/null +++ b/web/logout.html @@ -0,0 +1,96 @@ + + + + + + + + + + Alexa + + + + +
+ +
+ +
amazon alexa
+
+
Your device includes access to Alexa.
+
Connect your Amazon account to
+
access personalized features.
+
+
+
Alexa allows you to use your voice to play music and get news,
+
sports scores, weather and more—all hands-free.
+
All you have to do is ask Alexa.
+
+ + + + diff --git a/web/splash_screen.html b/web/splash_screen.html new file mode 100755 index 0000000..c641316 --- /dev/null +++ b/web/splash_screen.html @@ -0,0 +1,96 @@ + + + + + + + + + + Alexa + + + + +
+ +
+ +
amazon alexa
+
+
Your device includes access to Alexa.
+
Connect your Amazon account to
+
access personalized features.
+
+
+
Alexa allows you to use your voice to play music and get news,
+
sports scores, weather and more—all hands-free.
+
All you have to do is ask Alexa.
+
+ + + + diff --git a/web/things_to_try.html b/web/things_to_try.html new file mode 100755 index 0000000..8811a16 --- /dev/null +++ b/web/things_to_try.html @@ -0,0 +1,128 @@ + + + + + + + + + + Alexa + + + + +
+ +
+ +
amazon alexa
+
+
You're ready to use Alexa. Here are some things to try:
+
+
+
+ + Alexa, play some jazz music. +
+
+
+ + Alexa, what's my Flash Briefing. +
+
+
+ + Alexa, what's your favorite movie. +
+
+
+
+
To learn more and access additional features, download the Amazon Alexa App.
+
+ + +