diff --git a/findmy/keys.py b/findmy/keys.py index e0febc1..89f9fc0 100644 --- a/findmy/keys.py +++ b/findmy/keys.py @@ -12,7 +12,7 @@ from typing import Generator, Generic, TypeVar, overload from cryptography.hazmat.primitives.asymmetric import ec from typing_extensions import override -from .util import crypto +from .util import crypto, parsers class KeyType(Enum): @@ -71,6 +71,12 @@ class HasPublicKey(HasHashedPublicKey, ABC): """Return the advertised (public) key as a base64-encoded string.""" return base64.b64encode(self.adv_key_bytes).decode("ascii") + @property + def mac_address(self) -> str: + """Get the mac address from the public key.""" + first_byte = (self.adv_key_bytes[0] | 0b11000000).to_bytes(1) + return ":".join([parsers.format_hex_byte(x) for x in first_byte + self.adv_key_bytes[1:6]]) + @property @override def hashed_adv_key_bytes(self) -> bytes: diff --git a/findmy/reports/reports.py b/findmy/reports/reports.py index 3f17be3..931ad40 100644 --- a/findmy/reports/reports.py +++ b/findmy/reports/reports.py @@ -122,6 +122,14 @@ class LocationReport(HasHashedPublicKey): timestamp_int = int.from_bytes(self._payload[0:4], "big") + (60 * 60 * 24 * 11323) return datetime.fromtimestamp(timestamp_int, tz=timezone.utc).astimezone() + @property + def confidence(self) -> int: + """Confidence of the location of this report. Int between 1 and 3.""" + # If the payload length is 88, the confidence is the 5th byte, otherwise it's the 6th byte + if len(self._payload) == 88: + return self._payload[4] + return self._payload[5] + @property def latitude(self) -> float: """Latitude of the location of this report.""" @@ -145,10 +153,10 @@ class LocationReport(HasHashedPublicKey): return struct.unpack(">i", lon_bytes)[0] / 10000000 @property - def confidence(self) -> int: - """Confidence of the location of this report.""" + def horizontal_accuracy(self) -> int: + """Horizontal accuracy of the location of this report.""" if not self.is_decrypted: - msg = "Confidence is unavailable while the report is encrypted." + msg = "Horizontal accuracy is unavailable while the report is encrypted." raise RuntimeError(msg) assert self._decrypted_data is not None diff --git a/findmy/util/parsers.py b/findmy/util/parsers.py index 2f06769..fdd7ecb 100644 --- a/findmy/util/parsers.py +++ b/findmy/util/parsers.py @@ -15,3 +15,8 @@ def decode_plist(data: bytes) -> Any: # noqa: ANN401 data = plist_header + data return plistlib.loads(data) + + +def format_hex_byte(byte: int) -> str: + """Format a byte as a two character hex string in uppercase.""" + return f"{byte:02x}".upper()