├── package.json ├── config-sample.json ├── .gitignore ├── readme.md └── index.js /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alexa-app_find-my-phones", 3 | "version": "1.0.0", 4 | "description": "Ask amazon echo to find any icloud device, and send an alarm", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "alexa": { 10 | "applicationId": "amzn1.echo-sdk-ams.app.XXXX" 11 | }, 12 | "author": "Jed Lippold", 13 | "license": "ISC", 14 | "dependencies": { 15 | "alexa-app": "^2.2.0", 16 | "find-my-iphone": "git+https://github.com/jlippold/find-my-iphone.git", 17 | "moment": "^2.11.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /config-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "app_name": "iCloud", 3 | "apple_id": "obama@whitehouse.com", 4 | "apple_password": "obamaCare4life", 5 | "latitude": 38.8977, 6 | "longitude": 77.0366, 7 | "deviceMap": { 8 | "my wife": { 9 | "deviceName": "Michelles iPhone 10" 10 | }, 11 | "my daughter": { 12 | "deviceName": "Sasha iPhone 3g" 13 | }, 14 | "my phone": { 15 | "deviceName": "Baracks iPhone 12" 16 | }, 17 | "sasha": { 18 | "deviceName": "Sasha iPhone 3g" 19 | }, 20 | "my first lady": { 21 | "deviceName": "Michelles iPhone 10" 22 | }, 23 | "air force one": { 24 | "deviceName": "Baracks iPad" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config.json 2 | 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directory 30 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 31 | node_modules 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | This app allows you to ask Alexa to locate devices in your apple iCloud account. 4 | 5 | For example, 6 | 7 | `You`: Alexa, ask iCloud to find my phone 8 | 9 | `Alexa`: As of a few seconds ago, your phone is less than 10 feet away 10 | 11 | ------ 12 | 13 | `You`: Alexa, ask iCloud to find my wife 14 | 15 | `Alexa`: As of a few seconds ago, your wife is 3 miles away, nearby 1600 Pennsylvania Ave in Washington, DC. Approximate driving time: 2 hours and 30 minutes 16 | 17 | ------ 18 | 19 | `You`: Alexa, ask iCloud to alert my phone 20 | 21 | `Alexa`: Your phone will now beep. 22 | 23 | 24 | # Install 25 | 26 | Get the [alexa-app-server](https://github.com/matt-kruse/alexa-app-server) running on your server. Follow the instructions in the alexa-app-server readme. 27 | 28 | Download this repo, and copy it to `apps/findmyphone` and run `npm install` 29 | 30 | * Rename or copy the `config-sample.json` to `config.json` 31 | * Edit the contents accordingly with your iCloud user/pass 32 | * Edit the latitude/longitude of your home, so that the app can measure distance 33 | * Add your devices to the deviceMap object; each key is the name you want to call the device 34 | 35 | ### Connect with amazon 36 | 37 | * Create a developers account on Amazon 38 | * Create a new skill 39 | * Name it whatever 40 | * Invocation name can be `iCloud` 41 | * Intents and utterances can be determined by going to http://localhost:8080/alexa/findmyphone 42 | * Configure SSL, and test the app via the Amazon website 43 | * At this point you can try it on your Echo -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var alexa = require('alexa-app'); 2 | var util = require('util'); 3 | var moment = require('moment'); 4 | var config = require('./config.json'); 5 | var app = new alexa.app('findmyphone'); 6 | 7 | var icloud = require("find-my-iphone").findmyphone; 8 | 9 | icloud.apple_id = config.apple_id; 10 | icloud.password = config.apple_password; 11 | 12 | module.exports = app; 13 | module.change_code = 1; 14 | 15 | var alias = Object.keys(config.deviceMap).join("|"); 16 | 17 | app.intent('FindPhoneIntent', { 18 | "slots": { 19 | "device": "LITERAL" 20 | }, 21 | "utterances": ["find {" + alias + "|device}"] 22 | }, function(request, response) { 23 | 24 | var saidDevice = request.slot('device'); 25 | var possesiveDevice = saidDevice.replace("my", "your"); 26 | var invocation = config.app_name; 27 | var errMsg, msg; 28 | 29 | var instructions = util.format("You can also say, Alexa, ask %s to alert %s", invocation, saidDevice); 30 | 31 | if (!config.deviceMap.hasOwnProperty(saidDevice)) { 32 | errMsg = util.format("Sorry, I cannot find %s in your device list", possesiveDevice); 33 | return replyWithMessage(response, errMsg); 34 | } 35 | 36 | var deviceName = config.deviceMap[saidDevice].deviceName; 37 | 38 | icloud.getDevices(function(error, devices) { 39 | 40 | if (error) { 41 | errMsg = "Something when wrong when contacting iCloud" 42 | console.error(error); 43 | return replyWithMessage(response, errMsg); 44 | } 45 | 46 | var device; 47 | 48 | devices.forEach(function(d) { 49 | if (d.name == deviceName) { 50 | device = d; 51 | } 52 | }); 53 | 54 | if (device) { 55 | 56 | if (device.location == null) { 57 | msg = util.format("%s is currenly being located, ask me again in a few seconds.", possesiveDevice); 58 | return replyWithMessage(response, msg); 59 | } 60 | 61 | var myLatitude = config.latitude; 62 | var myLongitude = config.longitude; 63 | 64 | icloud.getDistanceOfDevice(device, myLatitude, myLongitude, function(err, result) { 65 | 66 | if (result && result.distance && result.distance.value) { 67 | 68 | var meters = result.distance.value; 69 | var miles = Math.floor(meters * 0.000621371192); 70 | var feet = Math.floor(meters * 3.28084); 71 | 72 | msg = ""; 73 | 74 | if (device.location.timeStamp) { 75 | var lastLocated = moment(device.location.timeStamp); 76 | var now = moment(); 77 | var lastSeen = moment.duration(now.diff(lastLocated)).humanize(); 78 | msg = "As of " + lastSeen + " ago, "; 79 | } 80 | 81 | if (feet <= 1000) { 82 | msg = util.format("%s %s is probably in the house, only %d feet away. %s", 83 | msg, possesiveDevice, feet, instructions); 84 | 85 | return replyWithMessage(response, msg); 86 | } else { 87 | 88 | if (miles < 1) { 89 | msg = util.format("%s %s is %d feet away. %s", msg, possesiveDevice, feet, instructions); 90 | return replyWithMessage(response, msg); 91 | } else { 92 | msg = util.format("%s %s is %d miles away", msg, possesiveDevice, miles); 93 | 94 | icloud.getLocationOfDevice(device, function(err, location) { 95 | if (location) { 96 | msg = util.format("%s, near %s", msg, location); 97 | if (result.duration) { 98 | msg = util.format("%s. Approximate driving time %s", msg, result.duration.text); 99 | } 100 | } 101 | return replyWithMessage(response, msg); 102 | }); 103 | } 104 | } 105 | } else { 106 | return replyWithMessage(response, "Sorry, I can not calculate the distance of this device."); 107 | } 108 | }); 109 | } else { 110 | errMsg = util.format("The device %s was not found on your iCloud account", deviceName); 111 | return replyWithMessage(response, errMsg); 112 | } 113 | }); 114 | 115 | return false; 116 | }); 117 | 118 | app.intent('AlertPhoneIntent', { 119 | "slots": { 120 | "device": "LITERAL" 121 | }, 122 | "utterances": ["alert {" + alias + "|device}"] 123 | }, function(request, response) { 124 | 125 | var saidDevice = request.slot('device'); 126 | var possesiveDevice = saidDevice.replace("my", "your"); 127 | var errMsg, msg; 128 | 129 | if (!config.deviceMap.hasOwnProperty(saidDevice)) { 130 | errMsg = util.format("Sorry, I cannot find %s in your device list", possesiveDevice); 131 | return replyWithMessage(response, errMsg); 132 | } 133 | 134 | var deviceName = config.deviceMap[saidDevice].deviceName; 135 | var device; 136 | 137 | icloud.getDevices(function(error, devices) { 138 | if (error) { 139 | console.log(error); 140 | return replyWithMessage(response, "Something when wrong when contacting iCloud"); 141 | } 142 | 143 | devices.forEach(function(d) { 144 | if (device == undefined && d.lostModeCapable && d.name == deviceName) { 145 | device = d; 146 | } 147 | }); 148 | 149 | if (device) { 150 | icloud.alertDevice(device.id, function(err) { 151 | msg = util.format("%s will now beep", possesiveDevice); 152 | return replyWithMessage(response, msg); 153 | }); 154 | } else { 155 | errMsg = util.format("The device %s was not found on your iCloud account", deviceName); 156 | return replyWithMessage(response, errMsg); 157 | } 158 | }); 159 | 160 | return false; 161 | }); 162 | 163 | function replyWithMessage(response, message) { 164 | return response.say(message).shouldEndSession(true).send(); 165 | } --------------------------------------------------------------------------------