Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 58 additions & 30 deletions NovaApi/ExecuteAction/LocateTracker/decrypt_locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@


def create_google_maps_link(latitude, longitude):
try:
try:
latitude = float(latitude)
longitude = float(longitude)
if not (-90 <= latitude <= 90 and -180 <= longitude <= 180):
raise ValueError("Invalid latitude or longitude values.")
except ValueError as e:
return f"Error: {e}" #more descriptive error message for the user
base_url = "https://www.google.com/maps/search/?api=1"
query_params = f"query={latitude},{longitude}"
query_params = f"query={latitude},{longitude}"

return f"{base_url}&{query_params}"

Expand Down Expand Up @@ -67,7 +67,7 @@ def retrieve_identity_key(device_registration: DeviceRegistration) -> bytes:
exit(1)


def decrypt_location_response_locations(device_update_protobuf):
def decrypt_location_response_locations(device_update_protobuf, batch_mode=False):

device_registration = device_update_protobuf.deviceMetadata.information.deviceRegistration

Expand Down Expand Up @@ -124,39 +124,67 @@ def decrypt_location_response_locations(device_update_protobuf):
)
location_time_array.append(wrapped_location)

print("-" * 40)
print("[DecryptLocations] Decrypted Locations:")
if batch_mode is False:
print("-" * 40)
print("[DecryptLocations] Decrypted Locations:")

if not location_time_array:
print("No locations found.")
return
if not location_time_array:
print("No locations found.")
return

for loc in location_time_array:
for loc in location_time_array:

if loc.status == Common_pb2.Status.SEMANTIC:
print(f"Semantic Location: {loc.name}")
if loc.status == Common_pb2.Status.SEMANTIC:
print(f"Semantic Location: {loc.name}")

else:
proto_loc = DeviceUpdate_pb2.Location()
proto_loc.ParseFromString(loc.decrypted_location)

latitude = proto_loc.latitude / 1e7
longitude = proto_loc.longitude / 1e7
altitude = proto_loc.altitude

print(f"Latitude: {latitude}")
print(f"Longitude: {longitude}")
print(f"Altitude: {altitude}")
print(f"Google Maps Link: {create_google_maps_link(latitude, longitude)}")

print(f"Time: {datetime.datetime.fromtimestamp(loc.time).strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Status: {loc.status}")
print(f"Is Own Report: {loc.is_own_report}")
print("-" * 40)
else:
proto_loc = DeviceUpdate_pb2.Location()
proto_loc.ParseFromString(loc.decrypted_location)

latitude = proto_loc.latitude / 1e7
longitude = proto_loc.longitude / 1e7
altitude = proto_loc.altitude

print(f"Latitude: {latitude}")
print(f"Longitude: {longitude}")
print(f"Altitude: {altitude}")
print(f"Google Maps Link: {create_google_maps_link(latitude, longitude)}")

print(f"Time: {datetime.datetime.fromtimestamp(loc.time).strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Status: {loc.status}")
print(f"Is Own Report: {loc.is_own_report}")
print("-" * 40)

else:
# batch mode
if not location_time_array:
print("JSON {}")
return

for loc in location_time_array:
ts=datetime.datetime.fromtimestamp(loc.time).strftime('%Y-%m-%d %H:%M:%S')

if loc.status == Common_pb2.Status.SEMANTIC:
name=loc.name # Semantic Location
lat=None
lon=None
alt=None

else:
proto_loc = DeviceUpdate_pb2.Location()
proto_loc.ParseFromString(loc.decrypted_location)

pass
latitude = proto_loc.latitude / 1e7
longitude = proto_loc.longitude / 1e7
altitude = proto_loc.altitude

name=''
lat=latitude
lon=longitude
alt=altitude
# format https://www.traccar.org/osmand/
print(f'JSON {{"timestamp":"{ts}", "lat":{lat}, "lon":{lon}, "altitude":{alt}, "posname":"{name}"}}')

if __name__ == '__main__':
res = parse_device_update_protobuf("")
decrypt_location_response_locations(res)
decrypt_location_response_locations(res)
6 changes: 3 additions & 3 deletions NovaApi/ExecuteAction/LocateTracker/location_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def create_location_request(canonic_device_id, fcm_registration_id, request_uuid
return hex_payload


def get_location_data_for_device(canonic_device_id, name):
def get_location_data_for_device(canonic_device_id, name, batch_mode=False):

print(f"[LocationRequest] Requesting location data for {name}...")

Expand All @@ -53,7 +53,7 @@ def handle_location_response(response):
while result is None:
asyncio.get_event_loop().run_until_complete(asyncio.sleep(0.1))

decrypt_location_response_locations(result)
decrypt_location_response_locations(result, batch_mode=batch_mode)

if __name__ == '__main__':
get_location_data_for_device(get_example_data("sample_canonic_device_id"), "Test")
get_location_data_for_device(get_example_data("sample_canonic_device_id"), "Test")
68 changes: 46 additions & 22 deletions NovaApi/ListDevices/nbe_list_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# Copyright © 2024 Leon Böttger. All rights reserved.
#

import sys
import argparse
import binascii
from NovaApi.ExecuteAction.LocateTracker.location_request import get_location_data_for_device
from NovaApi.nova_request import nova_request
Expand Down Expand Up @@ -42,34 +44,56 @@ def create_device_list_request():

def list_devices():
print("Loading...")
result_hex = request_device_list()

device_list = parse_device_list_protobuf(result_hex)
got_args = len(sys.argv) > 1
parser = argparse.ArgumentParser()
parser.add_argument('-add', '-a', dest='add', default=None, type=int, required=False, help='how many items to add')
parser.add_argument('-query','-q', dest='query', default=None, type=str, required=False, help='query location by name, comma separated list')
args = parser.parse_args()

# talk to server
result_hex = request_device_list()
device_list = parse_device_list_protobuf(result_hex)
refresh_custom_trackers(device_list)
canonic_ids = get_canonic_ids(device_list)

print("")
print("-" * 50)
print("Welcome to GoogleFindMyTools!")
print("-" * 50)
print("")
print("The following trackers are available:")

for idx, (device_name, canonic_id) in enumerate(canonic_ids, start=1):
print(f"{idx}. {device_name}: {canonic_id}")

selected_value = input("\nIf you want to see locations of a tracker, type the number of the tracker and press 'Enter'.\nIf you want to register a new ESP32- or Zephyr-based tracker, type 'r' and press 'Enter': ")

if selected_value == 'r':
print("Loading...")
register_esp32()
if got_args is False:
# interactive mode
print("")
print("-" * 50)
print("Welcome to GoogleFindMyTools!")
print("-" * 50)
print("")
print("The following trackers are available:")

for idx, (device_name, canonic_id) in enumerate(canonic_ids, start=1):
print(f"{idx}. {device_name}: {canonic_id}")

selected_value = input("\nIf you want to see locations of a tracker, type the number of the tracker and press 'Enter'.\nIf you want to register a new ESP32- or Zephyr-based tracker, type 'r' and press 'Enter': ")

if selected_value == 'r':
print("Loading...")
register_esp32()
else:
selected_idx = int(selected_value) - 1
selected_device_name = canonic_ids[selected_idx][0]
selected_canonic_id = canonic_ids[selected_idx][1]

get_location_data_for_device(selected_canonic_id, selected_device_name)
else:
selected_idx = int(selected_value) - 1
selected_device_name = canonic_ids[selected_idx][0]
selected_canonic_id = canonic_ids[selected_idx][1]

get_location_data_for_device(selected_canonic_id, selected_device_name)
# batch mode
if args.add is not None:
assert (args.add>0) and (args.add<1000), 'add argument out of range'
for _ in range(args.add):
register_esp32(batch_mode=True)

if args.query is not None:
# filter devices by name
assert len(args.query)>0, 'name argument out of range'
for nm in args.query.split(','):
for device_name, canonic_id in canonic_ids:
if nm == device_name:
get_location_data_for_device(canonic_id, device_name, batch_mode=True)


if __name__ == '__main__':
Expand Down
28 changes: 16 additions & 12 deletions SpotApi/CreateBleDevice/create_ble_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,20 @@
from SpotApi.spot_request import spot_request


def register_esp32():
def register_esp32(batch_mode=False):

owner_key = get_owner_key()

eik = secrets.token_bytes(32)
eid = generate_eid(eik, 0)
pair_date = int(time.time())
name = "GFMT-" + secrets.token_hex(5) # phone app uses huge fonts, need to fit screen..

register_request = RegisterBleDeviceRequest()
register_request.fastPairModelId = mcu_fast_pair_model_id

# Description
register_request.description.userDefinedName = "GoogleFindMyTools µC"
register_request.description.userDefinedName = name
register_request.description.deviceType = SpotDeviceType.DEVICE_TYPE_BEACON

# Device Components Information
Expand All @@ -42,7 +43,7 @@ def register_esp32():
register_request.capabilities.capableComponents = 1

# E2EE Registration
register_request.e2eePublicKeyRegistration.rotationExponent = 10
register_request.e2eePublicKeyRegistration.rotationExponent = 10 # dictated by protocol
register_request.e2eePublicKeyRegistration.pairingDate = pair_date

# Encrypted User Secrets
Expand Down Expand Up @@ -80,12 +81,15 @@ def register_esp32():
register_request.unwantedTrackingKey = ownerKeys.tracking_key

bytes_data = register_request.SerializeToString()
spot_request("CreateBleDevice", bytes_data)

print("Registered device successfully. Copy the Advertisement Key below. It will not be shown again.")
print("Afterward, go to the folder 'GoogleFindMyTools/ESP32Firmware' or 'GoogleFindMyTools/ZephyrFirmware' and follow the instructions in the README.md file.")

print("+" + "-" * 78 + "+")
print("|" + " " * 19 + eid.hex() + " " * 19 + "|")
print("|" + " " * 30 + "Advertisement Key" + " " * 31 + "|")
print("+" + "-" * 78 + "+")
resp = spot_request("CreateBleDevice", bytes_data)

if batch_mode is False:
print("Registered device successfully. Copy the Advertisement Key below. It will not be shown again.")
print("Afterward, go to the folder 'GoogleFindMyTools/ESP32Firmware' or 'GoogleFindMyTools/ZephyrFirmware' and follow the instructions in the README.md file.")

print("+" + "-" * 78 + "+")
print("|" + " " * 19 + eid.hex() + " " * 19 + "|")
print("|" + " " * 30 + "Advertisement Key" + " " * 31 + "|")
print("+" + "-" * 78 + "+")
else:
print('JSON {"name":"%s", "eid":"%s"}' % (name, eid.hex()))
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ httpx>=0.28.0
h2>=4.1.0
setuptools>=75.6.0
aiohttp>=3.11.8
http_ece>=1.1.0
http_ece>=1.1.0
argparse>=1.4.0