Goal:
- Use voice command (Google Assistant and/or Alexa) to initiate Android phone finder feature
- “Hey Google, turn on Jim’s Phone Finder”
- “Alexa, turn on Bob’s Phone Finder”
- Programmatically initiate phone finder feature
When you have multiple android phones, asking Google to find my phone results in it prompting about each phone one by one. It can get quite tedious. Sometimes it would just ring the first phone.
This solution lets you ring the specific phone using an openhab item.
Requirements:
- selenium standalone docker
- JRuby scripting (it can probably be done in other language too)
Selenium Docker Setup
Using docker-compose.yml:
version: "3"
services:
selenium:
container_name: selenium
image: selenium/standalone-chrome
restart: always
shm_size: 2gb
ports:
- "4444:4444"
Pull the image and start the docker:
$ docker-compose pull selenium
$ docker-compose up -d selenium
Items setup
Create openhab items to receive command and be linked to Google Assistant and Alexa
Group gPhoneFinders
Switch Find_Jims_Phone "Jim's Phone Finder" (gPhoneFinders) {phone="Jim's Phone", expire="3s", ga="Switch", alexa="Switch"}
Switch Find_Bobs_Phone "Bob's Phone Finder" (gPhoneFinders) {phone="Bob's Phone", expire="3s", ga="Switch", alexa="Switch"}
The phone
metadata should contain the phone’s name as displayed on your Find my phone page
Don’t forget to tell Google to Sync my devices
and Alexa to Discover devices
Obtain your google credential cookies
- Open your browser, and go to www.google.com
- Enable “Developer tool” of your browser
- Refresh the google page
- Go to `Network" tab and find the first item. This should be the request to www.google.com
- Find the request header and copy the cookies. It should look like “SIDCC=xxxxx; __Secure-3PSIDCC=xxxxx; blahblah=xxxxx”
- Paste that into the COOKIES in the code below
Rules
Using JRuby:
require 'openhab'
require 'json'
require 'cgi/cookie'
gemfile do
source 'https://rubygems.org'
gem 'selenium-webdriver'
end
GOOGLE_HOME_URL = 'https://www.google.com'
GOOGLE_FIND_URL = 'https://www.google.com/search?q=find+my+phone'
COOKIE_FILE = __conf__ + 'scripts/findmyphone-google-cookie.json'
COOKIES = 'xxxx' # paste your google cookies here
class FindMyPhone
def initialize(server = '127.0.0.1', port = 4444)
options = Selenium::WebDriver::Options.chrome
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-gpu')
@driver = Selenium::WebDriver.for :remote, url: "http://#{server}:#{port}/wd/hub", options: options
@driver.manage.delete_all_cookies
end
def close
@driver.manage.delete_all_cookies
@driver.quit
end
def logged_in?
return false unless @driver.title.include? 'Google'
login_btn = @driver.find_elements(xpath: "//a[contains(@href,'ServiceLogin')]")
if login_btn.nil? || login_btn.empty?
save_cookies
return true
end
false
end
def set_cookies(new_cookies)
CGI::Cookie.parse(new_cookies).each do |name, cookie|
cookie.value.each { |value| @driver.manage.add_cookie(name: name, value: value) }
end
end
def save_cookies
File.write(COOKIE_FILE, @driver.manage.all_cookies.to_json)
end
def load_cookies
return unless File.exist? COOKIE_FILE
JSON.parse(File.read(COOKIE_FILE))
.each { |cookie| @driver.manage.add_cookie(name: cookie['name'], value: cookie['value']) }
end
def refresh
@driver.navigate.refresh
end
def home
@driver.get(GOOGLE_HOME_URL)
end
def find
@driver.get(GOOGLE_FIND_URL)
end
def refresh_cookies
home
load_cookies
refresh
unless logged_in?
logger.info 'Not logged in, setting initial cookies'
set_cookies COOKIES
refresh
end
if logged_in?
save_cookies
logger.info 'Cookies refreshed'
else
logger.warn 'Unable to login. Please update the cookies'
end
end
def ring(phone)
find
dropdown_btn = @driver.find_element(tag_name: '.gws-action__act-device-label')
dropdown_btn.click
device = dropdown_btn.find_element(xpath: %(//*[contains(text(), "#{phone}")]))
device.click
ring_button = @driver.find_element(id: 'act-ring-link')
ring_button.click
sleep 1
end
end
rule 'Find my phone: Refresh cookies' do
every 3.hours
run do
fmp = FindMyPhone.new
fmp.refresh_cookies
ensure
fmp.close
end
end
rule 'Find my phone: Ring' do
received_command gPhoneFinders.members, command: ON
triggered do |item|
fmp = FindMyPhone.new
fmp.refresh_cookies
fmp.ring item.meta['phone'].value
ensure
fmp.close
end
end
Note:
-
this assumes that the Selenium docker is running on the same machine as openhab. If not, adjust the server parameter in the constructor.
-
This was inspired by GitHub - lukeIam/MqttFindMyPhone: Triggers Android FindMyPhone requests via mqtt
-
Need to implement a mutex that serialises the call to selenium to prevent simultaneous finding
-
The cookies are saved in CONF/scripts/ - this contains sensitive information that will allow login to your google account.