|
8 | 8 |
|
9 | 9 | import json |
10 | 10 | from datetime import datetime, timezone |
11 | | -from typing import Optional, AsyncIterator, List, Dict, Union, cast |
| 11 | +from typing import Optional, AsyncIterator, List, Dict, cast |
12 | 12 | from .types import JSONType, PictureMetadata |
13 | 13 |
|
14 | 14 | from .models import Location, PhotoResult |
|
17 | 17 | from .client import FmdClient |
18 | 18 |
|
19 | 19 |
|
20 | | -def _parse_location_blob(blob_b64: str) -> Location: |
21 | | - """Helper to decrypt and parse a location blob into Location dataclass.""" |
22 | | - # This function expects the caller to pass in a client to decrypt; kept here |
23 | | - # for signature clarity in Device methods. |
24 | | - raise RuntimeError("Internal: _parse_location_blob should not be called directly") |
25 | | - |
26 | | - |
27 | 20 | class Device: |
28 | 21 | def __init__(self, client: FmdClient, fmd_id: str, raw: Optional[Dict[str, JSONType]] = None) -> None: |
29 | 22 | self.client = client |
@@ -52,12 +45,21 @@ async def get_location(self, *, force: bool = False) -> Optional[Location]: |
52 | 45 | await self.refresh(force=force) |
53 | 46 | return self.cached_location |
54 | 47 |
|
55 | | - async def get_history( |
56 | | - self, start: Optional[Union[int, datetime]] = None, end: Optional[Union[int, datetime]] = None, limit: int = -1 |
57 | | - ) -> AsyncIterator[Location]: |
| 48 | + async def get_history(self, limit: int = -1) -> AsyncIterator[Location]: |
58 | 49 | """ |
59 | 50 | Iterate historical locations. Uses client.get_locations() under the hood. |
60 | | - Yields decrypted Location objects newest-first (matches get_all_locations when requesting N recent). |
| 51 | +
|
| 52 | + Args: |
| 53 | + limit: Maximum number of locations to return. -1 for all available. |
| 54 | +
|
| 55 | + Yields: |
| 56 | + Decrypted Location objects, newest-first. |
| 57 | +
|
| 58 | + Raises: |
| 59 | + OperationError: If decryption or parsing fails for a location blob. |
| 60 | +
|
| 61 | + Note: |
| 62 | + Time-based filtering (start/end) is not currently supported by the FMD server API. |
61 | 63 | """ |
62 | 64 | # For parity with original behavior, we request num_to_get=limit when limit!=-1, |
63 | 65 | # otherwise request all and stream. |
@@ -144,6 +146,8 @@ async def lock(self, message: Optional[str] = None, passcode: Optional[str] = No |
144 | 146 | # Remove characters that could break command parsing (quotes/backticks/semicolons) |
145 | 147 | for ch in ['"', "'", "`", ";"]: |
146 | 148 | sanitized = sanitized.replace(ch, "") |
| 149 | + # Re-collapse whitespace after removing special chars (may leave gaps) |
| 150 | + sanitized = " ".join(sanitized.split()) |
147 | 151 | # Cap length to 120 chars to avoid overly long command payloads |
148 | 152 | if len(sanitized) > 120: |
149 | 153 | sanitized = sanitized[:120] |
|
0 commit comments