mirror of
https://github.com/onyx-and-iris/vban-cmd-python.git
synced 2026-04-07 16:13:30 +00:00
Compare commits
23 Commits
add-event-
...
afa1867abc
| Author | SHA1 | Date | |
|---|---|---|---|
| afa1867abc | |||
| fcb656b7d0 | |||
| 9c0e2bef39 | |||
| 36692d1bc7 | |||
| 753714b639 | |||
| 27a26b8fe9 | |||
| 79260a0e47 | |||
| f9bcbfa74a | |||
| 0f2fb7121d | |||
| a635109308 | |||
| a61e09b075 | |||
| 763e44df12 | |||
| 69472a783e | |||
| 9a1ba06a21 | |||
| 14b2ee473a | |||
| ca2427c29a | |||
| ebacdcf82a | |||
| 7416108489 | |||
| bd6e57b3c6 | |||
| eed036ca03 | |||
| 55211b9b19 | |||
| 4af7c0f694 | |||
| f082fa8ac5 |
12
CHANGELOG.md
12
CHANGELOG.md
@@ -11,6 +11,18 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
|
|||||||
|
|
||||||
- [x]
|
- [x]
|
||||||
|
|
||||||
|
## [2.4.9] - 2023-08-13
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Error tests added in tests/test_errors.py
|
||||||
|
- Errors section in README updated.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- VBANCMDConnectionError class now subclasses VBANCMDError
|
||||||
|
- If the configs loader is passed an invalid config TOML it will log an error but continue to load further configs into memory.
|
||||||
|
|
||||||
## [2.3.2] - 2023-07-12
|
## [2.3.2] - 2023-07-12
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
[](https://badge.fury.io/py/vban-cmd)
|
[](https://badge.fury.io/py/vban-cmd)
|
||||||
[](https://github.com/onyx-and-iris/vban-cmd-python/blob/dev/LICENSE)
|
[](https://github.com/onyx-and-iris/vban-cmd-python/blob/dev/LICENSE)
|
||||||
|
[](https://python-poetry.org/)
|
||||||
[](https://github.com/psf/black)
|
[](https://github.com/psf/black)
|
||||||
[](https://pycqa.github.io/isort/)
|
[](https://pycqa.github.io/isort/)
|
||||||

|

|
||||||
@@ -94,6 +95,7 @@ def main():
|
|||||||
{
|
{
|
||||||
"strip-2": {"A1": True, "B1": True, "gain": -6.0},
|
"strip-2": {"A1": True, "B1": True, "gain": -6.0},
|
||||||
"bus-2": {"mute": True, "eq": {"on": True}},
|
"bus-2": {"mute": True, "eq": {"on": True}},
|
||||||
|
"vban-in-0": {"on": True},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -355,6 +357,7 @@ vban.apply(
|
|||||||
"strip-0": {"A1": True, "B1": True, "gain": -6.0},
|
"strip-0": {"A1": True, "B1": True, "gain": -6.0},
|
||||||
"bus-1": {"mute": True, "mode": "composite"},
|
"bus-1": {"mute": True, "mode": "composite"},
|
||||||
"bus-2": {"eq": {"on": True}},
|
"bus-2": {"eq": {"on": True}},
|
||||||
|
"vban-in-0": {"on": True},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
@@ -511,7 +514,7 @@ States not guaranteed to be current (requires use of dirty parameters to confirm
|
|||||||
|
|
||||||
## Errors
|
## Errors
|
||||||
|
|
||||||
- `errors.VBANCMDError`: Exception raised when general errors occur.
|
- `errors.VBANCMDError`: Base VBANCMD Exception class.
|
||||||
- `errors.VBANCMDConnectionError`: Exception raised when connection/timeout errors occur.
|
- `errors.VBANCMDConnectionError`: Exception raised when connection/timeout errors occur.
|
||||||
|
|
||||||
## Logging
|
## Logging
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ def main():
|
|||||||
{
|
{
|
||||||
"strip-2": {"A1": True, "B1": True, "gain": -6.0},
|
"strip-2": {"A1": True, "B1": True, "gain": -6.0},
|
||||||
"bus-2": {"mute": True},
|
"bus-2": {"mute": True},
|
||||||
|
"vban-in-0": {"on": True},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "vban-cmd"
|
name = "vban-cmd"
|
||||||
version = "2.4.3"
|
version = "2.4.9"
|
||||||
description = "Python interface for the VBAN RT Packet Service (Sendtext)"
|
description = "Python interface for the VBAN RT Packet Service (Sendtext)"
|
||||||
authors = ["onyx-and-iris <code@onyxandiris.online>"]
|
authors = ["onyx-and-iris <code@onyxandiris.online>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@@ -28,7 +28,10 @@ build-backend = "poetry.core.masonry.api"
|
|||||||
gui = "scripts:ex_gui"
|
gui = "scripts:ex_gui"
|
||||||
obs = "scripts:ex_obs"
|
obs = "scripts:ex_obs"
|
||||||
observer = "scripts:ex_observer"
|
observer = "scripts:ex_observer"
|
||||||
test = "scripts:test"
|
basic = "scripts:test_basic"
|
||||||
|
banana = "scripts:test_banana"
|
||||||
|
potato = "scripts:test_potato"
|
||||||
|
all = "scripts:test_all"
|
||||||
|
|
||||||
[tool.tox]
|
[tool.tox]
|
||||||
legacy_tox_ini = """
|
legacy_tox_ini = """
|
||||||
|
|||||||
19
scripts.py
19
scripts.py
@@ -1,3 +1,4 @@
|
|||||||
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -18,5 +19,21 @@ def ex_observer():
|
|||||||
subprocess.run([sys.executable, str(scriptpath)])
|
subprocess.run([sys.executable, str(scriptpath)])
|
||||||
|
|
||||||
|
|
||||||
def test():
|
def test_basic():
|
||||||
|
os.environ["KIND"] = "basic"
|
||||||
subprocess.run(["tox"])
|
subprocess.run(["tox"])
|
||||||
|
|
||||||
|
|
||||||
|
def test_banana():
|
||||||
|
os.environ["KIND"] = "banana"
|
||||||
|
subprocess.run(["tox"])
|
||||||
|
|
||||||
|
|
||||||
|
def test_potato():
|
||||||
|
os.environ["KIND"] = "potato"
|
||||||
|
subprocess.run(["tox"])
|
||||||
|
|
||||||
|
|
||||||
|
def test_all():
|
||||||
|
steps = [test_basic, test_banana, test_potato]
|
||||||
|
[step() for step in steps]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import os
|
||||||
import random
|
import random
|
||||||
import sys
|
import sys
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
@@ -6,8 +7,10 @@ import vban_cmd
|
|||||||
from vban_cmd.kinds import KindId
|
from vban_cmd.kinds import KindId
|
||||||
from vban_cmd.kinds import request_kind_map as kindmap
|
from vban_cmd.kinds import request_kind_map as kindmap
|
||||||
|
|
||||||
# let's keep things random
|
# get KIND_ID from env var, otherwise set to random
|
||||||
KIND_ID = random.choice(tuple(kind_id.name.lower() for kind_id in KindId))
|
KIND_ID = os.environ.get(
|
||||||
|
"KIND", random.choice(tuple(kind_id.name.lower() for kind_id in KindId))
|
||||||
|
)
|
||||||
|
|
||||||
opts = {
|
opts = {
|
||||||
"ip": "testing.local",
|
"ip": "testing.local",
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="60" height="20" role="img" aria-label="tests: 46"><title>tests: 46</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="60" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="23" height="20" fill="#4c1"/><rect width="60" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="475" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="130">46</text><text x="475" y="140" transform="scale(.1)" fill="#fff" textLength="130">46</text></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="60" height="20" role="img" aria-label="tests: 49"><title>tests: 49</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="60" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="23" height="20" fill="#4c1"/><rect width="60" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="475" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="130">49</text><text x="475" y="140" transform="scale(.1)" fill="#fff" textLength="130">49</text></g></svg>
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="60" height="20" role="img" aria-label="tests: 48"><title>tests: 48</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="60" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="23" height="20" fill="#4c1"/><rect width="60" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="475" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="130">48</text><text x="475" y="140" transform="scale(.1)" fill="#fff" textLength="130">48</text></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="60" height="20" role="img" aria-label="tests: 51"><title>tests: 51</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="60" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="23" height="20" fill="#4c1"/><rect width="60" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="475" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="130">51</text><text x="475" y="140" transform="scale(.1)" fill="#fff" textLength="130">51</text></g></svg>
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="60" height="20" role="img" aria-label="tests: 52"><title>tests: 52</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="60" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="23" height="20" fill="#4c1"/><rect width="60" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="475" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="130">52</text><text x="475" y="140" transform="scale(.1)" fill="#fff" textLength="130">52</text></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="60" height="20" role="img" aria-label="tests: 59"><title>tests: 59</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="60" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="23" height="20" fill="#4c1"/><rect width="60" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="475" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="130">59</text><text x="475" y="140" transform="scale(.1)" fill="#fff" textLength="130">59</text></g></svg>
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -11,7 +11,7 @@ Function RunTests {
|
|||||||
$line | Tee-Object -FilePath $coverage -Append
|
$line | Tee-Object -FilePath $coverage -Append
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Write-Output "$(Get-TimeStamp)" | Out-file $coverage -Append
|
Write-Output "$(Get-TimeStamp)" | Out-File $coverage -Append
|
||||||
|
|
||||||
Invoke-Expression "genbadge tests -t 90 -i ./tests/.coverage.xml -o ./tests/$kind.svg"
|
Invoke-Expression "genbadge tests -t 90 -i ./tests/.coverage.xml -o ./tests/$kind.svg"
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,10 @@ Function Get-TimeStamp {
|
|||||||
if ($MyInvocation.InvocationName -ne ".") {
|
if ($MyInvocation.InvocationName -ne ".") {
|
||||||
Invoke-Expression ".\.venv\Scripts\Activate.ps1"
|
Invoke-Expression ".\.venv\Scripts\Activate.ps1"
|
||||||
|
|
||||||
|
@("potato") | ForEach-Object {
|
||||||
|
$env:KIND = $_
|
||||||
RunTests
|
RunTests
|
||||||
|
}
|
||||||
|
|
||||||
Invoke-Expression "deactivate"
|
Invoke-Expression "deactivate"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests import data, vban
|
from tests import data, vban
|
||||||
@@ -11,11 +13,20 @@ class TestSetAndGetBoolHigher:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def setup_class(cls):
|
def setup_class(cls):
|
||||||
vban.apply_config("example")
|
vban.apply_config("example")
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
"not config.getoption('--run-slow')",
|
||||||
|
reason="Only run when --run-slow is given",
|
||||||
|
)
|
||||||
def test_it_tests_config_string(self):
|
def test_it_tests_config_string(self):
|
||||||
assert "PhysStrip" in vban.strip[data.phys_in].label
|
assert "PhysStrip" in vban.strip[data.phys_in].label
|
||||||
assert "VirtStrip" in vban.strip[data.virt_in].label
|
assert "VirtStrip" in vban.strip[data.virt_in].label
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
"not config.getoption('--run-slow')",
|
||||||
|
reason="Only run when --run-slow is given",
|
||||||
|
)
|
||||||
def test_it_tests_config_bool(self):
|
def test_it_tests_config_bool(self):
|
||||||
assert vban.strip[0].A1 == True
|
assert vban.strip[0].A1 == True
|
||||||
|
|
||||||
|
|||||||
37
tests/test_errors.py
Normal file
37
tests/test_errors.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import vban_cmd
|
||||||
|
from tests import data, vban
|
||||||
|
|
||||||
|
|
||||||
|
class TestErrors:
|
||||||
|
__test__ = True
|
||||||
|
|
||||||
|
def test_it_tests_an_unknown_kind(self):
|
||||||
|
with pytest.raises(
|
||||||
|
vban_cmd.error.VBANCMDError,
|
||||||
|
match=f"Unknown Voicemeeter kind 'unknown_kind'",
|
||||||
|
):
|
||||||
|
vban_cmd.api("unknown_kind")
|
||||||
|
|
||||||
|
def test_it_tests_an_unknown_config_name(self):
|
||||||
|
EXPECTED_MSG = "\n".join(
|
||||||
|
(
|
||||||
|
f"No config with name 'unknown' is loaded into memory",
|
||||||
|
f"Known configs: {list(vban.configs.keys())}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
with pytest.raises(vban_cmd.error.VBANCMDError, match=re.escape(EXPECTED_MSG)):
|
||||||
|
vban.apply_config("unknown")
|
||||||
|
|
||||||
|
def test_it_tests_an_invalid_config_key(self):
|
||||||
|
CONFIG = {
|
||||||
|
"strip-0": {"A1": True, "B1": True, "gain": -6.0},
|
||||||
|
"bus-0": {"mute": True, "eq": {"on": True}},
|
||||||
|
"unknown-0": {"state": True},
|
||||||
|
"vban-out-1": {"name": "streamname"},
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError, match="invalid config key 'unknown-0'"):
|
||||||
|
vban.apply(CONFIG)
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import time
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests import data, vban
|
from tests import data, vban
|
||||||
@@ -17,10 +15,6 @@ class TestPublicPacketLower:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
|
||||||
"not config.getoption('--run-slow')",
|
|
||||||
reason="Only run when --run-slow is given",
|
|
||||||
)
|
|
||||||
@pytest.mark.parametrize("value", [0, 1])
|
@pytest.mark.parametrize("value", [0, 1])
|
||||||
class TestSetRT:
|
class TestSetRT:
|
||||||
__test__ = True
|
__test__ = True
|
||||||
@@ -35,7 +29,6 @@ class TestSetRT:
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_it_sends_a_text_request(self, kls, index, param, value):
|
def test_it_sends_a_text_request(self, kls, index, param, value):
|
||||||
vban._set_rt(f"{kls}[{index}]", param, value)
|
vban._set_rt(f"{kls}[{index}].{param}", value)
|
||||||
time.sleep(0.02)
|
|
||||||
target = getattr(vban, kls)[index]
|
target = getattr(vban, kls)[index]
|
||||||
assert getattr(target, param) == bool(value)
|
assert getattr(target, param) == bool(value)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class Bus(IRemote):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def identifier(self) -> str:
|
def identifier(self) -> str:
|
||||||
return f"Bus[{self.index}]"
|
return f"bus[{self.index}]"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def gain(self) -> float:
|
def gain(self) -> float:
|
||||||
@@ -66,7 +66,7 @@ class BusEQ(IRemote):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def identifier(self) -> str:
|
def identifier(self) -> str:
|
||||||
return f"Bus[{self.index}].eq"
|
return f"bus[{self.index}].eq"
|
||||||
|
|
||||||
|
|
||||||
class PhysicalBus(Bus):
|
class PhysicalBus(Bus):
|
||||||
@@ -116,7 +116,7 @@ class BusLevel(IRemote):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def identifier(self) -> str:
|
def identifier(self) -> str:
|
||||||
return f"Bus[{self.index}]"
|
return f"bus[{self.index}]"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def all(self) -> tuple:
|
def all(self) -> tuple:
|
||||||
@@ -138,7 +138,7 @@ def _make_bus_mode_mixin():
|
|||||||
"""Creates a mixin of Bus Modes."""
|
"""Creates a mixin of Bus Modes."""
|
||||||
|
|
||||||
def identifier(self) -> str:
|
def identifier(self) -> str:
|
||||||
return f"Bus[{self.index}].mode"
|
return f"bus[{self.index}].mode"
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class Command(IRemote):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def identifier(self) -> str:
|
def identifier(self) -> str:
|
||||||
return "Command"
|
return "command"
|
||||||
|
|
||||||
def set_showvbanchat(self, val: bool):
|
def set_showvbanchat(self, val: bool):
|
||||||
self.setter("DialogShow.VBANCHAT", 1 if val else 0)
|
self.setter("DialogShow.VBANCHAT", 1 if val else 0)
|
||||||
|
|||||||
@@ -148,8 +148,13 @@ class Loader(metaclass=SingletonType):
|
|||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"config file with name {identifier} already in memory, skipping.."
|
f"config file with name {identifier} already in memory, skipping.."
|
||||||
)
|
)
|
||||||
return False
|
return
|
||||||
|
try:
|
||||||
self.parser = dataextraction_factory(data)
|
self.parser = dataextraction_factory(data)
|
||||||
|
except tomllib.TOMLDecodeError as e:
|
||||||
|
ERR_MSG = (str(e), f"When attempting to load {identifier}.toml")
|
||||||
|
self.logger.error(f"{type(e).__name__}: {' '.join(ERR_MSG)}")
|
||||||
|
return
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def register(self, identifier, data=None):
|
def register(self, identifier, data=None):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
class VBANCMDError(Exception):
|
class VBANCMDError(Exception):
|
||||||
"""Base VBANCMD Exception class. Raised when general errors occur"""
|
"""Base VBANCMD Exception class."""
|
||||||
|
|
||||||
|
|
||||||
class VBANCMDConnectionError(VBANCMDError):
|
class VBANCMDConnectionError(VBANCMDError):
|
||||||
|
|||||||
@@ -265,7 +265,7 @@ class VbanRtPacketHeader:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class RequestHeader:
|
class RequestHeader:
|
||||||
"""Represents the header of an REQUEST RT PACKET"""
|
"""Represents the header of a REQUEST RT PACKET"""
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
bps_index: int
|
bps_index: int
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class Strip(IRemote):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def identifier(self) -> str:
|
def identifier(self) -> str:
|
||||||
return f"Strip[{self.index}]"
|
return f"strip[{self.index}]"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def limit(self) -> int:
|
def limit(self) -> int:
|
||||||
@@ -79,7 +79,7 @@ class PhysicalStrip(Strip):
|
|||||||
class StripComp(IRemote):
|
class StripComp(IRemote):
|
||||||
@property
|
@property
|
||||||
def identifier(self) -> str:
|
def identifier(self) -> str:
|
||||||
return f"Strip[{self.index}].comp"
|
return f"strip[{self.index}].comp"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def knob(self) -> float:
|
def knob(self) -> float:
|
||||||
@@ -157,7 +157,7 @@ class StripComp(IRemote):
|
|||||||
class StripGate(IRemote):
|
class StripGate(IRemote):
|
||||||
@property
|
@property
|
||||||
def identifier(self) -> str:
|
def identifier(self) -> str:
|
||||||
return f"Strip[{self.index}].gate"
|
return f"strip[{self.index}].gate"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def knob(self) -> float:
|
def knob(self) -> float:
|
||||||
@@ -219,7 +219,7 @@ class StripGate(IRemote):
|
|||||||
class StripDenoiser(IRemote):
|
class StripDenoiser(IRemote):
|
||||||
@property
|
@property
|
||||||
def identifier(self) -> str:
|
def identifier(self) -> str:
|
||||||
return f"Strip[{self.index}].denoiser"
|
return f"strip[{self.index}].denoiser"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def knob(self) -> float:
|
def knob(self) -> float:
|
||||||
@@ -233,7 +233,7 @@ class StripDenoiser(IRemote):
|
|||||||
class StripEQ(IRemote):
|
class StripEQ(IRemote):
|
||||||
@property
|
@property
|
||||||
def identifier(self) -> str:
|
def identifier(self) -> str:
|
||||||
return f"Strip[{self.index}].eq"
|
return f"strip[{self.index}].eq"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def on(self):
|
def on(self):
|
||||||
@@ -312,7 +312,7 @@ class StripLevel(IRemote):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def identifier(self) -> str:
|
def identifier(self) -> str:
|
||||||
return f"Strip[{self.index}]"
|
return f"strip[{self.index}]"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def prefader(self) -> tuple:
|
def prefader(self) -> tuple:
|
||||||
@@ -345,7 +345,7 @@ class GainLayer(IRemote):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def identifier(self) -> str:
|
def identifier(self) -> str:
|
||||||
return f"Strip[{self.index}]"
|
return f"strip[{self.index}]"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def gain(self) -> float:
|
def gain(self) -> float:
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ def cache_bool(func, param):
|
|||||||
|
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
self, *rem = args
|
self, *rem = args
|
||||||
cmd = f"{self.identifier}.{param}"
|
if self._cmd(param) in self._remote.cache:
|
||||||
if cmd in self._remote.cache:
|
return self._remote.cache.pop(self._cmd(param)) == 1
|
||||||
return self._remote.cache.pop(cmd) == 1
|
|
||||||
if self._remote.sync:
|
if self._remote.sync:
|
||||||
self._remote.clear_dirty()
|
self._remote.clear_dirty()
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
@@ -22,9 +21,8 @@ def cache_string(func, param):
|
|||||||
|
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
self, *rem = args
|
self, *rem = args
|
||||||
cmd = f"{self.identifier}.{param}"
|
if self._cmd(param) in self._remote.cache:
|
||||||
if cmd in self._remote.cache:
|
return self._remote.cache.pop(self._cmd(param))
|
||||||
return self._remote.cache.pop(cmd)
|
|
||||||
if self._remote.sync:
|
if self._remote.sync:
|
||||||
self._remote.clear_dirty()
|
self._remote.clear_dirty()
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
|||||||
@@ -172,32 +172,24 @@ class VbanMidiOutstream(VbanOutstream):
|
|||||||
def _make_stream_pair(remote, kind):
|
def _make_stream_pair(remote, kind):
|
||||||
num_instream, num_outstream, num_midi, num_text = kind.vban
|
num_instream, num_outstream, num_midi, num_text = kind.vban
|
||||||
|
|
||||||
def _generate_streams(i, dir):
|
def _make_cls(i, direction):
|
||||||
"""generator function for creating instream/outstream tuples"""
|
match direction:
|
||||||
if dir == "in":
|
case "in":
|
||||||
if i < num_instream:
|
if i < num_instream:
|
||||||
yield VbanAudioInstream
|
return VbanAudioInstream(remote, i)
|
||||||
elif i < num_instream + num_midi:
|
elif i < num_instream + num_midi:
|
||||||
yield VbanMidiInstream
|
return VbanMidiInstream(remote, i)
|
||||||
else:
|
|
||||||
yield VbanTextInstream
|
|
||||||
else:
|
else:
|
||||||
|
return VbanTextInstream(remote, i)
|
||||||
|
case "out":
|
||||||
if i < num_outstream:
|
if i < num_outstream:
|
||||||
yield VbanAudioOutstream
|
return VbanAudioOutstream(remote, i)
|
||||||
else:
|
else:
|
||||||
yield VbanMidiOutstream
|
return VbanMidiOutstream(remote, i)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
tuple(
|
tuple(_make_cls(i, "in") for i in range(num_instream + num_midi + num_text)),
|
||||||
cls(remote, i)
|
tuple(_make_cls(i, "out") for i in range(num_outstream + num_midi)),
|
||||||
for i in range(num_instream + num_midi + num_text)
|
|
||||||
for cls in _generate_streams(i, "in")
|
|
||||||
),
|
|
||||||
tuple(
|
|
||||||
cls(remote, i)
|
|
||||||
for i in range(num_outstream + num_midi)
|
|
||||||
for cls in _generate_streams(i, "out")
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ class VbanCmd(metaclass=ABCMeta):
|
|||||||
self._pdirty = False
|
self._pdirty = False
|
||||||
self._ldirty = False
|
self._ldirty = False
|
||||||
self._script = str()
|
self._script = str()
|
||||||
|
self.stop_event = None
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -109,7 +110,7 @@ class VbanCmd(metaclass=ABCMeta):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def stopped(self):
|
def stopped(self):
|
||||||
return self.stop_event.is_set()
|
return self.stop_event is None or self.stop_event.is_set()
|
||||||
|
|
||||||
def _set_rt(self, cmd: str, val: Union[str, float]):
|
def _set_rt(self, cmd: str, val: Union[str, float]):
|
||||||
"""Sends a string request command over a network."""
|
"""Sends a string request command over a network."""
|
||||||
@@ -181,16 +182,25 @@ class VbanCmd(metaclass=ABCMeta):
|
|||||||
minor delay between each recursion
|
minor delay between each recursion
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def param(key):
|
def target(key):
|
||||||
obj, m2, *rem = key.split("-")
|
match key.split("-"):
|
||||||
index = int(m2) if m2.isnumeric() else int(*rem)
|
case ["strip" | "bus" as kls, index] if index.isnumeric():
|
||||||
if obj in ("strip", "bus", "button"):
|
target = getattr(self, kls)
|
||||||
return getattr(self, obj)[index]
|
case [
|
||||||
elif obj == "vban":
|
"vban",
|
||||||
return getattr(getattr(self, obj), f"{m2}stream")[index]
|
"in" | "instream" | "out" | "outstream" as direction,
|
||||||
raise ValueError(obj)
|
index,
|
||||||
|
] if index.isnumeric():
|
||||||
|
target = getattr(
|
||||||
|
self.vban, f"{direction.removesuffix('stream')}stream"
|
||||||
|
)
|
||||||
|
case _:
|
||||||
|
ERR_MSG = f"invalid config key '{key}'"
|
||||||
|
self.logger.error(ERR_MSG)
|
||||||
|
raise ValueError(ERR_MSG)
|
||||||
|
return target[int(index)]
|
||||||
|
|
||||||
[param(key).apply(datum).then_wait() for key, datum in data.items()]
|
[target(key).apply(di).then_wait() for key, di in data.items()]
|
||||||
|
|
||||||
def apply_config(self, name):
|
def apply_config(self, name):
|
||||||
"""applies a config from memory"""
|
"""applies a config from memory"""
|
||||||
@@ -221,7 +231,8 @@ class VbanCmd(metaclass=ABCMeta):
|
|||||||
if not self.stopped():
|
if not self.stopped():
|
||||||
self.logger.debug("events thread shutdown started")
|
self.logger.debug("events thread shutdown started")
|
||||||
self.stop_event.set()
|
self.stop_event.set()
|
||||||
self.subscriber.join() # wait for subscriber thread to complete cycle
|
for t in (self.producer, self.subscriber):
|
||||||
|
t.join()
|
||||||
[sock.close() for sock in self.socks]
|
[sock.close() for sock in self.socks]
|
||||||
self.logger.info(f"{type(self).__name__}: Successfully logged out of {self}")
|
self.logger.info(f"{type(self).__name__}: Successfully logged out of {self}")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user