Skip to content

Commit 2f944d2

Browse files
authored
Add remove devices impl (#155)
Adding implementation for adding/removing devices from/to account
1 parent c5135ba commit 2f944d2

File tree

4 files changed

+48
-13
lines changed

4 files changed

+48
-13
lines changed

soundcork/datastore.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22
import re
33
import xml.etree.ElementTree as ET
4-
from os import mkdir, path, walk
4+
from os import mkdir, path, remove, rmdir, walk
55
from typing import Optional
66

77
from soundcork.config import Settings
@@ -352,3 +352,13 @@ def add_device(self, account: str, device_id: str, device_info_xml: str) -> bool
352352
"w",
353353
) as device_info_file:
354354
device_info_file.write(device_info_xml)
355+
356+
def remove_device(self, account: str, device_id: str) -> bool:
357+
logger.debug(f"removing device {device_id} from account {account}")
358+
if not self.device_exists(account, device_id):
359+
return False
360+
361+
# TODO: add error handling if you can't delete the files
362+
remove(path.join(self.account_device_dir(account, device_id), DEVICE_INFO_FILE))
363+
rmdir(path.join(self.account_devices_dir(account), device_id))
364+
return True

soundcork/devices.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import urllib.request
1010
import xml.etree.ElementTree as ET
1111
from subprocess import run
12+
from typing import Optional
1213
from urllib.parse import urlparse
1314

1415
import upnpclient
@@ -136,6 +137,17 @@ def get_bose_devices() -> list[upnpclient.upnp.Device]:
136137
return bose_devices
137138

138139

140+
def get_device_by_id(device_id: str) -> Optional[upnpclient.upnp.Device]:
141+
devices = get_bose_devices()
142+
for device in devices:
143+
try:
144+
info_elem = ET.fromstring(read_device_info(device))
145+
if info_elem.attrib.get("deviceID", "") == device_id:
146+
return device
147+
except:
148+
pass
149+
150+
139151
def show_upnp_devices() -> None:
140152
"""Print a list of devices, specifying reachable ones."""
141153
devices = get_bose_devices()

soundcork/main.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -299,30 +299,44 @@ async def post_account_recent(
299299
"/marge/streaming/account/{account}/device/",
300300
response_class=BoseXMLResponse,
301301
tags=["marge"],
302+
status_code=HTTPStatus.CREATED,
302303
dependencies=[
303304
Depends(
304305
Etag(
305306
etag_gen=etag_for_account,
306307
weak=False,
308+
extra_headers={
309+
"method_name": "addDevice",
310+
"access-control-expose-headers": "Credentials",
311+
},
307312
)
308313
)
309314
],
310315
)
311316
async def post_account_device(
312-
account: Annotated[str, Path(pattern=ACCOUNT_RE)], request: Request
317+
account: Annotated[str, Path(pattern=ACCOUNT_RE)],
318+
request: Request,
313319
):
314320
xml = await request.body()
315-
xml_resp = add_device_to_account(datastore, account, xml)
321+
device_id, xml_resp = add_device_to_account(datastore, account, xml)
322+
316323
return bose_xml_str(xml_resp)
317324

318325

319-
@app.delete("/marge/streaming/account/{account}/device/{device}/", tags=["marge"])
326+
@app.delete("/marge/streaming/account/{account}/device/{device}", tags=["marge"])
320327
async def delete_account_device(
321328
account: Annotated[str, Path(pattern=ACCOUNT_RE)],
322329
device: Annotated[str, Path(pattern=DEVICE_RE)],
330+
response: Response,
323331
):
324332
xml_resp = remove_device_from_account(datastore, account, device)
325-
return {"ok": True}
333+
response.headers["method_name"] = "removeDevice"
334+
response.headers["location"] = (
335+
f"{settings.base_url}/marge/account/{account}/device/{device}"
336+
)
337+
response.body = ""
338+
response.status_code = HTTPStatus.OK
339+
return response
326340

327341

328342
@app.get("/bmx/registry/v1/services", response_model_exclude_none=True, tags=["bmx"])

soundcork/marge.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from soundcork.config import Settings
1010
from soundcork.constants import PROVIDERS
11+
from soundcork.devices import get_device_by_id, read_device_info
1112
from soundcork.model import (
1213
ConfiguredSource,
1314
ContentItem,
@@ -404,9 +405,9 @@ def add_device_to_account(
404405
new_device_elem = ET.fromstring(source_xml)
405406
device_id = new_device_elem.attrib.get("deviceid", "")
406407
name = new_device_elem.find("name").text
407-
408-
# TODO implement
409-
# datastore.add_device(account, device_id, name)
408+
device = get_device_by_id(device_id)
409+
device_xml = read_device_info(device)
410+
datastore.add_device(account, device_id, device_xml)
410411

411412
created_on = datetime.fromtimestamp(
412413
datetime.now().timestamp(), timezone.utc
@@ -419,14 +420,12 @@ def add_device_to_account(
419420
ET.SubElement(return_elem, "name").text = name
420421
ET.SubElement(return_elem, "updatedOn").text = created_on
421422

422-
return return_elem
423+
return (device_id, return_elem)
423424

424425

425426
def remove_device_from_account(datastore: "DataStore", account: str, device: str):
426-
# TODO implement
427-
# datastore.remove_device(account, device)
428-
return {"ok"}
429-
427+
removed = datastore.remove_device(account, device)
428+
return {"ok": removed}
430429

431430
def strip_element_text(elem: ET.Element) -> str:
432431
if elem == None:

0 commit comments

Comments
 (0)