python-swidget 1.4.10__tar.gz → 1.4.12__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_swidget-1.4.10 → python_swidget-1.4.12}/PKG-INFO +1 -1
- {python_swidget-1.4.10 → python_swidget-1.4.12}/pyproject.toml +1 -1
- {python_swidget-1.4.10 → python_swidget-1.4.12}/swidget/discovery.py +15 -13
- {python_swidget-1.4.10 → python_swidget-1.4.12}/swidget/swidgetdevice.py +35 -0
- {python_swidget-1.4.10 → python_swidget-1.4.12}/README.md +0 -0
- {python_swidget-1.4.10 → python_swidget-1.4.12}/swidget/__init__.py +0 -0
- {python_swidget-1.4.10 → python_swidget-1.4.12}/swidget/cli.py +0 -0
- {python_swidget-1.4.10 → python_swidget-1.4.12}/swidget/exceptions.py +0 -0
- {python_swidget-1.4.10 → python_swidget-1.4.12}/swidget/provision.py +0 -0
- {python_swidget-1.4.10 → python_swidget-1.4.12}/swidget/py.typed +0 -0
- {python_swidget-1.4.10 → python_swidget-1.4.12}/swidget/swidgetdimmer.py +0 -0
- {python_swidget-1.4.10 → python_swidget-1.4.12}/swidget/swidgetoutlet.py +0 -0
- {python_swidget-1.4.10 → python_swidget-1.4.12}/swidget/swidgetswitch.py +0 -0
- {python_swidget-1.4.10 → python_swidget-1.4.12}/swidget/swidgettimerswitch.py +0 -0
- {python_swidget-1.4.10 → python_swidget-1.4.12}/swidget/websocket.py +0 -0
|
@@ -17,7 +17,7 @@ from .swidgetswitch import SwidgetSwitch
|
|
|
17
17
|
from .swidgettimerswitch import SwidgetTimerSwitch
|
|
18
18
|
|
|
19
19
|
RESPONSE_SEC = 5
|
|
20
|
-
|
|
20
|
+
SWIDGET_STS = ("urn:swidget:pico:1", "urn:swidget:video:1")
|
|
21
21
|
# Generous timeout: TLS handshakes on the device's MCU can take several
|
|
22
22
|
# seconds on first connection.
|
|
23
23
|
DETECT_TIMEOUT_SEC = 10
|
|
@@ -91,7 +91,7 @@ class SwidgetProtocol(ssdp.SimpleServiceDiscoveryProtocol):
|
|
|
91
91
|
headers = {h[0]: h[1] for h in response.headers}
|
|
92
92
|
mac_address = headers["USN"].split("-")[-1]
|
|
93
93
|
ip_address = urlparse(headers["LOCATION"]).hostname
|
|
94
|
-
if headers["ST"]
|
|
94
|
+
if headers["ST"] in SWIDGET_STS:
|
|
95
95
|
device_type = headers["SERVER"].split(" ")[1].split("+")[0]
|
|
96
96
|
insert_type = headers["SERVER"].split(" ")[1].split("+")[1].split("/")[0]
|
|
97
97
|
friendly_name = headers["SERVER"].split("/")[2].strip('"')
|
|
@@ -116,17 +116,19 @@ async def discover_devices(timeout=RESPONSE_SEC):
|
|
|
116
116
|
SwidgetProtocol, family=socket.AF_INET
|
|
117
117
|
)
|
|
118
118
|
|
|
119
|
-
#
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
"
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
119
|
+
# SSDP M-SEARCH matches one ST per request, so emit one packet per
|
|
120
|
+
# supported service type. Devices reply only to their matching ST.
|
|
121
|
+
for st in SWIDGET_STS:
|
|
122
|
+
search_request = ssdp.SSDPRequest(
|
|
123
|
+
"M-SEARCH",
|
|
124
|
+
headers={
|
|
125
|
+
"HOST": "239.255.255.250:1900",
|
|
126
|
+
"MAN": '"ssdp:discover"',
|
|
127
|
+
"MX": timeout,
|
|
128
|
+
"ST": st,
|
|
129
|
+
},
|
|
130
|
+
)
|
|
131
|
+
search_request.sendto(transport, (SwidgetProtocol.MULTICAST_ADDRESS, 1900))
|
|
130
132
|
await asyncio.sleep(timeout)
|
|
131
133
|
return devices
|
|
132
134
|
|
|
@@ -381,6 +381,18 @@ class SwidgetDevice:
|
|
|
381
381
|
self.device_config = DeviceConfiguration(config)
|
|
382
382
|
self._last_update = int(time.time())
|
|
383
383
|
|
|
384
|
+
async def set_device_config(self, updates: Dict[str, Any]) -> None:
|
|
385
|
+
"""POST a partial device_config update.
|
|
386
|
+
|
|
387
|
+
The firmware accepts a sparse dict mirroring the full config tree
|
|
388
|
+
(only the changed leaves), so callers should pass the same nested
|
|
389
|
+
shape ``get_device_config()`` returns. After the POST succeeds the
|
|
390
|
+
local cache is refreshed so reads see the new value immediately.
|
|
391
|
+
"""
|
|
392
|
+
_LOGGER.debug("SwidgetDevice.set_device_config(%s) called", updates)
|
|
393
|
+
await self.make_http_request("POST", "device_config", json_payload=updates)
|
|
394
|
+
await self.get_device_config()
|
|
395
|
+
|
|
384
396
|
async def get_summary(self) -> None:
|
|
385
397
|
"""Get a summary of the device over HTTP."""
|
|
386
398
|
_LOGGER.debug("SwidgetDevice.get_summary() called")
|
|
@@ -889,6 +901,29 @@ class SwidgetDevice:
|
|
|
889
901
|
return True
|
|
890
902
|
return False
|
|
891
903
|
|
|
904
|
+
@property
|
|
905
|
+
def rtsp_enabled(self) -> Optional[bool]:
|
|
906
|
+
"""Return whether the video insert's RTSP server is enabled.
|
|
907
|
+
|
|
908
|
+
Returns None when the config hasn't been fetched yet or the path
|
|
909
|
+
isn't present (non-video insert, or older firmware that omits the
|
|
910
|
+
key). Callers should treat None as "unknown" rather than False.
|
|
911
|
+
"""
|
|
912
|
+
try:
|
|
913
|
+
return bool(
|
|
914
|
+
self.device_config.config["insert"]["components"]["video"]["rtsp"][
|
|
915
|
+
"enable"
|
|
916
|
+
]
|
|
917
|
+
)
|
|
918
|
+
except (KeyError, TypeError):
|
|
919
|
+
return None
|
|
920
|
+
|
|
921
|
+
async def set_rtsp_enabled(self, enabled: bool) -> None:
|
|
922
|
+
"""Enable or disable the video insert's RTSP server."""
|
|
923
|
+
await self.set_device_config(
|
|
924
|
+
{"insert": {"components": {"video": {"rtsp": {"enable": bool(enabled)}}}}}
|
|
925
|
+
)
|
|
926
|
+
|
|
892
927
|
async def __aenter__(self) -> "SwidgetDevice":
|
|
893
928
|
"""Initialize and connect the Swidget Websocket client."""
|
|
894
929
|
await self.connect()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|