aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/island/test_sonyctl.py
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/island/test_sonyctl.py')
-rw-r--r--desktop/island/test_sonyctl.py55
1 files changed, 55 insertions, 0 deletions
diff --git a/desktop/island/test_sonyctl.py b/desktop/island/test_sonyctl.py
new file mode 100644
index 0000000..347e565
--- /dev/null
+++ b/desktop/island/test_sonyctl.py
@@ -0,0 +1,55 @@
+#!/usr/bin/python3
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+"""Run: /usr/bin/python3 desktop/island/test_sonyctl.py
+
+The frame codec and state parsing only; never touches Bluetooth. The bytes
+are ones captured from a WH-CH720N.
+"""
+
+import sys
+sys.dont_write_bytecode = True # importing sonyctl must not leave a __pycache__
+
+import importlib.machinery
+import importlib.util
+from pathlib import Path
+
+path = str(Path(__file__).resolve().parent / "sonyctl")
+loader = importlib.machinery.SourceFileLoader("sonyctl", path)
+spec = importlib.util.spec_from_loader("sonyctl", loader)
+s = importlib.util.module_from_spec(spec)
+loader.exec_module(s)
+
+# Init request as sent, and the CH720N's init reply as received.
+assert s.encode(0x0C, 0, [0x00, 0x00]) == bytes.fromhex("3e0c000000000200000e3c")
+reply = s.encode(0x0C, 0, bytes.fromhex("0100030010020000"))
+assert s.decode(reply[:-1]) == (0x0C, 0, bytes.fromhex("0100030010020000"))
+
+# Escaping: a 3c, 3d or 3e in the body round-trips and never appears raw.
+for b in (0x3C, 0x3D, 0x3E):
+ f = s.encode(0x0C, 1, [0x68, b])
+ assert 0x3C not in f[1:-1] and 0x3E not in f[1:-1]
+ assert s.decode(f[:-1]) == (0x0C, 1, bytes([0x68, b]))
+
+# A bad checksum and a short frame are rejected, not misread.
+bad = bytearray(reply[:-1]); bad[-1] ^= 1
+assert s.decode(bytes(bad)) is None
+assert s.decode(b"\x3e\x0c") is None
+
+assert s.state(bytes.fromhex("6717010100000c")) == {"mode": "nc", "level": 12, "voice": False}
+assert s.state(bytes.fromhex("67170101010010")) == {"mode": "ambient", "level": 16, "voice": False}
+assert s.state(bytes.fromhex("6717010101010c")) == {"mode": "ambient", "level": 12, "voice": True}
+assert s.state(bytes.fromhex("69170100000010")) == {"mode": "off", "level": 16, "voice": False}
+
+assert not s.main(["sonyctl", "00:00:00:00:00:00", "ambient", "21"])["ok"]
+assert not s.main(["sonyctl"])["ok"]
+assert not s.main(["sonyctl", "00:00:00:00:00:00", "voice", "maybe"])["ok"]
+print("ok")