-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Nextcloud PhoneTrack App Intergration & visualization #11
Comments
That's a great proposal! I unfortunately do not have cycles in the near future to look into this. But anyone is welcome to contribute and make pull requests. |
Chiming in to say this integration would be awesome. Unfortunately don't have the skill set to make it happen. Will be following this one for future reference though. |
Here is a very quick-and-dirty phonetrack integration:
Note: I will NOT provide any support for this. Use at your own risk. |
I wanted to circle back after trying this out, but now so much time has passed, and it's still sitting on my to-do list. Figured I'd belatedly circle back and say thanks for the response. It's more than enough to play around with / build upon. |
I modded the code to work.. And have been out of town.. I will try to send you what I made to get it working its been running since a week after I asked. I frankly completely forgot about it.
…On December 7, 2023 1:41:05 PM PST, Chris ***@***.***> wrote:
I wanted to circle back after trying this out, but now so much time has passed, and it's still sitting on my to-do list. Figured I'd belatedly circle back and say thanks for the response. It's more than enough to play around with / build upon.
--
Reply to this email directly or view it on GitHub:
#11 (comment)
You are receiving this because you authored the thread.
Message ID: ***@***.***>
|
Awesome, no rush, but would definitely appreciate it. |
Hey Mitchel, I would love to have the modded code, I just thought I would reply to remind you in case you forgot. Thank you.
|
You had Perfect timing... I am sitting in the office... going back out of
town...
Sorry this is not in DIFF format...
Again this is to send airTag information to nextcloud Phonetrack app
https://gitlab.com/eneiluj/phonetrack-oc/-/wikis/home
Use the Session Token you get from PhoneTrack
I didn't make the URL for the server or the Session Token Variables...
sorry..
I had to add a check for change or it spams the server with 1,000 of the
same datapoint.
It's not Perfect but it's been running for 6mths.
Copyright of changes are under whatever license the existing code is.
import argparse
import time
import os
import curses
import requests
import urllib.request
from subprocess import check_call as shell_cmd
from datetime import datetime
from tabulate import tabulate
from urllib.parse import urlencode
from urllib.request import urlopen
from lib.constants import JSON_LAYER_SEPARATOR
from lib.constants import FINDMY_FILES
from lib.constants import NAME_SEPARATOR
from lib.constants import JSON_LAYER_SEPARATOR
from lib.constants import NULL_STR
from lib.constants import TIME_FORMAT
from lib.constants import DATE_FORMAT
from lib.log_manager import LogManager
def parse_args():
parser = argparse.ArgumentParser(
description='Record Apple findmy history for Apple devices.')
parser.add_argument(
'--refresh',
type=int,
action='store',
default=100,
help='Refresh interval (ms).')
parser.add_argument(
'--name_keys',
type=str,
action='append',
default=['name', 'serialNumber'],
help='Keys used to construct the filename for each device.')
parser.add_argument(
'--store_keys',
type=str,
action='append',
default=['name', 'batteryLevel', 'batteryStatus', 'batteryLevel',
f'location{JSON_LAYER_SEPARATOR}timeStamp',
f'location{JSON_LAYER_SEPARATOR}latitude',
f'location{JSON_LAYER_SEPARATOR}longitude',
f'location{JSON_LAYER_SEPARATOR}verticalAccuracy',
f'location{JSON_LAYER_SEPARATOR}horizontalAccuracy',
f'location{JSON_LAYER_SEPARATOR}altitude',
f'location{JSON_LAYER_SEPARATOR}positionType',
f'location{JSON_LAYER_SEPARATOR}floorLevel',
f'location{JSON_LAYER_SEPARATOR}isInaccurate',
f'location{JSON_LAYER_SEPARATOR}isOld',
f'location{JSON_LAYER_SEPARATOR}locationFinished',
'id', 'deviceDiscoveryId', 'baUUID', 'serialNumber',
'identifier', 'prsId',
'deviceModel', 'modelDisplayName', 'deviceDisplayName'],
help='Keys to log.')
parser.add_argument(
'--timestamp_key',
type=str,
action='store',
default=f'location{JSON_LAYER_SEPARATOR}timeStamp',
help='The key of timestamp in findmy JSON')
parser.add_argument(
'--log_folder',
type=str,
action='store',
default='log',
help='The path of log folder.')
parser.add_argument(
'--no_date_folder',
action='store_true',
help='By default, the logs of each day will be saved in a separated '
'folder. Use this option to turn it off.')
parser.add_argument(
'--server_url',
type=str,
action='store',
default='https://[Replace with nextcloud
Server]/apps/phonetrack/logGet/[Replace with phonetrack Session
Token]/',
help='The URL of the server to which the data is to be sent')
args = parser.parse_args()
return args
def send_to_server(server_url, log):
"""
Send the log data to a server via HTTP GET request using query string
"""
if log['serialNumber'] is None:
log['serialNumber']=log['name']
server_url = f"https://[Replace with nextcloud
Server]/apps/phonetrack/logGet/[Replace with phonetrack Session
Token]/{log['serialNumber']}" # replace with your server URL
query_string = {
"lat": log[f'location{JSON_LAYER_SEPARATOR}latitude'],
"lon": log[f'location{JSON_LAYER_SEPARATOR}longitude'],
"alt": log[f'location{JSON_LAYER_SEPARATOR}altitude'],
"acc": log[f'location{JSON_LAYER_SEPARATOR}horizontalAccuracy'],
"bat": log["batteryLevel"],
"sat": log[f'location{JSON_LAYER_SEPARATOR}verticalAccuracy'],
"speed": log[f'location{JSON_LAYER_SEPARATOR}positionType'],
"bearing": "bearing",
"timestamp": log[f'location{JSON_LAYER_SEPARATOR}timeStamp'],
# add more key-value pairs as necessary
}
query_params = urlencode(query_string)
req = urllib.request.Request(
f"{server_url}?{query_params}",
data=None,
headers={
'User-Agent': '{log[name]}'
}
)
response=""
try:
response = urllib.request.urlopen(req)
#print(f"{server_url}?{query_params}")
#print(f"{server_url}?{query_params}")
#print(f"Data sent to server with response code: {response.code}")
return response
except:
#print(f"{server_url}?{query_params}")
#print("Failed to send data to server")
return response
def main(stdscr):
stdscr.clear()
args = parse_args()
log_manager = LogManager(
findmy_files=[os.path.expanduser(f) for f in FINDMY_FILES],
store_keys=args.store_keys,
timestamp_key=args.timestamp_key,
log_folder=args.log_folder,
name_keys=args.name_keys,
name_separator=NAME_SEPARATOR,
json_layer_separator=JSON_LAYER_SEPARATOR,
null_str=NULL_STR,
date_format=DATE_FORMAT,
no_date_folder=args.no_date_folder)
server_url = args.server_url
prev_timestamps = {}
while True:
log_manager.refresh_log()
latest_log, log_cnt = log_manager.get_latest_log()
table = []
for name, log in latest_log.items():
latest_time = log[args.timestamp_key]
if isinstance(latest_time, int) or isinstance(latest_time, float):
latest_time = datetime.fromtimestamp(
float(latest_time) / 1000.)
latest_time = latest_time.strftime(TIME_FORMAT)
# Check if the timestamp has changed since the last update
if prev_timestamps.get(name) != latest_time:
result = send_to_server(server_url, log)
prev_timestamps[name] = latest_time
table.append([name, latest_time, log_cnt[name]])
table = tabulate(
table,
headers=['Name', 'Last update', 'Log count'],
tablefmt="github")
stdscr.erase()
try:
stdscr.addstr(
0, 0, f'Current time: {datetime.now().strftime(TIME_FORMAT)}')
stdscr.addstr(1, 0, table)
except:
pass
stdscr.refresh()
time.sleep(float(args.refresh) / 1000)
if __name__ == "__main__":
try:
shell_cmd("open -gja /System/Applications/FindMy.app", shell=True)
except:
#Maybe Apple changed the name or the dir of the app?
pass
curses.wrapper(main)
Mitch
…On Sat, Dec 23, 2023 at 3:10 PM DShakar ***@***.***> wrote:
Hey Mitchel, I would love to have the modded code, I just thought I would
reply to remind you in case you forgot. Thank you.
I modded the code to work.. And have been out of town.. I will try to send
you what I made to get it working its been running since a week after I
asked. I frankly completely forgot about it.
… <#m_-7143903286148740668_>
On December 7, 2023 1:41:05 PM PST, Chris *@*.*> wrote: I wanted to
circle back after trying this out, but now so much time has passed, and
it's still sitting on my to-do list. Figured I'd belatedly circle back and
say thanks for the response. It's more than enough to play around with /
build upon. -- Reply to this email directly or view it on GitHub: #11
(comment)
<#11 (comment)>
You are receiving this because you authored the thread. Message ID: @.*>
—
Reply to this email directly, view it on GitHub
<#11 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/APBEZBWW2SYXWTRGINAIVL3YK5QERAVCNFSM6AAAAAAWPFHB2GVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQNRYGM4DMNBXGU>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Just remembered... There is no auto correcting of AIR tag names. You can
create an air tag name that will be invalid i don't have an example since I
renamed all 20 airtags we use rather than fix the code to autocorrect...
Be sure to add that to the notes!! or normalize(that's the word I was
looking for) all the names before submitting to PhoneTrack!
Thanks for the work, I hope others can use this...
also sometimes it gets strange CORDS and well it is what it is... lol
Mitch
…On Sat, Dec 23, 2023 at 4:42 PM Mitch Hicks ***@***.***> wrote:
You had Perfect timing... I am sitting in the office... going back out of
town...
Sorry this is not in DIFF format...
Again this is to send airTag information to nextcloud Phonetrack app
https://gitlab.com/eneiluj/phonetrack-oc/-/wikis/home
Use the Session Token you get from PhoneTrack
I didn't make the URL for the server or the Session Token Variables...
sorry..
I had to add a check for change or it spams the server with 1,000 of the
same datapoint.
It's not Perfect but it's been running for 6mths.
Copyright of changes are under whatever license the existing code is.
import argparse
import time
import os
import curses
import requests
import urllib.request
from subprocess import check_call as shell_cmd
from datetime import datetime
from tabulate import tabulate
from urllib.parse import urlencode
from urllib.request import urlopen
from lib.constants import JSON_LAYER_SEPARATOR
from lib.constants import FINDMY_FILES
from lib.constants import NAME_SEPARATOR
from lib.constants import JSON_LAYER_SEPARATOR
from lib.constants import NULL_STR
from lib.constants import TIME_FORMAT
from lib.constants import DATE_FORMAT
from lib.log_manager import LogManager
def parse_args():
parser = argparse.ArgumentParser(
description='Record Apple findmy history for Apple devices.')
parser.add_argument(
'--refresh',
type=int,
action='store',
default=100,
help='Refresh interval (ms).')
parser.add_argument(
'--name_keys',
type=str,
action='append',
default=['name', 'serialNumber'],
help='Keys used to construct the filename for each device.')
parser.add_argument(
'--store_keys',
type=str,
action='append',
default=['name', 'batteryLevel', 'batteryStatus', 'batteryLevel',
f'location{JSON_LAYER_SEPARATOR}timeStamp',
f'location{JSON_LAYER_SEPARATOR}latitude',
f'location{JSON_LAYER_SEPARATOR}longitude',
f'location{JSON_LAYER_SEPARATOR}verticalAccuracy',
f'location{JSON_LAYER_SEPARATOR}horizontalAccuracy',
f'location{JSON_LAYER_SEPARATOR}altitude',
f'location{JSON_LAYER_SEPARATOR}positionType',
f'location{JSON_LAYER_SEPARATOR}floorLevel',
f'location{JSON_LAYER_SEPARATOR}isInaccurate',
f'location{JSON_LAYER_SEPARATOR}isOld',
f'location{JSON_LAYER_SEPARATOR}locationFinished',
'id', 'deviceDiscoveryId', 'baUUID', 'serialNumber',
'identifier', 'prsId',
'deviceModel', 'modelDisplayName', 'deviceDisplayName'],
help='Keys to log.')
parser.add_argument(
'--timestamp_key',
type=str,
action='store',
default=f'location{JSON_LAYER_SEPARATOR}timeStamp',
help='The key of timestamp in findmy JSON')
parser.add_argument(
'--log_folder',
type=str,
action='store',
default='log',
help='The path of log folder.')
parser.add_argument(
'--no_date_folder',
action='store_true',
help='By default, the logs of each day will be saved in a separated '
'folder. Use this option to turn it off.')
parser.add_argument(
'--server_url',
type=str,
action='store',
default='https://[Replace with nextcloud Server]/apps/phonetrack/logGet/[Replace with phonetrack Session Token]/',
help='The URL of the server to which the data is to be sent')
args = parser.parse_args()
return args
def send_to_server(server_url, log):
"""
Send the log data to a server via HTTP GET request using query string
"""
if log['serialNumber'] is None:
log['serialNumber']=log['name']
server_url = f"https://[Replace with nextcloud Server]/apps/phonetrack/logGet/[Replace with phonetrack Session Token]/{log['serialNumber']}" # replace with your server URL
query_string = {
"lat": log[f'location{JSON_LAYER_SEPARATOR}latitude'],
"lon": log[f'location{JSON_LAYER_SEPARATOR}longitude'],
"alt": log[f'location{JSON_LAYER_SEPARATOR}altitude'],
"acc": log[f'location{JSON_LAYER_SEPARATOR}horizontalAccuracy'],
"bat": log["batteryLevel"],
"sat": log[f'location{JSON_LAYER_SEPARATOR}verticalAccuracy'],
"speed": log[f'location{JSON_LAYER_SEPARATOR}positionType'],
"bearing": "bearing",
"timestamp": log[f'location{JSON_LAYER_SEPARATOR}timeStamp'],
# add more key-value pairs as necessary
}
query_params = urlencode(query_string)
req = urllib.request.Request(
f"{server_url}?{query_params}",
data=None,
headers={
'User-Agent': '{log[name]}'
}
)
response=""
try:
response = urllib.request.urlopen(req)
#print(f"{server_url}?{query_params}")
#print(f"{server_url}?{query_params}")
#print(f"Data sent to server with response code: {response.code}")
return response
except:
#print(f"{server_url}?{query_params}")
#print("Failed to send data to server")
return response
def main(stdscr):
stdscr.clear()
args = parse_args()
log_manager = LogManager(
findmy_files=[os.path.expanduser(f) for f in FINDMY_FILES],
store_keys=args.store_keys,
timestamp_key=args.timestamp_key,
log_folder=args.log_folder,
name_keys=args.name_keys,
name_separator=NAME_SEPARATOR,
json_layer_separator=JSON_LAYER_SEPARATOR,
null_str=NULL_STR,
date_format=DATE_FORMAT,
no_date_folder=args.no_date_folder)
server_url = args.server_url
prev_timestamps = {}
while True:
log_manager.refresh_log()
latest_log, log_cnt = log_manager.get_latest_log()
table = []
for name, log in latest_log.items():
latest_time = log[args.timestamp_key]
if isinstance(latest_time, int) or isinstance(latest_time, float):
latest_time = datetime.fromtimestamp(
float(latest_time) / 1000.)
latest_time = latest_time.strftime(TIME_FORMAT)
# Check if the timestamp has changed since the last update
if prev_timestamps.get(name) != latest_time:
result = send_to_server(server_url, log)
prev_timestamps[name] = latest_time
table.append([name, latest_time, log_cnt[name]])
table = tabulate(
table,
headers=['Name', 'Last update', 'Log count'],
tablefmt="github")
stdscr.erase()
try:
stdscr.addstr(
0, 0, f'Current time: {datetime.now().strftime(TIME_FORMAT)}')
stdscr.addstr(1, 0, table)
except:
pass
stdscr.refresh()
time.sleep(float(args.refresh) / 1000)
if __name__ == "__main__":
try:
shell_cmd("open -gja /System/Applications/FindMy.app", shell=True)
except:
#Maybe Apple changed the name or the dir of the app?
pass
curses.wrapper(main)
Mitch
On Sat, Dec 23, 2023 at 3:10 PM DShakar ***@***.***> wrote:
> Hey Mitchel, I would love to have the modded code, I just thought I would
> reply to remind you in case you forgot. Thank you.
>
> I modded the code to work.. And have been out of town.. I will try to
> send you what I made to get it working its been running since a week after
> I asked. I frankly completely forgot about it.
> … <#m_-2237118060453700791_m_-7143903286148740668_>
> On December 7, 2023 1:41:05 PM PST, Chris *@*.*> wrote: I wanted to
> circle back after trying this out, but now so much time has passed, and
> it's still sitting on my to-do list. Figured I'd belatedly circle back and
> say thanks for the response. It's more than enough to play around with /
> build upon. -- Reply to this email directly or view it on GitHub: #11
> (comment)
> <#11 (comment)>
> You are receiving this because you authored the thread. Message ID: @.*>
>
> —
> Reply to this email directly, view it on GitHub
> <#11 (comment)>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/APBEZBWW2SYXWTRGINAIVL3YK5QERAVCNFSM6AAAAAAWPFHB2GVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQNRYGM4DMNBXGU>
> .
> You are receiving this because you authored the thread.Message ID:
> ***@***.***>
>
|
I think this would provide an easy visual interface for people to follow the AirTags.
This is the Nextcloud App
https://apps.nextcloud.com/apps/phonetrack
This is the Page for Clients to send updates to the APP.
https://gitlab.com/eneiluj/phonetrack-oc/-/wikis/userdoc#logging-methods
It has a built in Post/Get option for adding History Points I am sure there are other options.
"HTTP request
You can build your own logging system and make GET or POST HTTP requests to PhoneTrack.
Here is an example of logging URL with POST:
https://your.server.org/NC_PATH_IF_NECESSARY/index.php/apps/phonetrack/logPost/TOKEN/DEVNAME
and with GET:
https://your.server.org/NC_PATH_IF_NECESSARY/index.php/apps/phonetrack/logGet/TOKEN/DEVNAME
The POST or GET parameters are:
lat (decimal latitude)
lon (decimal longitude)
alt (altitude in meters)
timestamp (epoch timestamp in seconds)
acc (accuracy in meters)
bat (battery level in percent)
sat (number of satellites)
useragent (device user agent)
speed (speed in meter per second)
bearing (bearing in decimal degrees)
"
Great Work on the Apple side..
The text was updated successfully, but these errors were encountered: