diff --git a/package.json b/package.json index b59f28b..fc9833b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,8 @@ { "author": "Mathew Reiss, ishotjr, pebbledev #pokemon", - "dependencies": {}, + "dependencies": { + "pebble-clay": "^1.0.2" + }, "keywords": [], "name": "pok-mon-go-radar", "pebble": { @@ -865,7 +867,9 @@ "Pokemon9Distance": 34, "Pokemon9Bearing": 35, "RequestType": 36, - "DisplayMessage" : 37 + "DisplayMessage" : 37, + "UseDataUrl" : 38, + "DataUrl" : 39 } }, "version": "0.8.0" diff --git a/src/js/app.js b/src/js/app.js index fdb6091..6f04a0c 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -1,6 +1,20 @@ //"use strict"; - +// clay initialization +var Clay = require('pebble-clay'); +var clayConfig = require('./config.json'); +// override defaults - no need for InboxReceivedHandler vs. just keeping it in JS +var clay = new Clay(clayConfig, null, { autoHandleEvents: false }); +var messageKeys = require('message_keys'); + +// Mock data for testing: +var useDataUrl = 0; +var date = new Date(); +var startTime = date.getTime(); + +// default to static data URL until config override +var staticUrl = "https://pokewatch.github.io/PoGO/unknown6.json"; +var dataUrl = staticUrl; var myLatitude, myLongitude; var hasBeenNotified = false; //var pkmnLatitude, pkmnLongitude; @@ -12,7 +26,7 @@ var MessageQueue = require("./MessageQueue"); var DummyDataCreator = require("./dummyData"); var dummyData; // = DummyDataCreator.createDummyData(5, 45, -97); -console.log(JSON.stringify(dummyData)); +//console.log(JSON.stringify(dummyData)); var firstTimeUpdatingLocation = true; var serverError = false; @@ -20,9 +34,21 @@ var serverError = false; // XMLHttpRequest helper var xhrRequest = function (url, type, callback) { var xhr = new XMLHttpRequest(); + + // time out after 10 seconds + xhr.timeout = 10 * 1000; + xhr.onload = function () { + // request finished callback(this.responseText); }; + + xhr.ontimeout = function (e) { + // request timed out + MessageQueue.sendAppMessage({"DisplayMessage": "Data request timed out"}); + serverError = true; + }; + xhr.open(type, url); xhr.send(); }; @@ -44,14 +70,20 @@ function getPokemon() { //(latitude, longitude) { console.log("myLatitude: " + myLatitude); console.log("myLongitude: " + myLongitude); - // live PokeVision data, hard-coded to Ann Arbor for now - var scanUrl = 'https://pokevision.com/map/scan/' + myLatitude + '/' + myLongitude; - var dataUrl = 'https://pokevision.com/map/data/' + myLatitude + '/' + myLongitude; + // live PokeVision data + //var scanUrl = 'https://pokevision.com/map/scan/' + myLatitude + '/' + myLongitude; + //var dataUrl = 'https://pokevision.com/map/data/' + myLatitude + '/' + myLongitude; // static (stable!) example of PokeVision data //var scanUrl = 'https://mathewreiss.github.io/PoGO/data.json'; //var dataUrl = 'https://mathewreiss.github.io/PoGO/data.json'; + // static example of new back end + //var scanUrl = 'https://pokewatch.github.io/PoGO/unknown6.json'; + //var dataUrl = 'https://pokewatch.github.io/PoGO/unknown6.json'; + + /* + // TODO: is this OK? xhrRequest(scanUrl, 'GET', function(scanResponseText) { if (scanResponseText.indexOf("maintenance") > -1) { @@ -85,132 +117,177 @@ function getPokemon() { //(latitude, longitude) { } } + */ + // TODO: fix indentation here during clean-up! xhrRequest(dataUrl, 'GET', function(dataResponseText) { - var json = JSON.parse(dataResponseText); + console.log(dataResponseText); // JSON.stringify() not necessary! + + var json; + try { + json = JSON.parse(dataResponseText); + } catch(e) { + // TODO: improve! + json = JSON.parse('{"pokemons":[]}'); + MessageQueue.sendAppMessage({"DisplayMessage": "Invalid data returned by server"}); + serverError = true; + } // TODO: this is silly - really should just not do xhrRequest at all, but // as a quick hack to enable "demo mode" for now... - if (serverError) { + if (useDataUrl == 0) { // populate if not set (once) if (dummyData == null) { dummyData = DummyDataCreator.createDummyData(5, myLatitude, myLongitude); } json = dummyData; + MessageQueue.sendAppMessage({"DisplayMessage": "No server available - reverting to DEMO mode using DUMMY data..."}); } console.log(dataResponseText); // JSON.stringify() not necessary! // TODO: status check! - console.log('status is "' + json.status + '"'); + //console.log('status is "' + json.status + '"'); // TODO: much better error checking??? - if (json.pokemon.length > 0) { - - var allNearbyPokemon = []; - - var i; - for (i = 0; i < json.pokemon.length; i++) { - // TODO: should still actually verify vs. using blindly! - console.log('pokemon[' + i + '].pokemonId is "' + json.pokemon[i].pokemonId + '"'); - // PokeVision is string for some reason - var pokemonId = Number(json.pokemon[i].pokemonId); - console.log('pokemonId is "' + pokemonId + '"'); + // TODO: better schema validation + if (json.hasOwnProperty("pokemons")) { + + if (json.pokemons.length > 0) { - var pokemonExpirationTime = json.pokemon[i].expiration_time; - console.log('pokemonExpirationTime is "' + pokemonExpirationTime + '"'); + if (json.pokemons[0].hasOwnProperty("disappear_time")) { + + var allNearbyPokemon = []; + + var i; + for (i = 0; i < json.pokemons.length; i++) { + + // TODO: should still actually verify vs. using blindly! + console.log('pokemon[' + i + '].pokemon_id is "' + json.pokemons[i].pokemon_id + '"'); + // PokeVision is string for some reason + var pokemonId = json.pokemons[i].pokemon_id; //Number(json.pokemons[i].pokemonId); + console.log('pokemonId is "' + pokemonId + '"'); + + + var pokemonExpirationTime = json.pokemons[i].disappear_time; + + // offset static times based on app launch time - ONLY when + // static URL specified (and enabled!) + // TODO: care about capitalization etc.? + if ((useDataUrl == 1) && (dataUrl == staticUrl)) { + pokemonExpirationTime = pokemonExpirationTime - 1470884585708 + + (7 * 60 * 1000) + startTime; + } + + // new data uses JS time vs. epoch, so divide by 1000 (for now/to prevent the need for changes to .c) + pokemonExpirationTime /= 1000; + + console.log('pokemonExpirationTime is "' + pokemonExpirationTime + '"'); + + + var pokemonLatitude = json.pokemons[i].latitude; + console.log('pokemonLatitude is "' + pokemonLatitude + '"'); + var pokemonLongitude = json.pokemons[i].longitude; + console.log('pokemonLongitude is "' + pokemonLongitude + '"'); + + var pokemonDistance = getDistance(myLatitude, myLongitude, pokemonLatitude, pokemonLongitude); + var pokemonBearing = getBearing(myLatitude, myLongitude, pokemonLatitude, pokemonLongitude); + + var pokemonUID = json.pokemons[i].uid; + + // fails on iOS! + // per @katharine: + // > PebbleKit JS Android is not to spec. + //allNearbyPokemon.push({i, pokemonId, pokemonExpirationTime, pokemonDistance}); + + var pokemonData = { + "i": i, + "pokemonId": pokemonId, + "pokemonExpirationTime": pokemonExpirationTime, + "pokemonLatitude": pokemonLatitude, + "pokemonLongitude": pokemonLongitude, + "pokemonDistance": pokemonDistance, + "pokemonBearing": pokemonBearing, + "pokemonUID": pokemonUID + }; + allNearbyPokemon.push(pokemonData); + + } + + console.log("allNearbyPokemon: " + JSON.stringify(allNearbyPokemon)); + + // sort by distance + allNearbyPokemon.sort(function(a, b) { + return a.pokemonDistance - b.pokemonDistance; + }); + + //get rid of duplicates that have the same UID + for(var i=0; i PebbleKit JS Android is not to spec. - //allNearbyPokemon.push({i, pokemonId, pokemonExpirationTime, pokemonDistance}); - - var pokemonData = { - "i": i, - "pokemonId": pokemonId, - "pokemonExpirationTime": pokemonExpirationTime, - "pokemonLatitude": pokemonLatitude, - "pokemonLongitude": pokemonLongitude, - "pokemonDistance": pokemonDistance, - "pokemonBearing": pokemonBearing, - "pokemonUID": pokemonUID - }; - allNearbyPokemon.push(pokemonData); - - } - - console.log("allNearbyPokemon: " + JSON.stringify(allNearbyPokemon)); - - // sort by distance - allNearbyPokemon.sort(function(a, b) { - return a.pokemonDistance - b.pokemonDistance; - }); - - //get rid of duplicates that have the same UID - for(var i=0; ipebbledev #pokemon members, and especially to @mathew and @pautomas for the initial UI/functionality, @liammcmains for the many late nights of assistance and support, and @robisodd for his amazing improvements and optimization, many of which we have yet to dazzle you with as we focus on back-end solutions!
-ishotjr \":bowtie:\"" + }, + { + "type": "section", + "items": [ + { + "type": "heading", + "defaultValue": "Pokemon Data Source" + }, + { + "type": "text", + "defaultValue": "We're working on a solution to the recent server issues - in the meantime, random dummy Pokemon data will be generated to demonstrate app functionality. If you are part of our private beta testing group, you can override the default data with a test server URL that we will provide." + }, + { + "type": "toggle", + "messageKey": "UseDataUrl", + "defaultValue": false, + "label": "Override Dummy Data" + }, + { + "type": "input", + "messageKey": "DataUrl", + "defaultValue": "https://pokewatch.github.io/PoGO/unknown6.json", + "label": "Data URL", + "attributes": { + "placeholder": "https://pokewatch.github.io/PoGO/unknown6.json", + "type": "url" + } + }, + { + "type": "submit", + "defaultValue": "Save" + } + ] +} +] \ No newline at end of file diff --git a/src/js/dummyData.js b/src/js/dummyData.js index 02c5ee1..565f43f 100644 --- a/src/js/dummyData.js +++ b/src/js/dummyData.js @@ -8,19 +8,21 @@ module.exports = { var idCounter = 0; for(var i = 0; i < numberOfPokemon; i++) { + // updated to conform to new unknown6 API var pokemon = new Object(); pokemon.id = idCounter++ + ""; - pokemon.expiration_time = (Math.floor((new Date).getTime()/1000)) + (Math.floor(Math.random() * 600)); + // note removal of / 1000 and addition of * 1000 + pokemon.disappear_time = (Math.floor((new Date).getTime())) + (Math.floor(Math.random() * 600 * 1000)); pokemon.longitude = centerLong + ((Math.random() - 0.5) / 100); pokemon.latitude = centerLat + ((Math.random() - 0.5) / 100); pokemon.uid = "{unused}" pokemon.is_alive = true; - pokemon.pokemonId = (Math.floor(Math.random() * (150 - 1 + 1)) + 1).toString(); + pokemon.pokemon_id = (Math.floor(Math.random() * (150 - 1 + 1)) + 1);//.toString(); pokemon.data = "[]"; pokemonList.push(pokemon); } - response.pokemon = pokemonList; + response.pokemons = pokemonList; console.log(JSON.stringify(response)); return response; } diff --git a/src/main.c b/src/main.c index 0709c69..f39e68d 100644 --- a/src/main.c +++ b/src/main.c @@ -169,7 +169,7 @@ static void tick_handler(struct tm *tick_time, TimeUnits units_changed) { // call API on the 15s (for now until distance refresh implemented) //if (tick_time->tm_sec % 15 == 0) { // TODO: restore temporary decrease in frequency when demo mode is no longer nec. - if (tick_time->tm_sec % 30 == 0) { + if (tick_time->tm_sec % 15 == 0) { APP_LOG(APP_LOG_LEVEL_INFO, "tick_handler() 0/15/30/45"); @@ -281,8 +281,23 @@ static void inbox_received_callback(DictionaryIterator *iterator, void *context) nearby[i].sprite = gbitmap_create_with_resource(poke_images[nearby[i].dex]); //strncpy(nearby[0].listBuffer, poke_names[nearby[0].dex], sizeof(nearby[0].listBuffer)); - snprintf(nearby[i].listBuffer, sizeof(nearby[i].listBuffer), "%s\n%d:%02d\n%d m", poke_names[nearby[i].dex], - (int) expiration_delta / 60, (int) expiration_delta % 60, distance); + + // TODO: better + if (expiration_delta < 0) { + // expired pokemon should really be destroyed (or never present in data anyway!), but for now... + snprintf(nearby[i].listBuffer, sizeof(nearby[i].listBuffer), "%s\n-:--\n- m", poke_names[nearby[i].dex]); + } else if (distance > 99999) { + // format "k" if 100km+ (saving two characters vs. " km") + snprintf(nearby[i].listBuffer, sizeof(nearby[i].listBuffer), "%s\n%d:%02d\n%dk", poke_names[nearby[i].dex], + (int) expiration_delta / 60, (int) expiration_delta % 60, distance / 1000); + } else if (distance > 999) { + // format " km" if 1000m+ + snprintf(nearby[i].listBuffer, sizeof(nearby[i].listBuffer), "%s\n%d:%02d\n%d km", poke_names[nearby[i].dex], + (int) expiration_delta / 60, (int) expiration_delta % 60, distance / 1000); + } else { + snprintf(nearby[i].listBuffer, sizeof(nearby[i].listBuffer), "%s\n%d:%02d\n%d m", poke_names[nearby[i].dex], + (int) expiration_delta / 60, (int) expiration_delta % 60, distance); + } nearby[i].angle = bearing * TRIG_MAX_ANGLE / 360; APP_LOG(APP_LOG_LEVEL_DEBUG, "nearby[i].angle: %d", nearby[i].angle); diff --git a/wscript b/wscript index 78aad52..1137474 100644 --- a/wscript +++ b/wscript @@ -38,4 +38,6 @@ def build(ctx): binaries.append({'platform': p, 'app_elf': app_elf}) ctx.set_group('bundle') - ctx.pbl_bundle(binaries=binaries, js=ctx.path.ant_glob('src/js/**/*.js'), js_entry_file='src/js/app.js') + # ctx.pbl_bundle(binaries=binaries, js=ctx.path.ant_glob('src/js/**/*.js'), js_entry_file='src/js/app.js') + # add .json for Clay: + ctx.pbl_bundle(binaries=binaries, js=ctx.path.ant_glob(['src/js/**/*.js', 'src/js/**/*.json']), js_entry_file='src/js/app.js')