Hi all,
In another topic I have been reporting on an implementation of the Find My iPhone service in LUA. I have improved it a bit further, and thought it deserved a separate topic.
What does it do?
This script checks the location of one or more iPhones using Apples Find My iPhone service. This funtion first has to be enabled on each phone (if this has not already been done). You have to put the iCloud credentials of every user in the script. Don't worry, the information is sent over the internet using a secure https connection, so your credentials are not out in the open. You can do several things with the location info. At the moment, I have implemented two functions:
- Activate a presence switch if the phone is within a certain radius from your home
- Do a reverse address lookup and log the current whereabouts (address) of each phone in a dummy tect device, along with the distance from home

The default polling time is 5 minutes. If you feel this is draining your battery (I'm still investigating this myself) you can easily increase this. We could even make this into a dynamic value, i.e. different polling times depending on location or time.
Where does this come from? (And where could it lead to...?)
I wasn't happy with the existing presence awareness techniques described around here, such as WiFi or Bluetooth detection scripts. I simply had too many false positives or negatives, usually a couple a day. I started to look for alternatives, and since we all have iPhones at home, the Find My iPhone service had my special attention. It didn't take long before I found a basic LUA script from Jason Gill on the MiCasaVerde forum. I created my own implementation based on this code, including multiple accounts functionality and switching of Domoticz devices. Interesting note: At MiCasaVerde, Jason's original idea eventually resulted in a very nice plugin, complete with UI to fill in user data, set options and show locations on a map. Wouldn't it be great if we could one day create our own Domoticz plugin based on this?
How to install
There are four steps:
- Activate Find My iPhone on all phones you want to keep track of (usually this is already standard on)
- Download a lua JSON decoder, e.g. this one: http://regex.info/blog/lua/json and copy the file (json.lua) somewhere where it can be found (in my case this was /usr/local/share/lua/5.2/). I think you can also append a path after the "require" command in the script below.
- Create a dummy switch named "iPhone Person1" for each iPhone you want to track (where "Person1" is replaced with the name of the person). Also create a dummy text device for each iPhone named "Position Person1" (again replacing "Person1" with the person's name). If you want (and haven't already done so), configure one or more notification services, then you will be informed when someone leaves the house or comes home.
- Create the following script and put this e.g. in "~/domoticz/scripts/lua/script_time_checkphones.lua"
Replace the following data with your own:
Code: Select all
-- Script to check the location of multiple iPhones every X minutes, -- test if they are "home" and represent this using virtual switches commandArray = {} -- polling interval in minutes (1-59), setting this too low may drain the phones' batteries interval = 5 local m = os.date('%M') if (m % interval == 0) then json = require('json') -- Array of users to be checked users = { Person1 = {username = "[email protected]" ; password = "password1" ; devicename = "iPhone Person1"}; Person2 = {username = "[email protected]" ; password = "password2" ; devicename = "iPhone Person2"}; Person3 = {username = "[email protected]" ; password = "password3" ; devicename = "iPhone Person3"} } -- The latitude and longitude of your house (use Google Maps or similar to find this) homelongitude = 1.23456789 homelatitude = 9.87654321 -- Radius (in km) which will be used to determine if a device is at home radius = 0.5 function address(longitude, latitude) command = "curl -s https://maps.googleapis.com/maps/api/geocode/json?latlng=" .. latitude .. "," .. longitude .. "&sensor=false" local handle = io.popen(command) local result = handle:read("*a") handle:close() output = json:decode(result) return output.results[1].formatted_address end for user,credentials in pairs(users) do stage1command = "curl -s -X POST -D - -o /dev/null -L -u '" .. credentials.username .. ":" .. credentials.password .. "' -H 'Content-Type: application/json; charset=utf-8' -H 'X-Apple-Find-Api-Ver: 2.0' -H 'X-Apple-Authscheme: UserIdGuest' -H 'X-Apple-Realm-Support: 1.0' -H 'User-agent: Find iPhone/1.3 MeKit (iPad: iPhone OS/4.2.1)' -H 'X-Client-Name: iPad' -H 'X-Client-UUID: 0cf3dc501ff812adb0b202baed4f37274b210853' -H 'Accept-Language: en-us' -H 'Connection: keep-alive' https://fmipmobile.icloud.com/fmipservice/device/" .. credentials.username .."/initClient" local handle = io.popen(stage1command) local result = handle:read("*a") handle:close() stage2server = string.match(result, "X%-Apple%-MMe%-Host%:%s(.*%.icloud%.com)") stage2command = "curl -s -X POST -L -u '" .. credentials.username .. ":" .. credentials.password .. "' -H 'Content-Type: application/json; charset=utf-8' -H 'X-Apple-Find-Api-Ver: 2.0' -H 'X-Apple-Authscheme: UserIdGuest' -H 'X-Apple-Realm-Support: 1.0' -H 'User-agent: Find iPhone/1.3 MeKit (iPad: iPhone OS/4.2.1)' -H 'X-Client-Name: iPad' -H 'X-Client-UUID: 0cf3dc501ff812adb0b202baed4f37274b210853' -H 'Accept-Language: en-us' -H 'Connection: keep-alive' https://" .. stage2server .. "/fmipservice/device/" .. credentials.username .."/initClient" local handle = io.popen(stage2command) local result = handle:read("*a") handle:close() output = json:decode(result) for key,value in pairs(output.content) do if value.name == credentials.devicename then lon = value.location.longitude lat = value.location.latitude distance = math.sqrt(((lon - homelongitude) * 111.320 * math.cos(math.rad(lat)))^2 + ((lat - homelatitude) * 110.547)^2) -- approximation position = address(lon,lat) position_text = string.gsub(position, ', Netherlands', '') .. ' (' .. (math.floor(distance*10+0.5)/10) .. ' km)' prev_distance_str = string.match(otherdevices['Position ' .. user], '%(.*%)') or '(1000 km)' prev_distance = tonumber(string.sub(prev_distance_str, 2,-5)) -- update text device, but only if postion has changed more than defined in "radius" to reduce log size if math.abs(prev_distance - distance) > radius then table.insert(commandArray,{['UpdateDevice'] = otherdevices_idx['Position ' .. user] .. '|0|' .. position_text}) end --print('iPhone ' .. user .. ': ' .. math.floor(distance*100+0.5)/100 .. ' km from home') if distance < radius then if otherdevices['iPhone ' .. user] == 'Off' then commandArray['iPhone ' .. user] = 'On' table.insert(commandArray, {['SendNotification'] = 'Presence update#' .. user .. ' came home'}) end else if otherdevices['iPhone ' .. user] == 'On' then commandArray['iPhone ' .. user] = 'Off' table.insert(commandArray, {['SendNotification'] = 'Presence update#' .. user .. ' left home'}) end end end end end end return commandArray
- The value of "interval" (number of minutes between polls)
- The values in the "users" array. Replace "Person1" etc. with the name(s) of the person(s) and fill in the credentials. The device name is the name you gave each iPhone during its configuration. This name has to be filled in exactly right (note the use of capitals etc.). You can add as many users as you want in new lines (pay attention to the correct placement of brackets, parentheses etc.).
- The "homelongitude" and "homelatitude" values (the position of your house)
- The value of "radius". This is the radius around your house (in km) to call something "at home". Don't choose this value too low, this could lead to intermittent "left home" - "came home" events since the GPS accuracy is sometimes limited.
- (optional) I put some code in the script to remove the text "Netherlands" from the address to keep it compact. Change this into your own country if you want.