mirror of
https://github.com/onyx-and-iris/vban-cmd-python.git
synced 2025-01-18 10:30:48 +00:00
Merge branch 'dev' of https://github.com/onyx-and-iris/vban-cmd-python into dev
This commit is contained in:
commit
43a02a2adc
96
CHANGELOG.md
Normal file
96
CHANGELOG.md
Normal file
@ -0,0 +1,96 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
Before any major/minor/patch bump all unit tests will be run to verify they pass.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- [ ] Create a stable branch.
|
||||
|
||||
## [0.3.0] - 2022-04
|
||||
|
||||
### Added
|
||||
|
||||
- strip_levels, bus_levels property objects added to base class. These now return the full level array.
|
||||
- filter out empty values from strip_levels/bus_levels
|
||||
- script decorator added to sendtext() in base class. Now supports passing a nested dict, similar to apply()
|
||||
- pre-commit.ps1 added for use with git hook. test badges added to readme.
|
||||
- genbadge added to development dependencies in setup.py
|
||||
- Lower tests added.
|
||||
|
||||
### Changed
|
||||
|
||||
- mc getter implemented in strip class
|
||||
- bus modes meta function reworked.
|
||||
- sendtext() now for multi set operationis (used by apply() method)
|
||||
- tests now run according to a kind, for a single run version is random.
|
||||
- now using psuedo decorator functions cache_bool and cache_string to handle caching.
|
||||
- meta functions reworked.
|
||||
- strip bool props moved into factory function.
|
||||
|
||||
### Fixed
|
||||
|
||||
- fixed size of recvfrom buffer for self.rt_packet_socket in base class
|
||||
- nose tests migrated to pytest as nose will not be supported in python 3.10+
|
||||
- sendtext() removed from readme. Still in interface but not advised to use since it doesn't update cache.
|
||||
|
||||
## [0.2.0] - 2022-03-29
|
||||
|
||||
### Added
|
||||
|
||||
- profiles module
|
||||
- example profiles added to \_profiles/ directory.
|
||||
|
||||
### Changed
|
||||
|
||||
- setup/teardown moved into login()/logout() functions in base class.
|
||||
- now using black formatter, code style badge added to readme.
|
||||
|
||||
### Fixed
|
||||
|
||||
- bus/strip labels split at null terminator in ascii string.
|
||||
- all gainlayers added to isdirty() function in VBAN_VMRT_Packet_Data
|
||||
|
||||
## [0.1.0] - 2022-03-21
|
||||
|
||||
### Added
|
||||
|
||||
- gain property added to strip class.
|
||||
- added worker2 thread for keeping the public packet constantly updated in the background.
|
||||
- self.running flag for notifying threads when to stop.
|
||||
- docstrings added to base class.
|
||||
- apply() added to base class and strip/bus classes. supports setting parameters through dict.
|
||||
- bus modes mixin added to bus class
|
||||
- isdirty() added to VBAN_VMRT_Packet_Data for precisely defining the dirty parameter.
|
||||
|
||||
### Changed
|
||||
|
||||
- underscore removed from package name. https://peps.python.org/pep-0008/#package-and-module-names
|
||||
- readme updated to reflect changes.
|
||||
- boolean strip/bus properties now defined by meta functions.
|
||||
|
||||
### Fixed
|
||||
|
||||
- fixed kind map ins, outs order. (causing error with basic version)
|
||||
|
||||
## [0.0.1] - 2022-02
|
||||
|
||||
### Added
|
||||
|
||||
- Create the base class, setup entry point to interface.
|
||||
- worker thread added to keep interface registered to the rt packet service
|
||||
- Added definitions for rt packet data and the various packet headers, as dataclasses.
|
||||
- Property objects in data packet dataclass for returning byte tuples/parsing string params.
|
||||
- Adding kinds module for mapping each Voicemeeter version to a namedtuple.
|
||||
- Added meta module.
|
||||
- Strip/Bus modules added.
|
||||
- Modes dataclass for defining strip states through bit modes.
|
||||
- GainLayer added to strip module. gainlayer properties added as mixin.
|
||||
- Higher unit tests added.
|
||||
- show(), hide(), shutdown() and restart() added to base class.
|
||||
- Add initial version of readme.
|
||||
- add property objects sr, nbc and streamname to TextRequestHeader. Now settable by kwargs.
|
37
README.md
37
README.md
@ -6,12 +6,16 @@
|
||||
|
||||
# VBAN CMD
|
||||
|
||||
This package offers a Python interface for [Voicemeeter VBAN TEXT](https://vb-audio.com/Voicemeeter/VBANProtocol_Specifications.pdf#page=19) as well as the [Voicemeeter RT Packet Service](https://vb-audio.com/Voicemeeter/VBANProtocol_Specifications.pdf#page=27) which allows a client to send and receive parameter values over a local network.
|
||||
This package offers a Python interface for the Voicemeeter RT Packet Service as well as Voicemeeter VBAN-TEXT.
|
||||
|
||||
This allows a user to get (rt packets) and set (vban-text) parameters over a local network. Consider the Streamer View app over VBAN, for example.
|
||||
|
||||
It may be used standalone or to extend the [Voicemeeter Remote Python API](https://github.com/onyx-and-iris/voicemeeter-api-python)
|
||||
|
||||
For sending audio across a network with VBAN you will need to look elsewhere.
|
||||
|
||||
For an outline of past/future changes refer to: [CHANGELOG](CHANGELOG.md)
|
||||
|
||||
## Tested against
|
||||
|
||||
- Basic 1.0.8.2
|
||||
@ -20,7 +24,7 @@ For sending audio across a network with VBAN you will need to look elsewhere.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Voicemeeter 1 (Basic), 2 (Banana) or 3 (Potato)
|
||||
- [Voicemeeter](https://voicemeeter.com/)
|
||||
- Python 3.9+
|
||||
|
||||
## Installation
|
||||
@ -46,7 +50,7 @@ pip install -e .['development']
|
||||
|
||||
#### Use with a context manager:
|
||||
|
||||
Parameter coverage is not as extensive for the RT Packet Service as with the Remote API.
|
||||
Parameter coverage is not as extensive for this interface as with the Remote API.
|
||||
|
||||
### Example 1
|
||||
|
||||
@ -82,8 +86,6 @@ if __name__ == '__main__':
|
||||
|
||||
#### Or perform setup/teardown independently:
|
||||
|
||||
for example:
|
||||
|
||||
### Example 2
|
||||
|
||||
```python
|
||||
@ -267,6 +269,10 @@ vban.bus[0].mode.tvmix = True
|
||||
|
||||
### `VbanCmd` (lower level)
|
||||
|
||||
#### `vban.pdirty`
|
||||
|
||||
True iff a parameter has been changed. Typically this is checked periodically to update states.
|
||||
|
||||
#### `vban.set_rt(id_, param, val)`
|
||||
|
||||
Sends a string request RT Packet where the command would take the form:
|
||||
@ -275,22 +281,9 @@ Sends a string request RT Packet where the command would take the form:
|
||||
f'{id_}.{param}={val}'
|
||||
```
|
||||
|
||||
#### `vban._get_rt()`
|
||||
#### `vban.public_packet`
|
||||
|
||||
Used for updating the RT data packet, used internally by the Interface.
|
||||
|
||||
```python
|
||||
vban.public_packet = vban._get_rt()
|
||||
```
|
||||
|
||||
#### `vban.sendtext(cmd)`
|
||||
|
||||
Sends a multi parameter TEXT string command, for example:
|
||||
|
||||
```python
|
||||
# Use ';' or ',' for delimiters.
|
||||
vban.sendtext('Strip[0].Mute=1;Strip[3].A3=0;Bus[2].Mute=0;Bus[3].Eq.On=1')
|
||||
```
|
||||
Returns a Voicemeeter rt data packet. Designed to be used internally by the interface but available for parsing through this read only property object. States may or may not be current, use the polling parameter pdirty to be sure.
|
||||
|
||||
### `Errors`
|
||||
|
||||
@ -306,4 +299,6 @@ Then from tests directory:
|
||||
|
||||
## Resources
|
||||
|
||||
- [Voicemeeter RT Packet Service](https://vb-audio.com/Voicemeeter/VBANProtocol_Specifications.pdf)
|
||||
- [Voicemeeter VBAN TEXT](https://vb-audio.com/Voicemeeter/VBANProtocol_Specifications.pdf#page=19)
|
||||
|
||||
- [Voicemeeter RT Packet Service](https://vb-audio.com/Voicemeeter/VBANProtocol_Specifications.pdf#page=27)
|
||||
|
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="vbancmd",
|
||||
version="0.0.1",
|
||||
version="0.3.0",
|
||||
description="VBAN CMD Python API",
|
||||
packages=["vbancmd"],
|
||||
install_requires=["toml"],
|
||||
|
@ -5,7 +5,7 @@ import random
|
||||
import sys
|
||||
|
||||
# let's keep things random
|
||||
kind_id = random.choice(("basic", "banana", "potato"))
|
||||
kind_id = random.choice(tuple(kind.id for kind in kinds.all))
|
||||
|
||||
opts = {
|
||||
"ip": "codey.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: 41"><title>tests: 41</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">41</text><text x="475" y="140" transform="scale(.1)" fill="#fff" textLength="130">41</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: 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>
|
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: 41"><title>tests: 41</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">41</text><text x="475" y="140" transform="scale(.1)" fill="#fff" textLength="130">41</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: 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>
|
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: 45"><title>tests: 45</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">45</text><text x="475" y="140" transform="scale(.1)" fill="#fff" textLength="130">45</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: 50"><title>tests: 50</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">50</text><text x="475" y="140" transform="scale(.1)" fill="#fff" textLength="130">50</text></g></svg>
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@ -3,12 +3,12 @@ Function RunTests {
|
||||
$run_tests = "pytest -v --capture=tee-sys --junitxml=./tests/.coverage.xml"
|
||||
$match_pattern = "^=|^\s*$|^Running|^Using|^plugins|^collecting|^tests"
|
||||
|
||||
Clear-Content $coverage
|
||||
if ( Test-Path $coverage ) { Clear-Content $coverage }
|
||||
|
||||
ForEach ($line in $(Invoke-Expression $run_tests)) {
|
||||
If ( $line -Match $match_pattern ) {
|
||||
if ( $line -Match "^Running tests for kind \[(\w+)\]" ) { $kind = $Matches[1] }
|
||||
$line | Tee-Object -FilePath $coverage -Append
|
||||
$line | Tee-Object -FilePath $coverage -Append
|
||||
}
|
||||
}
|
||||
Write-Output "$(Get-TimeStamp)" | Out-file $coverage -Append
|
||||
@ -17,9 +17,9 @@ Function RunTests {
|
||||
}
|
||||
|
||||
Function Get-TimeStamp {
|
||||
|
||||
|
||||
return "[{0:MM/dd/yy} {0:HH:mm:ss}]" -f (Get-Date)
|
||||
|
||||
|
||||
}
|
||||
|
||||
if ($MyInvocation.InvocationName -ne ".") {
|
||||
|
@ -1,79 +1,32 @@
|
||||
import pytest
|
||||
from tests import tests, data
|
||||
from vbancmd import kinds
|
||||
import re
|
||||
|
||||
|
||||
class TestSetAndGetFloatLower:
|
||||
__test__ = False
|
||||
class TestPublicPacketLower:
|
||||
__test__ = True
|
||||
|
||||
"""VBVMR_SetParameterFloat, VBVMR_GetParameterFloat"""
|
||||
"""Tests for a valid rt data packet"""
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[
|
||||
(f"Strip[{data.phys_in}].Mute", 1),
|
||||
(f"Bus[{data.virt_out}].Eq.on", 1),
|
||||
(f"Strip[{data.phys_in}].Mute", 0),
|
||||
(f"Bus[{data.virt_out}].Eq.on", 0),
|
||||
],
|
||||
)
|
||||
def test_it_sets_and_gets_mute_eq_float_params(self, param, value):
|
||||
tests.set(param, value)
|
||||
assert (round(tests.get(param))) == value
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[
|
||||
(f"Strip[{data.phys_in}].Comp", 5.3),
|
||||
(f"Strip[{data.virt_in}].Gain", -37.5),
|
||||
(f"Bus[{data.virt_out}].Gain", -22.7),
|
||||
],
|
||||
)
|
||||
def test_it_sets_and_gets_comp_gain_float_params(self, param, value):
|
||||
tests.set(param, value)
|
||||
assert (round(tests.get(param), 1)) == value
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", ["test0", "test1"])
|
||||
class TestSetAndGetStringLower:
|
||||
__test__ = False
|
||||
|
||||
"""VBVMR_SetParameterStringW, VBVMR_GetParameterStringW"""
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param",
|
||||
[(f"Strip[{data.phys_out}].label"), (f"Bus[{data.virt_out}].label")],
|
||||
)
|
||||
def test_it_sets_and_gets_string_params(self, param, value):
|
||||
tests.set(param, value)
|
||||
assert tests.get(param, string=True) == value
|
||||
def test_it_gets_an_rt_data_packet(self):
|
||||
assert tests.public_packet.voicemeetertype in (kind.id for kind in kinds.all)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", [0, 1])
|
||||
class TestMacroButtonsLower:
|
||||
__test__ = False
|
||||
class TestSetRT:
|
||||
__test__ = True
|
||||
|
||||
"""VBVMR_MacroButton_SetStatus, VBVMR_MacroButton_GetStatus"""
|
||||
"""Tests set_rt"""
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"index, mode",
|
||||
[(33, 1), (49, 1)],
|
||||
"kls,index,param",
|
||||
[
|
||||
("strip", data.phys_in, "mute"),
|
||||
("bus", data.virt_out, "mono"),
|
||||
],
|
||||
)
|
||||
def test_it_sets_and_gets_macrobuttons_state(self, index, mode, value):
|
||||
tests.set_buttonstatus(index, value, mode)
|
||||
assert tests.get_buttonstatus(index, mode) == value
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"index, mode",
|
||||
[(14, 2), (12, 2)],
|
||||
)
|
||||
def test_it_sets_and_gets_macrobuttons_stateonly(self, index, mode, value):
|
||||
tests.set_buttonstatus(index, value, mode)
|
||||
assert tests.get_buttonstatus(index, mode) == value
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"index, mode",
|
||||
[(50, 3), (65, 3)],
|
||||
)
|
||||
def test_it_sets_and_gets_macrobuttons_trigger(self, index, mode, value):
|
||||
tests.set_buttonstatus(index, value, mode)
|
||||
assert tests.get_buttonstatus(index, mode) == value
|
||||
def test_it_gets_an_rt_data_packet(self, kls, index, param, value):
|
||||
tests.set_rt(f"{kls}[{index}]", param, value)
|
||||
target = getattr(tests, kls)[index]
|
||||
assert getattr(target, param) == bool(value)
|
||||
|
@ -22,6 +22,9 @@ class OutputBus(Channel):
|
||||
"levels": BusLevel(remote, index),
|
||||
"mode": BusModeMixin(remote, index),
|
||||
**{param: channel_bool_prop(param) for param in ["mute", "mono"]},
|
||||
"eq": channel_bool_prop("eq.On"),
|
||||
"eq_ab": channel_bool_prop("eq.ab"),
|
||||
"label": channel_label_prop(),
|
||||
},
|
||||
)
|
||||
return OB_cls(remote, index, *args, **kwargs)
|
||||
@ -30,12 +33,6 @@ class OutputBus(Channel):
|
||||
def identifier(self):
|
||||
return "bus"
|
||||
|
||||
eq = channel_bool_prop("eq.On")
|
||||
|
||||
eq_ab = channel_bool_prop("eq.ab")
|
||||
|
||||
label = channel_label_prop()
|
||||
|
||||
@property
|
||||
def gain(self) -> float:
|
||||
def fget():
|
||||
|
@ -103,8 +103,12 @@ class Channel(abc.ABC):
|
||||
return self._remote.public_packet
|
||||
|
||||
def apply(self, mapping):
|
||||
"""Sets all parameters of a dict for the strip."""
|
||||
"""Sets all parameters of a dict for the channel."""
|
||||
script = ""
|
||||
for key, val in mapping.items():
|
||||
if not hasattr(self, key):
|
||||
raise VMCMDErrors(f"Invalid {self.identifier} attribute: {key}")
|
||||
setattr(self, key, val)
|
||||
self._remote.cache[f"{self.identifier}[{self.index}].{key}"] = val
|
||||
script += f"{self.identifier}[{self.index}].{key}={val};"
|
||||
|
||||
self._remote.sendtext(script)
|
||||
|
@ -14,7 +14,7 @@ def channel_bool_prop(param):
|
||||
getattr(self.public_packet, f"{self.identifier}state")[self.index],
|
||||
"little",
|
||||
)
|
||||
& getattr(self._modes, f"_{param}")
|
||||
& getattr(self._modes, f'_{param.replace(".", "_").lower()}')
|
||||
== 0
|
||||
)
|
||||
|
||||
|
@ -25,6 +25,7 @@ class InputStrip(Channel):
|
||||
param: channel_bool_prop(param)
|
||||
for param in ["mono", "solo", "mute"]
|
||||
},
|
||||
"label": channel_label_prop(),
|
||||
},
|
||||
)
|
||||
return IS_cls(remote, index, **kwargs)
|
||||
@ -33,8 +34,6 @@ class InputStrip(Channel):
|
||||
def identifier(self):
|
||||
return "strip"
|
||||
|
||||
label = channel_label_prop()
|
||||
|
||||
@property
|
||||
def limit(self) -> int:
|
||||
return
|
||||
|
@ -34,6 +34,12 @@ def cache_string(func, param):
|
||||
return wrapper
|
||||
|
||||
|
||||
def depth(d):
|
||||
if isinstance(d, dict):
|
||||
return 1 + (max(map(depth, d.values())) if d else 0)
|
||||
return 0
|
||||
|
||||
|
||||
def script(func):
|
||||
"""Convert dictionary to script"""
|
||||
|
||||
|
@ -67,7 +67,6 @@ class VbanCmd(abc.ABC):
|
||||
self.running = True
|
||||
self._pdirty = False
|
||||
self.cache = {}
|
||||
self.in_apply = False
|
||||
|
||||
def __enter__(self):
|
||||
self.login()
|
||||
@ -156,10 +155,6 @@ class VbanCmd(abc.ABC):
|
||||
while self.pdirty:
|
||||
pass
|
||||
|
||||
@public_packet.setter
|
||||
def public_packet(self, val):
|
||||
self._public_packet = val
|
||||
|
||||
def _keepupdated(self) -> NoReturn:
|
||||
"""
|
||||
Continously update public packet in background.
|
||||
@ -173,8 +168,8 @@ class VbanCmd(abc.ABC):
|
||||
while self.running:
|
||||
private_packet = self._get_rt()
|
||||
self._pdirty = private_packet.isdirty(self.public_packet)
|
||||
if not private_packet.__eq__(self.public_packet):
|
||||
self.public_packet = private_packet
|
||||
if not private_packet == self.public_packet:
|
||||
self._public_packet = private_packet
|
||||
|
||||
def _get_rt(self) -> VBAN_VMRT_Packet_Data:
|
||||
"""Attempt to fetch data packet until a valid one found"""
|
||||
@ -204,8 +199,6 @@ class VbanCmd(abc.ABC):
|
||||
self._text_header.framecounter = count.to_bytes(4, "little")
|
||||
if param:
|
||||
self.cache[f"{id_}.{param}"] = val
|
||||
if self._sync or self.in_apply:
|
||||
sleep(self._delay)
|
||||
|
||||
@script
|
||||
def sendtext(self, cmd):
|
||||
@ -241,7 +234,6 @@ class VbanCmd(abc.ABC):
|
||||
|
||||
def apply(self, mapping: dict):
|
||||
"""Sets all parameters of a di"""
|
||||
self.in_apply = True
|
||||
for key, submapping in mapping.items():
|
||||
obj, index = key.split("-")
|
||||
|
||||
@ -252,7 +244,6 @@ class VbanCmd(abc.ABC):
|
||||
else:
|
||||
raise ValueError(obj)
|
||||
target.apply(submapping)
|
||||
self.in_apply = False
|
||||
|
||||
def apply_profile(self, name: str):
|
||||
try:
|
||||
@ -266,7 +257,7 @@ class VbanCmd(abc.ABC):
|
||||
else:
|
||||
base[key] = profile[key]
|
||||
profile = base
|
||||
self.sendtext(profile)
|
||||
self.apply(profile)
|
||||
except KeyError:
|
||||
raise VMCMDErrors(f"Unknown profile: {self.kind.id}/{name}")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user