Skip to content

Commit 65ab3e4

Browse files
committed
Require a PIN for device wipe method
1 parent 153595e commit 65ab3e4

File tree

4 files changed

+25
-8
lines changed

4 files changed

+25
-8
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ Tips:
123123
- `await device.get_picture_blobs(n)` + `await device.decode_picture(blob)`
124124
- Commands: `await device.play_sound()`, `await device.take_front_picture()`,
125125
`await device.take_rear_picture()`, `await device.lock(message=None)`,
126-
`await device.wipe(confirm=True)`
126+
`await device.wipe(pin="1234", confirm=True)`
127+
Note: wipe requires the FMD PIN and must be enabled in the Android app's General settings.
127128

128129
### Example: Lock device with a message
129130

docs/MIGRATE_FROM_V1.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ client = await FmdClient.create("https://fmd.example.com", "alice", "secret")
6767
|----|----------------|-------------|-------|
6868
| `await api.send_command('ring')` | `await client.send_command('ring')` | `await device.play_sound()` | Device method preferred |
6969
| `await api.send_command('lock')` | `await client.send_command('lock')` | `await device.lock()` | Device method preferred |
70-
| `await api.send_command('delete')` | `await client.send_command('delete')` | `await device.wipe(confirm=True)` | **REQUIRES confirm flag** |
70+
| `await api.send_command('delete')` | `await client.send_command('fmd delete <PIN>')` | `await device.wipe(pin="1234", confirm=True)` | **Requires confirm + PIN (new)** |
7171

7272
### Camera Commands
7373

@@ -150,7 +150,7 @@ await device.play_sound() # Ring device
150150
await device.take_rear_photo() # Rear camera
151151
await device.take_front_photo() # Front camera
152152
await device.lock(message="Lost device") # Lock with message
153-
await device.wipe(confirm=True) # Factory reset (DESTRUCTIVE)
153+
await device.wipe(pin="1234", confirm=True) # Factory reset (DESTRUCTIVE, needs PIN + enabled setting)
154154

155155
# Pictures
156156
pictures = await device.get_picture_blobs(10)

fmd_api/device.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,23 @@ async def lock(self, message: Optional[str] = None, passcode: Optional[str] = No
188188
base = f"lock {sanitized}"
189189
return await self.client.send_command(base)
190190

191-
async def wipe(self, confirm: bool = False) -> bool:
191+
async def wipe(self, pin: Optional[str] = None, *, confirm: bool = False) -> bool:
192+
"""Factory reset (delete) the device. Requires user confirmation and PIN.
193+
194+
The underlying command format (per Android client) is: `fmd delete <PIN>`.
195+
Notes:
196+
- The Delete feature must be enabled in the FMD Android client's General settings.
197+
- A PIN is mandatory and must be sent when calling wipe(confirm=True).
198+
- PIN is sanitized to digits only; must be 4-10 digits.
199+
"""
192200
if not confirm:
193201
raise OperationError("wipe() requires confirm=True to proceed (destructive action)")
194-
return await self.client.send_command("delete")
202+
if not pin:
203+
raise OperationError("wipe() now requires a PIN: pass pin='1234' (digits only)")
204+
sanitized = "".join(ch for ch in pin if ch.isdigit())
205+
if sanitized != pin:
206+
warnings.warn("PIN contained non-digit characters; they were removed.", DeprecationWarning, stacklevel=2)
207+
if len(sanitized) < 4 or len(sanitized) > 10:
208+
raise OperationError("PIN must be between 4 and 10 digits after sanitization")
209+
command = f"fmd delete {sanitized}"
210+
return await self.client.send_command(command)

tests/unit/test_device.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ async def test_device_wipe_requires_confirm():
124124

125125
# Should raise without confirm
126126
with pytest.raises(OperationError, match="wipe.*requires confirm=True"):
127-
await device.wipe()
127+
await device.wipe(pin="1234")
128128

129129
# Should work with confirm
130130
client.access_token = "token"
@@ -138,7 +138,7 @@ def sign(self, message_bytes, pad, algo):
138138
with aioresponses() as m:
139139
m.post("https://fmd.example.com/api/v1/command", status=200, body="OK")
140140
try:
141-
assert await device.wipe(confirm=True) is True
141+
assert await device.wipe(pin="1234", confirm=True) is True
142142
finally:
143143
await client.close()
144144

@@ -488,7 +488,7 @@ def sign(self, message_bytes, pad, algo):
488488
m.post("https://fmd.example.com/api/v1/command", status=200, body="OK")
489489

490490
try:
491-
result = await device.wipe(confirm=True)
491+
result = await device.wipe(pin="1234", confirm=True)
492492
assert result is True
493493
finally:
494494
await client.close()

0 commit comments

Comments
 (0)