mirror of
				https://github.com/onyx-and-iris/vban-cmd-python.git
				synced 2025-11-04 07:21:49 +00:00 
			
		
		
		
	make changes to sockets.
replace black+isort with ruff upd examples
This commit is contained in:
		
							parent
							
								
									dad5ee9e9d
								
							
						
					
					
						commit
						16df0d559e
					
				@ -1,8 +1,7 @@
 | 
				
			|||||||
[](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://python-poetry.org/)
 | 
				
			||||||
[](https://github.com/psf/black)
 | 
					[](https://github.com/astral-sh/ruff)
 | 
				
			||||||
[](https://pycqa.github.io/isort/)
 | 
					 | 
				
			||||||

 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										22
									
								
								__main__.py
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								__main__.py
									
									
									
									
									
								
							@ -6,27 +6,27 @@ class ManyThings:
 | 
				
			|||||||
        self.vban = vban
 | 
					        self.vban = vban
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def things(self):
 | 
					    def things(self):
 | 
				
			||||||
        self.vban.strip[0].label = "podmic"
 | 
					        self.vban.strip[0].label = 'podmic'
 | 
				
			||||||
        self.vban.strip[0].mute = True
 | 
					        self.vban.strip[0].mute = True
 | 
				
			||||||
        print(
 | 
					        print(
 | 
				
			||||||
            f"strip 0 ({self.vban.strip[0].label}) mute has been set to {self.vban.strip[0].mute}"
 | 
					            f'strip 0 ({self.vban.strip[0].label}) mute has been set to {self.vban.strip[0].mute}'
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def other_things(self):
 | 
					    def other_things(self):
 | 
				
			||||||
        self.vban.bus[3].gain = -6.3
 | 
					        self.vban.bus[3].gain = -6.3
 | 
				
			||||||
        self.vban.bus[4].eq = True
 | 
					        self.vban.bus[4].eq = True
 | 
				
			||||||
        info = (
 | 
					        info = (
 | 
				
			||||||
            f"bus 3 gain has been set to {self.vban.bus[3].gain}",
 | 
					            f'bus 3 gain has been set to {self.vban.bus[3].gain}',
 | 
				
			||||||
            f"bus 4 eq has been set to {self.vban.bus[4].eq}",
 | 
					            f'bus 4 eq has been set to {self.vban.bus[4].eq}',
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        print("\n".join(info))
 | 
					        print('\n'.join(info))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def main():
 | 
					def main():
 | 
				
			||||||
    kind_id = "banana"
 | 
					    kind_id = 'banana'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    with vban_cmd.api(
 | 
					    with vban_cmd.api(
 | 
				
			||||||
        kind_id, ip="gamepc.local", port=6980, streamname="Command1"
 | 
					        kind_id, ip='gamepc.local', port=6980, streamname='Command1'
 | 
				
			||||||
    ) as vban:
 | 
					    ) as vban:
 | 
				
			||||||
        do = ManyThings(vban)
 | 
					        do = ManyThings(vban)
 | 
				
			||||||
        do.things()
 | 
					        do.things()
 | 
				
			||||||
@ -35,12 +35,12 @@ def main():
 | 
				
			|||||||
        # set many parameters at once
 | 
					        # set many parameters at once
 | 
				
			||||||
        vban.apply(
 | 
					        vban.apply(
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                "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},
 | 
					                'vban-in-0': {'on': True},
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == '__main__':
 | 
				
			||||||
    main()
 | 
					    main()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,10 @@
 | 
				
			|||||||
import logging
 | 
					import logging
 | 
				
			||||||
 | 
					import tkinter as tk
 | 
				
			||||||
 | 
					from tkinter import ttk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import vban_cmd
 | 
					import vban_cmd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logging.basicConfig(level=logging.DEBUG)
 | 
					logging.basicConfig(level=logging.DEBUG)
 | 
				
			||||||
import tkinter as tk
 | 
					 | 
				
			||||||
from tkinter import ttk
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class App(tk.Tk):
 | 
					class App(tk.Tk):
 | 
				
			||||||
@ -13,7 +13,7 @@ class App(tk.Tk):
 | 
				
			|||||||
    def __init__(self, vban):
 | 
					    def __init__(self, vban):
 | 
				
			||||||
        super().__init__()
 | 
					        super().__init__()
 | 
				
			||||||
        self.vban = vban
 | 
					        self.vban = vban
 | 
				
			||||||
        self.title(f"{vban} - version {vban.version}")
 | 
					        self.title(f'{vban} - version {vban.version}')
 | 
				
			||||||
        self.vban.observer.add(self.on_ldirty)
 | 
					        self.vban.observer.add(self.on_ldirty)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # create widget variables
 | 
					        # create widget variables
 | 
				
			||||||
@ -24,10 +24,10 @@ class App(tk.Tk):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # initialize style table
 | 
					        # initialize style table
 | 
				
			||||||
        self.style = ttk.Style()
 | 
					        self.style = ttk.Style()
 | 
				
			||||||
        self.style.theme_use("clam")
 | 
					        self.style.theme_use('clam')
 | 
				
			||||||
        self.style.configure(
 | 
					        self.style.configure(
 | 
				
			||||||
            "Mute.TButton",
 | 
					            'Mute.TButton',
 | 
				
			||||||
            foreground="#cd5c5c" if vban.strip[self.INDEX].mute else "#5a5a5a",
 | 
					            foreground='#cd5c5c' if vban.strip[self.INDEX].mute else '#5a5a5a',
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # create labelframe and grid it onto the mainframe
 | 
					        # create labelframe and grid it onto the mainframe
 | 
				
			||||||
@ -39,7 +39,7 @@ class App(tk.Tk):
 | 
				
			|||||||
            self.labelframe,
 | 
					            self.labelframe,
 | 
				
			||||||
            from_=12,
 | 
					            from_=12,
 | 
				
			||||||
            to_=-60,
 | 
					            to_=-60,
 | 
				
			||||||
            orient="vertical",
 | 
					            orient='vertical',
 | 
				
			||||||
            variable=self.slider_var,
 | 
					            variable=self.slider_var,
 | 
				
			||||||
            command=lambda arg: self.on_slider_move(arg),
 | 
					            command=lambda arg: self.on_slider_move(arg),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
@ -47,15 +47,15 @@ class App(tk.Tk):
 | 
				
			|||||||
            column=0,
 | 
					            column=0,
 | 
				
			||||||
            row=0,
 | 
					            row=0,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        slider.bind("<Double-Button-1>", self.on_button_double_click)
 | 
					        slider.bind('<Double-Button-1>', self.on_button_double_click)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # create level meter and grid it onto the labelframe
 | 
					        # create level meter and grid it onto the labelframe
 | 
				
			||||||
        level_meter = ttk.Progressbar(
 | 
					        level_meter = ttk.Progressbar(
 | 
				
			||||||
            self.labelframe,
 | 
					            self.labelframe,
 | 
				
			||||||
            orient="vertical",
 | 
					            orient='vertical',
 | 
				
			||||||
            variable=self.meter_var,
 | 
					            variable=self.meter_var,
 | 
				
			||||||
            maximum=72,
 | 
					            maximum=72,
 | 
				
			||||||
            mode="determinate",
 | 
					            mode='determinate',
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        level_meter.grid(column=1, row=0)
 | 
					        level_meter.grid(column=1, row=0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -66,8 +66,8 @@ class App(tk.Tk):
 | 
				
			|||||||
        # create button and grid it onto the labelframe
 | 
					        # create button and grid it onto the labelframe
 | 
				
			||||||
        button = ttk.Button(
 | 
					        button = ttk.Button(
 | 
				
			||||||
            self.labelframe,
 | 
					            self.labelframe,
 | 
				
			||||||
            text="Mute",
 | 
					            text='Mute',
 | 
				
			||||||
            style="Mute.TButton",
 | 
					            style='Mute.TButton',
 | 
				
			||||||
            command=lambda: self.on_button_press(),
 | 
					            command=lambda: self.on_button_press(),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        button.grid(column=0, row=2, columnspan=2, padx=1, pady=2)
 | 
					        button.grid(column=0, row=2, columnspan=2, padx=1, pady=2)
 | 
				
			||||||
@ -83,7 +83,7 @@ class App(tk.Tk):
 | 
				
			|||||||
        self.button_var.set(not self.button_var.get())
 | 
					        self.button_var.set(not self.button_var.get())
 | 
				
			||||||
        self.vban.strip[self.INDEX].mute = self.button_var.get()
 | 
					        self.vban.strip[self.INDEX].mute = self.button_var.get()
 | 
				
			||||||
        self.style.configure(
 | 
					        self.style.configure(
 | 
				
			||||||
            "Mute.TButton", foreground="#cd5c5c" if self.button_var.get() else "#5a5a5a"
 | 
					            'Mute.TButton', foreground='#cd5c5c' if self.button_var.get() else '#5a5a5a'
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def on_button_double_click(self, e):
 | 
					    def on_button_double_click(self, e):
 | 
				
			||||||
@ -100,10 +100,10 @@ class App(tk.Tk):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def main():
 | 
					def main():
 | 
				
			||||||
    with vban_cmd.api("banana", ldirty=True) as vban:
 | 
					    with vban_cmd.api('banana', ldirty=True) as vban:
 | 
				
			||||||
        app = App(vban)
 | 
					        app = App(vban)
 | 
				
			||||||
        app.mainloop()
 | 
					        app.mainloop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == '__main__':
 | 
				
			||||||
    main()
 | 
					    main()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import time
 | 
					import threading
 | 
				
			||||||
from logging import config
 | 
					from logging import config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import obsws_python as obsws
 | 
					import obsws_python as obsws
 | 
				
			||||||
@ -7,85 +7,98 @@ import vban_cmd
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
config.dictConfig(
 | 
					config.dictConfig(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        "version": 1,
 | 
					        'version': 1,
 | 
				
			||||||
        "formatters": {
 | 
					        'formatters': {
 | 
				
			||||||
            "standard": {
 | 
					            'standard': {
 | 
				
			||||||
                "format": "%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s"
 | 
					                'format': '%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s'
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "handlers": {
 | 
					        'handlers': {
 | 
				
			||||||
            "stream": {
 | 
					            'stream': {
 | 
				
			||||||
                "level": "DEBUG",
 | 
					                'level': 'DEBUG',
 | 
				
			||||||
                "class": "logging.StreamHandler",
 | 
					                'class': 'logging.StreamHandler',
 | 
				
			||||||
                "formatter": "standard",
 | 
					                'formatter': 'standard',
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "loggers": {"vban_cmd.iremote": {"handlers": ["stream"], "level": "DEBUG"}},
 | 
					        'loggers': {
 | 
				
			||||||
 | 
					            'vban_cmd.iremote': {
 | 
				
			||||||
 | 
					                'handlers': ['stream'],
 | 
				
			||||||
 | 
					                'level': 'DEBUG',
 | 
				
			||||||
 | 
					                'propagate': False,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'root': {'handlers': ['stream'], 'level': 'WARNING'},
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Observer:
 | 
					class Observer:
 | 
				
			||||||
    def __init__(self, vban):
 | 
					    def __init__(self, vban, stop_event):
 | 
				
			||||||
        self.vban = vban
 | 
					        self._vban = vban
 | 
				
			||||||
        self.client = obsws.EventClient()
 | 
					        self._stop_event = stop_event
 | 
				
			||||||
        self.client.callback.register(
 | 
					        self._client = obsws.EventClient()
 | 
				
			||||||
 | 
					        self._client.callback.register(
 | 
				
			||||||
            (
 | 
					            (
 | 
				
			||||||
                self.on_current_program_scene_changed,
 | 
					                self.on_current_program_scene_changed,
 | 
				
			||||||
                self.on_exit_started,
 | 
					                self.on_exit_started,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.is_running = True
 | 
					
 | 
				
			||||||
 | 
					    def __enter__(self):
 | 
				
			||||||
 | 
					        return self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __exit__(self, exc_type, exc_value, exc_traceback):
 | 
				
			||||||
 | 
					        self._client.disconnect()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def on_start(self):
 | 
					    def on_start(self):
 | 
				
			||||||
        self.vban.strip[0].mute = True
 | 
					        self._vban.strip[0].mute = True
 | 
				
			||||||
        self.vban.strip[1].B1 = True
 | 
					        self._vban.strip[1].B1 = True
 | 
				
			||||||
        self.vban.strip[2].B2 = True
 | 
					        self._vban.strip[2].B2 = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def on_brb(self):
 | 
					    def on_brb(self):
 | 
				
			||||||
        self.vban.strip[7].fadeto(0, 500)
 | 
					        self._vban.strip[7].fadeto(0, 500)
 | 
				
			||||||
        self.vban.bus[0].mute = True
 | 
					        self._vban.bus[0].mute = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def on_end(self):
 | 
					    def on_end(self):
 | 
				
			||||||
        self.vban.apply(
 | 
					        self._vban.apply(
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                "strip-0": {"mute": True},
 | 
					                'strip-0': {'mute': True},
 | 
				
			||||||
                "strip-1": {"mute": True, "B1": False},
 | 
					                'strip-1': {'mute': True, 'B1': False},
 | 
				
			||||||
                "strip-2": {"mute": True, "B1": False},
 | 
					                'strip-2': {'mute': True, 'B1': False},
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def on_live(self):
 | 
					    def on_live(self):
 | 
				
			||||||
        self.vban.strip[0].mute = False
 | 
					        self._vban.strip[0].mute = False
 | 
				
			||||||
        self.vban.strip[7].fadeto(-6, 500)
 | 
					        self._vban.strip[7].fadeto(-6, 500)
 | 
				
			||||||
        self.vban.strip[7].A3 = True
 | 
					        self._vban.strip[7].A3 = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def on_current_program_scene_changed(self, data):
 | 
					    def on_current_program_scene_changed(self, data):
 | 
				
			||||||
        def fget(scene):
 | 
					 | 
				
			||||||
            run = {
 | 
					 | 
				
			||||||
                "START": self.on_start,
 | 
					 | 
				
			||||||
                "BRB": self.on_brb,
 | 
					 | 
				
			||||||
                "END": self.on_end,
 | 
					 | 
				
			||||||
                "LIVE": self.on_live,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return run.get(scene)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        scene = data.scene_name
 | 
					        scene = data.scene_name
 | 
				
			||||||
        print(f"Switched to scene {scene}")
 | 
					        print(f'Switched to scene {scene}')
 | 
				
			||||||
        if fn := fget(scene):
 | 
					        match scene:
 | 
				
			||||||
            fn()
 | 
					            case 'START':
 | 
				
			||||||
 | 
					                self.on_start()
 | 
				
			||||||
 | 
					            case 'BRB':
 | 
				
			||||||
 | 
					                self.on_brb()
 | 
				
			||||||
 | 
					            case 'END':
 | 
				
			||||||
 | 
					                self.on_end()
 | 
				
			||||||
 | 
					            case 'LIVE':
 | 
				
			||||||
 | 
					                self.on_live()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def on_exit_started(self, _):
 | 
					    def on_exit_started(self, _):
 | 
				
			||||||
        self.client.unsubscribe()
 | 
					        self._stop_event.set()
 | 
				
			||||||
        self.is_running = False
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def main():
 | 
					def main():
 | 
				
			||||||
    with vban_cmd.api("potato") as vban:
 | 
					    KIND_ID = 'potato'
 | 
				
			||||||
        observer = Observer(vban)
 | 
					
 | 
				
			||||||
        while observer.is_running:
 | 
					    with vban_cmd.api(KIND_ID) as vban:
 | 
				
			||||||
            time.sleep(0.1)
 | 
					        stop_event = threading.Event()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with Observer(vban, stop_event):
 | 
				
			||||||
 | 
					            stop_event.wait()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == '__main__':
 | 
				
			||||||
    main()
 | 
					    main()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
from setuptools import setup
 | 
					from setuptools import setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
setup(
 | 
					setup(
 | 
				
			||||||
    name="obs",
 | 
					    name='obs',
 | 
				
			||||||
    description="OBS Example",
 | 
					    description='OBS Example',
 | 
				
			||||||
    install_requires=["obsws-python"],
 | 
					    install_requires=['obsws-python'],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
@ -13,23 +13,23 @@ class App:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    # define an 'on_update' callback function to receive event updates
 | 
					    # define an 'on_update' callback function to receive event updates
 | 
				
			||||||
    def on_update(self, event):
 | 
					    def on_update(self, event):
 | 
				
			||||||
        if event == "pdirty":
 | 
					        if event == 'pdirty':
 | 
				
			||||||
            print("pdirty!")
 | 
					            print('pdirty!')
 | 
				
			||||||
        elif event == "ldirty":
 | 
					        elif event == 'ldirty':
 | 
				
			||||||
            for bus in self.vban.bus:
 | 
					            for bus in self.vban.bus:
 | 
				
			||||||
                if bus.levels.isdirty:
 | 
					                if bus.levels.isdirty:
 | 
				
			||||||
                    print(bus, bus.levels.all)
 | 
					                    print(bus, bus.levels.all)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def main():
 | 
					def main():
 | 
				
			||||||
    KIND_ID = "banana"
 | 
					    KIND_ID = 'banana'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    with vban_cmd.api(KIND_ID, pdirty=True, ldirty=True) as vban:
 | 
					    with vban_cmd.api(KIND_ID, pdirty=True, ldirty=True) as vban:
 | 
				
			||||||
        App(vban)
 | 
					        App(vban)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        while cmd := input("Press <Enter> to exit\n"):
 | 
					        while _ := input('Press <Enter> to exit\n'):
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == '__main__':
 | 
				
			||||||
    main()
 | 
					    main()
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										360
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										360
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							@ -1,93 +1,36 @@
 | 
				
			|||||||
# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
 | 
					# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.
 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "black"
 | 
					 | 
				
			||||||
version = "24.3.0"
 | 
					 | 
				
			||||||
description = "The uncompromising code formatter."
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.8"
 | 
					 | 
				
			||||||
files = [
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"},
 | 
					 | 
				
			||||||
    {file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
click = ">=8.0.0"
 | 
					 | 
				
			||||||
mypy-extensions = ">=0.4.3"
 | 
					 | 
				
			||||||
packaging = ">=22.0"
 | 
					 | 
				
			||||||
pathspec = ">=0.9.0"
 | 
					 | 
				
			||||||
platformdirs = ">=2"
 | 
					 | 
				
			||||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
 | 
					 | 
				
			||||||
typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
colorama = ["colorama (>=0.4.3)"]
 | 
					 | 
				
			||||||
d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
 | 
					 | 
				
			||||||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
 | 
					 | 
				
			||||||
uvloop = ["uvloop (>=0.15.2)"]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "cachetools"
 | 
					name = "cachetools"
 | 
				
			||||||
version = "5.3.1"
 | 
					version = "5.5.0"
 | 
				
			||||||
description = "Extensible memoizing collections and decorators"
 | 
					description = "Extensible memoizing collections and decorators"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7"
 | 
					python-versions = ">=3.7"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"},
 | 
					    {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"},
 | 
				
			||||||
    {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"},
 | 
					    {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "chardet"
 | 
					name = "chardet"
 | 
				
			||||||
version = "5.1.0"
 | 
					version = "5.2.0"
 | 
				
			||||||
description = "Universal encoding detector for Python 3"
 | 
					description = "Universal encoding detector for Python 3"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7"
 | 
					python-versions = ">=3.7"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "chardet-5.1.0-py3-none-any.whl", hash = "sha256:362777fb014af596ad31334fde1e8c327dfdb076e1960d1694662d46a6917ab9"},
 | 
					    {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"},
 | 
				
			||||||
    {file = "chardet-5.1.0.tar.gz", hash = "sha256:0d62712b956bc154f85fb0a266e2a3c5913c2967e00348701b32411d6def31e5"},
 | 
					    {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "click"
 | 
					 | 
				
			||||||
version = "8.1.3"
 | 
					 | 
				
			||||||
description = "Composable command line interface toolkit"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.7"
 | 
					 | 
				
			||||||
files = [
 | 
					 | 
				
			||||||
    {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
 | 
					 | 
				
			||||||
    {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "colorama"
 | 
					name = "colorama"
 | 
				
			||||||
version = "0.4.6"
 | 
					version = "0.4.6"
 | 
				
			||||||
description = "Cross-platform colored terminal text."
 | 
					description = "Cross-platform colored terminal text."
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
 | 
					python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
 | 
					    {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
 | 
				
			||||||
    {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
 | 
					    {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
 | 
				
			||||||
@ -99,6 +42,7 @@ version = "0.3.9"
 | 
				
			|||||||
description = "Distribution utilities"
 | 
					description = "Distribution utilities"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = "*"
 | 
					python-versions = "*"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"},
 | 
					    {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"},
 | 
				
			||||||
    {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"},
 | 
					    {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"},
 | 
				
			||||||
@ -106,13 +50,15 @@ files = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "exceptiongroup"
 | 
					name = "exceptiongroup"
 | 
				
			||||||
version = "1.2.0"
 | 
					version = "1.2.2"
 | 
				
			||||||
description = "Backport of PEP 654 (exception groups)"
 | 
					description = "Backport of PEP 654 (exception groups)"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7"
 | 
					python-versions = ">=3.7"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
 | 
					markers = "python_version < \"3.11\""
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
 | 
					    {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
 | 
				
			||||||
    {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
 | 
					    {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.extras]
 | 
					[package.extras]
 | 
				
			||||||
@ -120,78 +66,43 @@ test = ["pytest (>=6)"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "filelock"
 | 
					name = "filelock"
 | 
				
			||||||
version = "3.12.2"
 | 
					version = "3.16.1"
 | 
				
			||||||
description = "A platform independent file lock."
 | 
					description = "A platform independent file lock."
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"},
 | 
					    {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"},
 | 
				
			||||||
    {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"},
 | 
					    {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.extras]
 | 
					[package.extras]
 | 
				
			||||||
docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
 | 
					docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"]
 | 
				
			||||||
testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"]
 | 
					testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"]
 | 
				
			||||||
 | 
					typing = ["typing-extensions (>=4.12.2)"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "iniconfig"
 | 
					name = "iniconfig"
 | 
				
			||||||
version = "1.1.1"
 | 
					version = "2.0.0"
 | 
				
			||||||
description = "iniconfig: brain-dead simple config-ini parsing"
 | 
					description = "brain-dead simple config-ini parsing"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = "*"
 | 
					python-versions = ">=3.7"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
 | 
					    {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
 | 
				
			||||||
    {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
 | 
					    {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "isort"
 | 
					 | 
				
			||||||
version = "5.10.1"
 | 
					 | 
				
			||||||
description = "A Python utility / library to sort Python imports."
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.6.1,<4.0"
 | 
					 | 
				
			||||||
files = [
 | 
					 | 
				
			||||||
    {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
 | 
					 | 
				
			||||||
    {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
colors = ["colorama (>=0.4.3,<0.5.0)"]
 | 
					 | 
				
			||||||
pipfile-deprecated-finder = ["pipreqs", "requirementslib"]
 | 
					 | 
				
			||||||
plugins = ["setuptools"]
 | 
					 | 
				
			||||||
requirements-deprecated-finder = ["pip-api", "pipreqs"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "mypy-extensions"
 | 
					 | 
				
			||||||
version = "0.4.3"
 | 
					 | 
				
			||||||
description = "Experimental type system extensions for programs checked with the mypy typechecker."
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = "*"
 | 
					 | 
				
			||||||
files = [
 | 
					 | 
				
			||||||
    {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
 | 
					 | 
				
			||||||
    {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
 | 
					 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "packaging"
 | 
					name = "packaging"
 | 
				
			||||||
version = "23.1"
 | 
					version = "24.2"
 | 
				
			||||||
description = "Core utilities for Python packages"
 | 
					description = "Core utilities for Python packages"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"},
 | 
					    {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
 | 
				
			||||||
    {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"},
 | 
					    {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "pathspec"
 | 
					 | 
				
			||||||
version = "0.10.1"
 | 
					 | 
				
			||||||
description = "Utility library for gitignore style pattern matching of file paths."
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.7"
 | 
					 | 
				
			||||||
files = [
 | 
					 | 
				
			||||||
    {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"},
 | 
					 | 
				
			||||||
    {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"},
 | 
					 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@ -200,6 +111,7 @@ version = "4.3.6"
 | 
				
			|||||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
 | 
					description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
 | 
					    {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
 | 
				
			||||||
    {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
 | 
					    {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
 | 
				
			||||||
@ -212,47 +124,62 @@ type = ["mypy (>=1.11.2)"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pluggy"
 | 
					name = "pluggy"
 | 
				
			||||||
version = "1.0.0"
 | 
					version = "1.5.0"
 | 
				
			||||||
description = "plugin and hook calling mechanisms for python"
 | 
					description = "plugin and hook calling mechanisms for python"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.6"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
 | 
					    {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
 | 
				
			||||||
    {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
 | 
					    {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.extras]
 | 
					[package.extras]
 | 
				
			||||||
dev = ["pre-commit", "tox"]
 | 
					dev = ["pre-commit", "tox"]
 | 
				
			||||||
testing = ["pytest", "pytest-benchmark"]
 | 
					testing = ["pytest", "pytest-benchmark"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "pyenv-inspect"
 | 
				
			||||||
 | 
					version = "0.4.0"
 | 
				
			||||||
 | 
					description = "An auxiliary library for the virtualenv-pyenv and tox-pyenv-redux plugins"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "pyenv-inspect-0.4.0.tar.gz", hash = "sha256:ec429d1d81b67ab0b08a0408414722a79d24fd1845a5b264267e44e19d8d60f0"},
 | 
				
			||||||
 | 
					    {file = "pyenv_inspect-0.4.0-py3-none-any.whl", hash = "sha256:618683ae7d3e6db14778d58aa0fc6b3170180d944669b5d35a8aa4fb7db550d2"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pyproject-api"
 | 
					name = "pyproject-api"
 | 
				
			||||||
version = "1.5.2"
 | 
					version = "1.8.0"
 | 
				
			||||||
description = "API to interact with the python pyproject.toml based projects"
 | 
					description = "API to interact with the python pyproject.toml based projects"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "pyproject_api-1.5.2-py3-none-any.whl", hash = "sha256:9cffcbfb64190f207444d7579d315f3278f2c04ba46d685fad93197b5326d348"},
 | 
					    {file = "pyproject_api-1.8.0-py3-none-any.whl", hash = "sha256:3d7d347a047afe796fd5d1885b1e391ba29be7169bd2f102fcd378f04273d228"},
 | 
				
			||||||
    {file = "pyproject_api-1.5.2.tar.gz", hash = "sha256:999f58fa3c92b23ebd31a6bad5d1f87d456744d75e05391be7f5c729015d3d91"},
 | 
					    {file = "pyproject_api-1.8.0.tar.gz", hash = "sha256:77b8049f2feb5d33eefcc21b57f1e279636277a8ac8ad6b5871037b243778496"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
packaging = ">=23.1"
 | 
					packaging = ">=24.1"
 | 
				
			||||||
tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
 | 
					tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.extras]
 | 
					[package.extras]
 | 
				
			||||||
docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
 | 
					docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=2.4.1)"]
 | 
				
			||||||
testing = ["covdefaults (>=2.3)", "importlib-metadata (>=6.6)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "setuptools (>=67.8)", "wheel (>=0.40)"]
 | 
					testing = ["covdefaults (>=2.3)", "pytest (>=8.3.3)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "setuptools (>=75.1)"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pytest"
 | 
					name = "pytest"
 | 
				
			||||||
version = "7.4.4"
 | 
					version = "8.3.4"
 | 
				
			||||||
description = "pytest: simple powerful testing with Python"
 | 
					description = "pytest: simple powerful testing with Python"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
 | 
					    {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"},
 | 
				
			||||||
    {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
 | 
					    {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -260,98 +187,149 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""}
 | 
				
			|||||||
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
 | 
					exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
 | 
				
			||||||
iniconfig = "*"
 | 
					iniconfig = "*"
 | 
				
			||||||
packaging = "*"
 | 
					packaging = "*"
 | 
				
			||||||
pluggy = ">=0.12,<2.0"
 | 
					pluggy = ">=1.5,<2"
 | 
				
			||||||
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
 | 
					tomli = {version = ">=1", markers = "python_version < \"3.11\""}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.extras]
 | 
					[package.extras]
 | 
				
			||||||
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
 | 
					dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pytest-randomly"
 | 
					name = "pytest-randomly"
 | 
				
			||||||
version = "3.12.0"
 | 
					version = "3.16.0"
 | 
				
			||||||
description = "Pytest plugin to randomly order tests and control random.seed."
 | 
					description = "Pytest plugin to randomly order tests and control random.seed."
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7"
 | 
					python-versions = ">=3.9"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "pytest-randomly-3.12.0.tar.gz", hash = "sha256:d60c2db71ac319aee0fc6c4110a7597d611a8b94a5590918bfa8583f00caccb2"},
 | 
					    {file = "pytest_randomly-3.16.0-py3-none-any.whl", hash = "sha256:8633d332635a1a0983d3bba19342196807f6afb17c3eef78e02c2f85dade45d6"},
 | 
				
			||||||
    {file = "pytest_randomly-3.12.0-py3-none-any.whl", hash = "sha256:f4f2e803daf5d1ba036cc22bf4fe9dbbf99389ec56b00e5cba732fb5c1d07fdd"},
 | 
					    {file = "pytest_randomly-3.16.0.tar.gz", hash = "sha256:11bf4d23a26484de7860d82f726c0629837cf4064b79157bd18ec9d41d7feb26"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
pytest = "*"
 | 
					pytest = "*"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pytest-repeat"
 | 
					name = "ruff"
 | 
				
			||||||
version = "0.9.1"
 | 
					version = "0.9.2"
 | 
				
			||||||
description = "pytest plugin for repeating tests"
 | 
					description = "An extremely fast Python linter and code formatter, written in Rust."
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
 | 
					python-versions = ">=3.7"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "pytest-repeat-0.9.1.tar.gz", hash = "sha256:5cd3289745ab3156d43eb9c8e7f7d00a926f3ae5c9cf425bec649b2fe15bad5b"},
 | 
					    {file = "ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347"},
 | 
				
			||||||
    {file = "pytest_repeat-0.9.1-py2.py3-none-any.whl", hash = "sha256:4474a7d9e9137f6d8cc8ae297f8c4168d33c56dd740aa78cfffe562557e6b96e"},
 | 
					    {file = "ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
pytest = ">=3.6"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "tomli"
 | 
					name = "tomli"
 | 
				
			||||||
version = "2.0.1"
 | 
					version = "2.2.1"
 | 
				
			||||||
description = "A lil' TOML parser"
 | 
					description = "A lil' TOML parser"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["main", "dev"]
 | 
				
			||||||
 | 
					markers = "python_version < \"3.11\""
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
 | 
					    {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
 | 
				
			||||||
    {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
 | 
					    {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"},
 | 
				
			||||||
 | 
					    {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "tox"
 | 
					name = "tox"
 | 
				
			||||||
version = "4.6.3"
 | 
					version = "4.23.2"
 | 
				
			||||||
description = "tox is a generic virtualenv management and test command line tool"
 | 
					description = "tox is a generic virtualenv management and test command line tool"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "tox-4.6.3-py3-none-any.whl", hash = "sha256:2946a0bb38924c3a9f9575c7fb4ca1f4c11a7c69c61592f176778892155cb50c"},
 | 
					    {file = "tox-4.23.2-py3-none-any.whl", hash = "sha256:452bc32bb031f2282881a2118923176445bac783ab97c874b8770ab4c3b76c38"},
 | 
				
			||||||
    {file = "tox-4.6.3.tar.gz", hash = "sha256:9e2c5091a117d03b583c57c4c40aecd068099c17d40520e7b165e85c19334534"},
 | 
					    {file = "tox-4.23.2.tar.gz", hash = "sha256:86075e00e555df6e82e74cfc333917f91ecb47ffbc868dcafbd2672e332f4a2c"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
cachetools = ">=5.3.1"
 | 
					cachetools = ">=5.5"
 | 
				
			||||||
chardet = ">=5.1"
 | 
					chardet = ">=5.2"
 | 
				
			||||||
colorama = ">=0.4.6"
 | 
					colorama = ">=0.4.6"
 | 
				
			||||||
filelock = ">=3.12.2"
 | 
					filelock = ">=3.16.1"
 | 
				
			||||||
packaging = ">=23.1"
 | 
					packaging = ">=24.1"
 | 
				
			||||||
platformdirs = ">=3.5.3"
 | 
					platformdirs = ">=4.3.6"
 | 
				
			||||||
pluggy = ">=1"
 | 
					pluggy = ">=1.5"
 | 
				
			||||||
pyproject-api = ">=1.5.2"
 | 
					pyproject-api = ">=1.8"
 | 
				
			||||||
tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
 | 
					tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
 | 
				
			||||||
virtualenv = ">=20.23.1"
 | 
					typing-extensions = {version = ">=4.12.2", markers = "python_version < \"3.11\""}
 | 
				
			||||||
 | 
					virtualenv = ">=20.26.6"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.extras]
 | 
					[package.extras]
 | 
				
			||||||
docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.23.2,!=1.23.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
 | 
					test = ["devpi-process (>=1.0.2)", "pytest (>=8.3.3)", "pytest-mock (>=3.14)"]
 | 
				
			||||||
testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.1.1)", "devpi-process (>=0.3.1)", "diff-cover (>=7.6)", "distlib (>=0.3.6)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.17.1)", "psutil (>=5.9.5)", "pytest (>=7.3.2)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-xdist (>=3.3.1)", "re-assert (>=1.1)", "time-machine (>=2.10)", "wheel (>=0.40)"]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "typing-extensions"
 | 
					name = "typing-extensions"
 | 
				
			||||||
version = "4.10.0"
 | 
					version = "4.12.2"
 | 
				
			||||||
description = "Backported and Experimental Type Hints for Python 3.8+"
 | 
					description = "Backported and Experimental Type Hints for Python 3.8+"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
 | 
					markers = "python_version < \"3.11\""
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"},
 | 
					    {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
 | 
				
			||||||
    {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"},
 | 
					    {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "virtualenv"
 | 
					name = "virtualenv"
 | 
				
			||||||
version = "20.26.6"
 | 
					version = "20.29.0"
 | 
				
			||||||
description = "Virtual Python Environment builder"
 | 
					description = "Virtual Python Environment builder"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2"},
 | 
					    {file = "virtualenv-20.29.0-py3-none-any.whl", hash = "sha256:c12311863497992dc4b8644f8ea82d3b35bb7ef8ee82e6630d76d0197c39baf9"},
 | 
				
			||||||
    {file = "virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48"},
 | 
					    {file = "virtualenv-20.29.0.tar.gz", hash = "sha256:6345e1ff19d4b1296954cee076baaf58ff2a12a84a338c62b02eda39f20aa982"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -363,7 +341,23 @@ platformdirs = ">=3.9.1,<5"
 | 
				
			|||||||
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
 | 
					docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
 | 
				
			||||||
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
 | 
					test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "virtualenv-pyenv"
 | 
				
			||||||
 | 
					version = "0.5.0"
 | 
				
			||||||
 | 
					description = "A virtualenv Python discovery plugin for pyenv-installed interpreters"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["dev"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "virtualenv-pyenv-0.5.0.tar.gz", hash = "sha256:7b0e5fe3dfbdf484f4cf9b01e1f98111e398db6942237910f666356e6293597f"},
 | 
				
			||||||
 | 
					    {file = "virtualenv_pyenv-0.5.0-py3-none-any.whl", hash = "sha256:21750247e36c55b3c547cfdeb08f51a3867fe7129922991a4f9c96980c0a4a5d"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.dependencies]
 | 
				
			||||||
 | 
					pyenv-inspect = ">=0.4,<0.5"
 | 
				
			||||||
 | 
					virtualenv = "*"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[metadata]
 | 
					[metadata]
 | 
				
			||||||
lock-version = "2.0"
 | 
					lock-version = "2.1"
 | 
				
			||||||
python-versions = "^3.10"
 | 
					python-versions = ">=3.10"
 | 
				
			||||||
content-hash = "a42b70c2ebaa214c9447f0bfc42a75a09e63066211668202a29c7dc6c40aaab9"
 | 
					content-hash = "13fc9d0eb15d5fc09b54c1c8cd8f528b260259e97ee6813b50ab4724c35d6677"
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										142
									
								
								pyproject.toml
									
									
									
									
									
								
							
							
						
						
									
										142
									
								
								pyproject.toml
									
									
									
									
									
								
							@ -1,36 +1,39 @@
 | 
				
			|||||||
[tool.poetry]
 | 
					[project]
 | 
				
			||||||
name = "vban-cmd"
 | 
					name = "vban-cmd"
 | 
				
			||||||
version = "2.4.12"
 | 
					version = "2.5.0"
 | 
				
			||||||
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 = [
 | 
				
			||||||
license = "MIT"
 | 
					    {name = "Onyx and Iris",email = "code@onyxandiris.online"}
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					license = {text = "MIT"}
 | 
				
			||||||
readme = "README.md"
 | 
					readme = "README.md"
 | 
				
			||||||
repository = "https://github.com/onyx-and-iris/vban-cmd-python"
 | 
					requires-python = ">=3.10"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					    "tomli (>=2.0.1,<3.0) ; python_version < '3.11'",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[tool.poetry.dependencies]
 | 
					[tool.poetry.requires-plugins]
 | 
				
			||||||
python = "^3.10"
 | 
					poethepoet = "^0.32.1"
 | 
				
			||||||
tomli = { version = "^2.0.1", python = "<3.11" }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
[tool.poetry.group.dev.dependencies]
 | 
					[tool.poetry.group.dev.dependencies]
 | 
				
			||||||
pytest = "^7.4.4"
 | 
					pytest = "^8.3.4"
 | 
				
			||||||
pytest-randomly = "^3.12.0"
 | 
					pytest-randomly = "^3.16.0"
 | 
				
			||||||
pytest-repeat = "^0.9.1"
 | 
					ruff = "^0.9.2"
 | 
				
			||||||
black = ">=22.3,<25.0"
 | 
					tox = "^4.23.2"
 | 
				
			||||||
isort = "^5.10.1"
 | 
					virtualenv-pyenv = "^0.5.0"
 | 
				
			||||||
tox = "^4.6.3"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
[build-system]
 | 
					[build-system]
 | 
				
			||||||
requires = ["poetry-core>=1.0.0"]
 | 
					requires = ["poetry-core>=2.0.0,<3.0.0"]
 | 
				
			||||||
build-backend = "poetry.core.masonry.api"
 | 
					build-backend = "poetry.core.masonry.api"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[tool.poetry.scripts]
 | 
					[tool.poe.tasks]
 | 
				
			||||||
gui = "scripts:ex_gui"
 | 
					gui.script = "scripts:ex_gui"
 | 
				
			||||||
obs = "scripts:ex_obs"
 | 
					obs.script = "scripts:ex_obs"
 | 
				
			||||||
observer = "scripts:ex_observer"
 | 
					observer.script = "scripts:ex_observer"
 | 
				
			||||||
basic = "scripts:test_basic"
 | 
					basic.script = "scripts:test_basic"
 | 
				
			||||||
banana = "scripts:test_banana"
 | 
					banana.script = "scripts:test_banana"
 | 
				
			||||||
potato = "scripts:test_potato"
 | 
					potato.script = "scripts:test_potato"
 | 
				
			||||||
all = "scripts:test_all"
 | 
					all.script = "scripts:test_all"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[tool.tox]
 | 
					[tool.tox]
 | 
				
			||||||
legacy_tox_ini = """
 | 
					legacy_tox_ini = """
 | 
				
			||||||
@ -38,8 +41,101 @@ legacy_tox_ini = """
 | 
				
			|||||||
envlist = py310,py311,py312
 | 
					envlist = py310,py311,py312
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[testenv]
 | 
					[testenv]
 | 
				
			||||||
 | 
					setenv = VIRTUALENV_DISCOVERY=pyenv
 | 
				
			||||||
allowlist_externals = poetry
 | 
					allowlist_externals = poetry
 | 
				
			||||||
commands =
 | 
					commands =
 | 
				
			||||||
  poetry install -v
 | 
					  poetry install -v
 | 
				
			||||||
  poetry run pytest tests/
 | 
					  poetry run pytest tests/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[testenv:obs]
 | 
				
			||||||
 | 
					setenv = VIRTUALENV_DISCOVERY=pyenv
 | 
				
			||||||
 | 
					allowlist_externals = poetry
 | 
				
			||||||
 | 
					deps = obsws-python
 | 
				
			||||||
 | 
					commands =
 | 
				
			||||||
 | 
					  poetry install -v --without dev
 | 
				
			||||||
 | 
					  poetry run python examples/obs/
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[tool.ruff]
 | 
				
			||||||
 | 
					exclude = [
 | 
				
			||||||
 | 
					    ".bzr",
 | 
				
			||||||
 | 
					    ".direnv",
 | 
				
			||||||
 | 
					    ".eggs",
 | 
				
			||||||
 | 
					    ".git",
 | 
				
			||||||
 | 
					    ".git-rewrite",
 | 
				
			||||||
 | 
					    ".hg",
 | 
				
			||||||
 | 
					    ".mypy_cache",
 | 
				
			||||||
 | 
					    ".nox",
 | 
				
			||||||
 | 
					    ".pants.d",
 | 
				
			||||||
 | 
					    ".pytype",
 | 
				
			||||||
 | 
					    ".ruff_cache",
 | 
				
			||||||
 | 
					    ".svn",
 | 
				
			||||||
 | 
					    ".tox",
 | 
				
			||||||
 | 
					    ".venv",
 | 
				
			||||||
 | 
					    "__pypackages__",
 | 
				
			||||||
 | 
					    "_build",
 | 
				
			||||||
 | 
					    "buck-out",
 | 
				
			||||||
 | 
					    "build",
 | 
				
			||||||
 | 
					    "dist",
 | 
				
			||||||
 | 
					    "node_modules",
 | 
				
			||||||
 | 
					    "venv",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Same as Black.
 | 
				
			||||||
 | 
					line-length = 88
 | 
				
			||||||
 | 
					indent-width = 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Assume Python 3.10
 | 
				
			||||||
 | 
					target-version = "py310"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[tool.ruff.lint]
 | 
				
			||||||
 | 
					# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`)  codes by default.
 | 
				
			||||||
 | 
					# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
 | 
				
			||||||
 | 
					# McCabe complexity (`C901`) by default.
 | 
				
			||||||
 | 
					select = ["E4", "E7", "E9", "F"]
 | 
				
			||||||
 | 
					ignore = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Allow fix for all enabled rules (when `--fix`) is provided.
 | 
				
			||||||
 | 
					fixable = ["ALL"]
 | 
				
			||||||
 | 
					unfixable = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Allow unused variables when underscore-prefixed.
 | 
				
			||||||
 | 
					dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[tool.ruff.format]
 | 
				
			||||||
 | 
					# Unlike Black, use single quotes for strings.
 | 
				
			||||||
 | 
					quote-style = "single"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Like Black, indent with spaces, rather than tabs.
 | 
				
			||||||
 | 
					indent-style = "space"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Like Black, respect magic trailing commas.
 | 
				
			||||||
 | 
					skip-magic-trailing-comma = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Like Black, automatically detect the appropriate line ending.
 | 
				
			||||||
 | 
					line-ending = "auto"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Enable auto-formatting of code examples in docstrings. Markdown,
 | 
				
			||||||
 | 
					# reStructuredText code/literal blocks and doctests are all supported.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This is currently disabled by default, but it is planned for this
 | 
				
			||||||
 | 
					# to be opt-out in the future.
 | 
				
			||||||
 | 
					docstring-code-format = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Set the line length limit used when formatting code snippets in
 | 
				
			||||||
 | 
					# docstrings.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This only has an effect when the `docstring-code-format` setting is
 | 
				
			||||||
 | 
					# enabled.
 | 
				
			||||||
 | 
					docstring-code-line-length = "dynamic"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[tool.ruff.lint.mccabe]
 | 
				
			||||||
 | 
					max-complexity = 10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[tool.ruff.lint.per-file-ignores]
 | 
				
			||||||
 | 
					"__init__.py" = [
 | 
				
			||||||
 | 
					    "E402",
 | 
				
			||||||
 | 
					    "F401",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										19
									
								
								scripts.py
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								scripts.py
									
									
									
									
									
								
							@ -5,33 +5,32 @@ from pathlib import Path
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def ex_gui():
 | 
					def ex_gui():
 | 
				
			||||||
    scriptpath = Path.cwd() / "examples" / "gui" / "."
 | 
					    scriptpath = Path.cwd() / 'examples' / 'gui' / '.'
 | 
				
			||||||
    subprocess.run([sys.executable, str(scriptpath)])
 | 
					    subprocess.run([sys.executable, str(scriptpath)])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def ex_obs():
 | 
					def ex_obs():
 | 
				
			||||||
    scriptpath = Path.cwd() / "examples" / "obs" / "."
 | 
					    subprocess.run(['tox', 'r', '-e', 'obs'])
 | 
				
			||||||
    subprocess.run([sys.executable, str(scriptpath)])
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def ex_observer():
 | 
					def ex_observer():
 | 
				
			||||||
    scriptpath = Path.cwd() / "examples" / "observer" / "."
 | 
					    scriptpath = Path.cwd() / 'examples' / 'observer' / '.'
 | 
				
			||||||
    subprocess.run([sys.executable, str(scriptpath)])
 | 
					    subprocess.run([sys.executable, str(scriptpath)])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_basic():
 | 
					def test_basic():
 | 
				
			||||||
    os.environ["KIND"] = "basic"
 | 
					    os.environ['KIND'] = 'basic'
 | 
				
			||||||
    subprocess.run(["tox"])
 | 
					    subprocess.run(['tox'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_banana():
 | 
					def test_banana():
 | 
				
			||||||
    os.environ["KIND"] = "banana"
 | 
					    os.environ['KIND'] = 'banana'
 | 
				
			||||||
    subprocess.run(["tox"])
 | 
					    subprocess.run(['tox'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_potato():
 | 
					def test_potato():
 | 
				
			||||||
    os.environ["KIND"] = "potato"
 | 
					    os.environ['KIND'] = 'potato'
 | 
				
			||||||
    subprocess.run(["tox"])
 | 
					    subprocess.run(['tox'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_all():
 | 
					def test_all():
 | 
				
			||||||
 | 
				
			|||||||
@ -9,14 +9,14 @@ from vban_cmd.kinds import request_kind_map as kindmap
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# get KIND_ID from env var, otherwise set to random
 | 
					# get KIND_ID from env var, otherwise set to random
 | 
				
			||||||
KIND_ID = os.environ.get(
 | 
					KIND_ID = os.environ.get(
 | 
				
			||||||
    "KIND", random.choice(tuple(kind_id.name.lower() for kind_id in KindId))
 | 
					    'KIND', random.choice(tuple(kind_id.name.lower() for kind_id in KindId))
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
opts = {
 | 
					opts = {
 | 
				
			||||||
    "ip": "testing.local",
 | 
					    'ip': 'ws.local',
 | 
				
			||||||
    "streamname": "testing",
 | 
					    'streamname': 'workstation',
 | 
				
			||||||
    "port": 6990,
 | 
					    'port': 6980,
 | 
				
			||||||
    "bps": 0,
 | 
					    'bps': 0,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
vban = vban_cmd.api(KIND_ID, **opts)
 | 
					vban = vban_cmd.api(KIND_ID, **opts)
 | 
				
			||||||
@ -42,7 +42,7 @@ data = Data()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def setup_module():
 | 
					def setup_module():
 | 
				
			||||||
    print(f"\nRunning tests for kind [{data.name}]\n", file=sys.stdout)
 | 
					    print(f'\nRunning tests for kind [{data.name}]\n', file=sys.stdout)
 | 
				
			||||||
    vban.login()
 | 
					    vban.login()
 | 
				
			||||||
    vban.command.reset()
 | 
					    vban.command.reset()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,3 @@
 | 
				
			|||||||
from .factory import request_vbancmd_obj as api
 | 
					from .factory import request_vbancmd_obj as api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__ALL__ = ["api"]
 | 
					__ALL__ = ['api']
 | 
				
			||||||
 | 
				
			|||||||
@ -7,8 +7,8 @@ from .iremote import IRemote
 | 
				
			|||||||
from .meta import bus_mode_prop, channel_bool_prop, channel_label_prop
 | 
					from .meta import bus_mode_prop, channel_bool_prop, channel_label_prop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BusModes = IntEnum(
 | 
					BusModes = IntEnum(
 | 
				
			||||||
    "BusModes",
 | 
					    'BusModes',
 | 
				
			||||||
    "normal amix bmix repeat composite tvmix upmix21 upmix41 upmix61 centeronly lfeonly rearonly",
 | 
					    'normal amix bmix repeat composite tvmix upmix21 upmix41 upmix61 centeronly lfeonly rearonly',
 | 
				
			||||||
    start=0,
 | 
					    start=0,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -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:
 | 
				
			||||||
@ -36,19 +36,19 @@ class Bus(IRemote):
 | 
				
			|||||||
                return val * 0.01
 | 
					                return val * 0.01
 | 
				
			||||||
            return (((1 << 16) - 1) - val) * -0.01
 | 
					            return (((1 << 16) - 1) - val) * -0.01
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        val = self.getter("gain")
 | 
					        val = self.getter('gain')
 | 
				
			||||||
        return round(val if val else fget(), 1)
 | 
					        return round(val if val else fget(), 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @gain.setter
 | 
					    @gain.setter
 | 
				
			||||||
    def gain(self, val: float):
 | 
					    def gain(self, val: float):
 | 
				
			||||||
        self.setter("gain", val)
 | 
					        self.setter('gain', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def fadeto(self, target: float, time_: int):
 | 
					    def fadeto(self, target: float, time_: int):
 | 
				
			||||||
        self.setter("FadeTo", f"({target}, {time_})")
 | 
					        self.setter('FadeTo', f'({target}, {time_})')
 | 
				
			||||||
        time.sleep(self._remote.DELAY)
 | 
					        time.sleep(self._remote.DELAY)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def fadeby(self, change: float, time_: int):
 | 
					    def fadeby(self, change: float, time_: int):
 | 
				
			||||||
        self.setter("FadeBy", f"({change}, {time_})")
 | 
					        self.setter('FadeBy', f'({change}, {time_})')
 | 
				
			||||||
        time.sleep(self._remote.DELAY)
 | 
					        time.sleep(self._remote.DELAY)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -56,22 +56,22 @@ class BusEQ(IRemote):
 | 
				
			|||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def make(cls, remote, index):
 | 
					    def make(cls, remote, index):
 | 
				
			||||||
        BUSEQ_cls = type(
 | 
					        BUSEQ_cls = type(
 | 
				
			||||||
            f"BusEQ{remote.kind}",
 | 
					            f'BusEQ{remote.kind}',
 | 
				
			||||||
            (cls,),
 | 
					            (cls,),
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                **{param: channel_bool_prop(param) for param in ["on", "ab"]},
 | 
					                **{param: channel_bool_prop(param) for param in ['on', 'ab']},
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        return BUSEQ_cls(remote, index)
 | 
					        return BUSEQ_cls(remote, index)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @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):
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return f"{type(self).__name__}{self.index}"
 | 
					        return f'{type(self).__name__}{self.index}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def device(self) -> str:
 | 
					    def device(self) -> str:
 | 
				
			||||||
@ -84,7 +84,7 @@ class PhysicalBus(Bus):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class VirtualBus(Bus):
 | 
					class VirtualBus(Bus):
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return f"{type(self).__name__}{self.index}"
 | 
					        return f'{type(self).__name__}{self.index}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BusLevel(IRemote):
 | 
					class BusLevel(IRemote):
 | 
				
			||||||
@ -105,7 +105,7 @@ class BusLevel(IRemote):
 | 
				
			|||||||
        if not self._remote.stopped() and self._remote.event.ldirty:
 | 
					        if not self._remote.stopped() and self._remote.event.ldirty:
 | 
				
			||||||
            return tuple(
 | 
					            return tuple(
 | 
				
			||||||
                fget(i)
 | 
					                fget(i)
 | 
				
			||||||
                for i in self._remote.cache["bus_level"][self.range[0] : self.range[-1]]
 | 
					                for i in self._remote.cache['bus_level'][self.range[0] : self.range[-1]]
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        return tuple(
 | 
					        return tuple(
 | 
				
			||||||
            fget(i)
 | 
					            fget(i)
 | 
				
			||||||
@ -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,26 +138,26 @@ def _make_bus_mode_mixin():
 | 
				
			|||||||
    """Creates a mixin of Bus Modes."""
 | 
					    """Creates a mixin of Bus Modes."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    modestates = {
 | 
					    modestates = {
 | 
				
			||||||
        "normal": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 | 
					        'normal': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 | 
				
			||||||
        "amix": [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
 | 
					        'amix': [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
 | 
				
			||||||
        "repeat": [0, 2, 2, 0, 0, 2, 2, 0, 0, 2, 2],
 | 
					        'repeat': [0, 2, 2, 0, 0, 2, 2, 0, 0, 2, 2],
 | 
				
			||||||
        "bmix": [1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3],
 | 
					        'bmix': [1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3],
 | 
				
			||||||
        "composite": [0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0],
 | 
					        'composite': [0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0],
 | 
				
			||||||
        "tvmix": [1, 0, 1, 4, 5, 4, 5, 0, 1, 0, 1],
 | 
					        'tvmix': [1, 0, 1, 4, 5, 4, 5, 0, 1, 0, 1],
 | 
				
			||||||
        "upmix21": [0, 2, 2, 4, 4, 6, 6, 0, 0, 2, 2],
 | 
					        'upmix21': [0, 2, 2, 4, 4, 6, 6, 0, 0, 2, 2],
 | 
				
			||||||
        "upmix41": [1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3],
 | 
					        'upmix41': [1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3],
 | 
				
			||||||
        "upmix61": [0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8],
 | 
					        'upmix61': [0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8],
 | 
				
			||||||
        "centeronly": [1, 0, 1, 0, 1, 0, 1, 8, 9, 8, 9],
 | 
					        'centeronly': [1, 0, 1, 0, 1, 0, 1, 8, 9, 8, 9],
 | 
				
			||||||
        "lfeonly": [0, 2, 2, 0, 0, 2, 2, 8, 8, 10, 10],
 | 
					        'lfeonly': [0, 2, 2, 0, 0, 2, 2, 8, 8, 10, 10],
 | 
				
			||||||
        "rearonly": [1, 2, 3, 0, 1, 2, 3, 8, 9, 10, 11],
 | 
					        'rearonly': [1, 2, 3, 0, 1, 2, 3, 8, 9, 10, 11],
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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):
 | 
				
			||||||
        states = [
 | 
					        states = [
 | 
				
			||||||
            (int.from_bytes(self.public_packet.busstate[self.index], "little") & val)
 | 
					            (int.from_bytes(self.public_packet.busstate[self.index], 'little') & val)
 | 
				
			||||||
            >> 4
 | 
					            >> 4
 | 
				
			||||||
            for val in self._modes.modevals
 | 
					            for val in self._modes.modevals
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
@ -166,13 +166,13 @@ def _make_bus_mode_mixin():
 | 
				
			|||||||
                return k
 | 
					                return k
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return type(
 | 
					    return type(
 | 
				
			||||||
        "BusModeMixin",
 | 
					        'BusModeMixin',
 | 
				
			||||||
        (IRemote,),
 | 
					        (IRemote,),
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "identifier": property(identifier),
 | 
					            'identifier': property(identifier),
 | 
				
			||||||
            "modestates": modestates,
 | 
					            'modestates': modestates,
 | 
				
			||||||
            **{mode.name: bus_mode_prop(mode.name) for mode in BusModes},
 | 
					            **{mode.name: bus_mode_prop(mode.name) for mode in BusModes},
 | 
				
			||||||
            "get": get,
 | 
					            'get': get,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -186,14 +186,14 @@ def bus_factory(phys_bus, remote, i) -> Union[PhysicalBus, VirtualBus]:
 | 
				
			|||||||
    BUS_cls = PhysicalBus if phys_bus else VirtualBus
 | 
					    BUS_cls = PhysicalBus if phys_bus else VirtualBus
 | 
				
			||||||
    BUSMODEMIXIN_cls = _make_bus_mode_mixin()
 | 
					    BUSMODEMIXIN_cls = _make_bus_mode_mixin()
 | 
				
			||||||
    return type(
 | 
					    return type(
 | 
				
			||||||
        f"{BUS_cls.__name__}{remote.kind}",
 | 
					        f'{BUS_cls.__name__}{remote.kind}',
 | 
				
			||||||
        (BUS_cls,),
 | 
					        (BUS_cls,),
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "eq": BusEQ.make(remote, i),
 | 
					            'eq': BusEQ.make(remote, i),
 | 
				
			||||||
            "levels": BusLevel(remote, i),
 | 
					            'levels': BusLevel(remote, i),
 | 
				
			||||||
            "mode": BUSMODEMIXIN_cls(remote, i),
 | 
					            'mode': BUSMODEMIXIN_cls(remote, i),
 | 
				
			||||||
            **{param: channel_bool_prop(param) for param in ["mute", "mono"]},
 | 
					            **{param: channel_bool_prop(param) for param in ['mute', 'mono']},
 | 
				
			||||||
            "label": channel_label_prop(),
 | 
					            'label': channel_label_prop(),
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    )(remote, i)
 | 
					    )(remote, i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -17,30 +17,30 @@ class Command(IRemote):
 | 
				
			|||||||
        Returns a Command class of a kind.
 | 
					        Returns a Command class of a kind.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        CMD_cls = type(
 | 
					        CMD_cls = type(
 | 
				
			||||||
            f"Command{remote.kind}",
 | 
					            f'Command{remote.kind}',
 | 
				
			||||||
            (cls,),
 | 
					            (cls,),
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                **{
 | 
					                **{
 | 
				
			||||||
                    param: action_fn(param) for param in ["show", "shutdown", "restart"]
 | 
					                    param: action_fn(param) for param in ['show', 'shutdown', 'restart']
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "hide": action_fn("show", val=0),
 | 
					                'hide': action_fn('show', val=0),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        return CMD_cls(remote)
 | 
					        return CMD_cls(remote)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    showvbanchat = property(fset=set_showvbanchat)
 | 
					    showvbanchat = property(fset=set_showvbanchat)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def set_lock(self, val: bool):
 | 
					    def set_lock(self, val: bool):
 | 
				
			||||||
        self.setter("lock", 1 if val else 0)
 | 
					        self.setter('lock', 1 if val else 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    lock = property(fset=set_lock)
 | 
					    lock = property(fset=set_lock)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def reset(self):
 | 
					    def reset(self):
 | 
				
			||||||
        self._remote.apply_config("reset")
 | 
					        self._remote.apply_config('reset')
 | 
				
			||||||
 | 
				
			|||||||
@ -20,73 +20,73 @@ class TOMLStrBuilder:
 | 
				
			|||||||
    def __init__(self, kind):
 | 
					    def __init__(self, kind):
 | 
				
			||||||
        self.kind = kind
 | 
					        self.kind = kind
 | 
				
			||||||
        self.higher = itertools.chain(
 | 
					        self.higher = itertools.chain(
 | 
				
			||||||
            [f"strip-{i}" for i in range(kind.num_strip)],
 | 
					            [f'strip-{i}' for i in range(kind.num_strip)],
 | 
				
			||||||
            [f"bus-{i}" for i in range(kind.num_bus)],
 | 
					            [f'bus-{i}' for i in range(kind.num_bus)],
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def init_config(self, profile=None):
 | 
					    def init_config(self, profile=None):
 | 
				
			||||||
        self.virt_strip_params = (
 | 
					        self.virt_strip_params = (
 | 
				
			||||||
            [
 | 
					            [
 | 
				
			||||||
                "mute = false",
 | 
					                'mute = false',
 | 
				
			||||||
                "mono = false",
 | 
					                'mono = false',
 | 
				
			||||||
                "solo = false",
 | 
					                'solo = false',
 | 
				
			||||||
                "gain = 0.0",
 | 
					                'gain = 0.0',
 | 
				
			||||||
            ]
 | 
					            ]
 | 
				
			||||||
            + [f"A{i} = false" for i in range(1, self.kind.phys_out + 1)]
 | 
					            + [f'A{i} = false' for i in range(1, self.kind.phys_out + 1)]
 | 
				
			||||||
            + [f"B{i} = false" for i in range(1, self.kind.virt_out + 1)]
 | 
					            + [f'B{i} = false' for i in range(1, self.kind.virt_out + 1)]
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.phys_strip_params = self.virt_strip_params + [
 | 
					        self.phys_strip_params = self.virt_strip_params + [
 | 
				
			||||||
            "comp.knob = 0.0",
 | 
					            'comp.knob = 0.0',
 | 
				
			||||||
            "gate.knob = 0.0",
 | 
					            'gate.knob = 0.0',
 | 
				
			||||||
            "denoiser.knob = 0.0",
 | 
					            'denoiser.knob = 0.0',
 | 
				
			||||||
            "eq.on = false",
 | 
					            'eq.on = false',
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
        self.bus_float = ["gain = 0.0"]
 | 
					        self.bus_float = ['gain = 0.0']
 | 
				
			||||||
        self.bus_params = [
 | 
					        self.bus_params = [
 | 
				
			||||||
            "mono = false",
 | 
					            'mono = false',
 | 
				
			||||||
            "eq.on = false",
 | 
					            'eq.on = false',
 | 
				
			||||||
            "mute = false",
 | 
					            'mute = false',
 | 
				
			||||||
            "gain = 0.0",
 | 
					            'gain = 0.0',
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if profile == "reset":
 | 
					        if profile == 'reset':
 | 
				
			||||||
            self.reset_config()
 | 
					            self.reset_config()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def reset_config(self):
 | 
					    def reset_config(self):
 | 
				
			||||||
        self.phys_strip_params = list(
 | 
					        self.phys_strip_params = list(
 | 
				
			||||||
            map(lambda x: x.replace("B1 = false", "B1 = true"), self.phys_strip_params)
 | 
					            map(lambda x: x.replace('B1 = false', 'B1 = true'), self.phys_strip_params)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.virt_strip_params = list(
 | 
					        self.virt_strip_params = list(
 | 
				
			||||||
            map(lambda x: x.replace("A1 = false", "A1 = true"), self.virt_strip_params)
 | 
					            map(lambda x: x.replace('A1 = false', 'A1 = true'), self.virt_strip_params)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def build(self, profile="reset"):
 | 
					    def build(self, profile='reset'):
 | 
				
			||||||
        self.init_config(profile)
 | 
					        self.init_config(profile)
 | 
				
			||||||
        toml_str = str()
 | 
					        toml_str = str()
 | 
				
			||||||
        for eachclass in self.higher:
 | 
					        for eachclass in self.higher:
 | 
				
			||||||
            toml_str += f"[{eachclass}]\n"
 | 
					            toml_str += f'[{eachclass}]\n'
 | 
				
			||||||
            toml_str = self.join(eachclass, toml_str)
 | 
					            toml_str = self.join(eachclass, toml_str)
 | 
				
			||||||
        return toml_str
 | 
					        return toml_str
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def join(self, eachclass, toml_str):
 | 
					    def join(self, eachclass, toml_str):
 | 
				
			||||||
        kls, index = eachclass.split("-")
 | 
					        kls, index = eachclass.split('-')
 | 
				
			||||||
        match kls:
 | 
					        match kls:
 | 
				
			||||||
            case "strip":
 | 
					            case 'strip':
 | 
				
			||||||
                toml_str += ("\n").join(
 | 
					                toml_str += ('\n').join(
 | 
				
			||||||
                    self.phys_strip_params
 | 
					                    self.phys_strip_params
 | 
				
			||||||
                    if int(index) < self.kind.phys_in
 | 
					                    if int(index) < self.kind.phys_in
 | 
				
			||||||
                    else self.virt_strip_params
 | 
					                    else self.virt_strip_params
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            case "bus":
 | 
					            case 'bus':
 | 
				
			||||||
                toml_str += ("\n").join(self.bus_params)
 | 
					                toml_str += ('\n').join(self.bus_params)
 | 
				
			||||||
            case _:
 | 
					            case _:
 | 
				
			||||||
                pass
 | 
					                pass
 | 
				
			||||||
        return toml_str + "\n"
 | 
					        return toml_str + '\n'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TOMLDataExtractor:
 | 
					class TOMLDataExtractor:
 | 
				
			||||||
    def __init__(self, file):
 | 
					    def __init__(self, file):
 | 
				
			||||||
        with open(file, "rb") as f:
 | 
					        with open(file, 'rb') as f:
 | 
				
			||||||
            self._data = tomllib.load(f)
 | 
					            self._data = tomllib.load(f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
@ -104,10 +104,10 @@ def dataextraction_factory(file):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    this opens the possibility for other parsers to be added
 | 
					    this opens the possibility for other parsers to be added
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    if file.suffix == ".toml":
 | 
					    if file.suffix == '.toml':
 | 
				
			||||||
        extractor = TOMLDataExtractor
 | 
					        extractor = TOMLDataExtractor
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        raise ValueError("Cannot extract data from {}".format(file))
 | 
					        raise ValueError('Cannot extract data from {}'.format(file))
 | 
				
			||||||
    return extractor(file)
 | 
					    return extractor(file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -141,25 +141,25 @@ class Loader(metaclass=SingletonType):
 | 
				
			|||||||
    def defaults(self, kind):
 | 
					    def defaults(self, kind):
 | 
				
			||||||
        self.builder = TOMLStrBuilder(kind)
 | 
					        self.builder = TOMLStrBuilder(kind)
 | 
				
			||||||
        toml_str = self.builder.build()
 | 
					        toml_str = self.builder.build()
 | 
				
			||||||
        self.register("reset", tomllib.loads(toml_str))
 | 
					        self.register('reset', tomllib.loads(toml_str))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def parse(self, identifier, data):
 | 
					    def parse(self, identifier, data):
 | 
				
			||||||
        if identifier in self._configs:
 | 
					        if identifier in self._configs:
 | 
				
			||||||
            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
 | 
					            return
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self.parser = dataextraction_factory(data)
 | 
					            self.parser = dataextraction_factory(data)
 | 
				
			||||||
        except tomllib.TOMLDecodeError as e:
 | 
					        except tomllib.TOMLDecodeError as e:
 | 
				
			||||||
            ERR_MSG = (str(e), f"When attempting to load {identifier}.toml")
 | 
					            ERR_MSG = (str(e), f'When attempting to load {identifier}.toml')
 | 
				
			||||||
            self.logger.error(f"{type(e).__name__}: {' '.join(ERR_MSG)}")
 | 
					            self.logger.error(f'{type(e).__name__}: {" ".join(ERR_MSG)}')
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        return True
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def register(self, identifier, data=None):
 | 
					    def register(self, identifier, data=None):
 | 
				
			||||||
        self._configs[identifier] = data if data else self.parser.data
 | 
					        self._configs[identifier] = data if data else self.parser.data
 | 
				
			||||||
        self.logger.info(f"config {self.name}/{identifier} loaded into memory")
 | 
					        self.logger.info(f'config {self.name}/{identifier} loaded into memory')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def deregister(self):
 | 
					    def deregister(self):
 | 
				
			||||||
        self._configs.clear()
 | 
					        self._configs.clear()
 | 
				
			||||||
@ -182,18 +182,18 @@ def loader(kind):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    returns configs loaded into memory
 | 
					    returns configs loaded into memory
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    logger_loader = logger.getChild("loader")
 | 
					    logger_loader = logger.getChild('loader')
 | 
				
			||||||
    loader = Loader(kind)
 | 
					    loader = Loader(kind)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for path in (
 | 
					    for path in (
 | 
				
			||||||
        Path.cwd() / "configs" / kind.name,
 | 
					        Path.cwd() / 'configs' / kind.name,
 | 
				
			||||||
        Path.home() / ".config" / "vban-cmd" / kind.name,
 | 
					        Path.home() / '.config' / 'vban-cmd' / kind.name,
 | 
				
			||||||
        Path.home() / "Documents" / "Voicemeeter" / "configs" / kind.name,
 | 
					        Path.home() / 'Documents' / 'Voicemeeter' / 'configs' / kind.name,
 | 
				
			||||||
    ):
 | 
					    ):
 | 
				
			||||||
        if path.is_dir():
 | 
					        if path.is_dir():
 | 
				
			||||||
            logger_loader.info(f"Checking [{path}] for TOML config files:")
 | 
					            logger_loader.info(f'Checking [{path}] for TOML config files:')
 | 
				
			||||||
            for file in path.glob("*.toml"):
 | 
					            for file in path.glob('*.toml'):
 | 
				
			||||||
                identifier = file.with_suffix("").stem
 | 
					                identifier = file.with_suffix('').stem
 | 
				
			||||||
                if loader.parse(identifier, file):
 | 
					                if loader.parse(identifier, file):
 | 
				
			||||||
                    loader.register(identifier)
 | 
					                    loader.register(identifier)
 | 
				
			||||||
    return loader.configs
 | 
					    return loader.configs
 | 
				
			||||||
@ -208,5 +208,5 @@ def request_config(kind_id: str):
 | 
				
			|||||||
    try:
 | 
					    try:
 | 
				
			||||||
        configs = loader(kindmap(kind_id))
 | 
					        configs = loader(kindmap(kind_id))
 | 
				
			||||||
    except KeyError:
 | 
					    except KeyError:
 | 
				
			||||||
        raise VBANCMDError(f"Unknown Voicemeeter kind {kind_id}")
 | 
					        raise VBANCMDError(f'Unknown Voicemeeter kind {kind_id}')
 | 
				
			||||||
    return configs
 | 
					    return configs
 | 
				
			||||||
 | 
				
			|||||||
@ -12,30 +12,30 @@ class Event:
 | 
				
			|||||||
        self.logger = logger.getChild(self.__class__.__name__)
 | 
					        self.logger = logger.getChild(self.__class__.__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def info(self, msg=None):
 | 
					    def info(self, msg=None):
 | 
				
			||||||
        info = (f"{msg} events",) if msg else ()
 | 
					        info = (f'{msg} events',) if msg else ()
 | 
				
			||||||
        if self.any():
 | 
					        if self.any():
 | 
				
			||||||
            info += (f"now listening for {', '.join(self.get())} events",)
 | 
					            info += (f'now listening for {", ".join(self.get())} events',)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            info += (f"not listening for any events",)
 | 
					            info += ('not listening for any events',)
 | 
				
			||||||
        self.logger.info(", ".join(info))
 | 
					        self.logger.info(', '.join(info))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def pdirty(self) -> bool:
 | 
					    def pdirty(self) -> bool:
 | 
				
			||||||
        return self.subs["pdirty"]
 | 
					        return self.subs['pdirty']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @pdirty.setter
 | 
					    @pdirty.setter
 | 
				
			||||||
    def pdirty(self, val: bool):
 | 
					    def pdirty(self, val: bool):
 | 
				
			||||||
        self.subs["pdirty"] = val
 | 
					        self.subs['pdirty'] = val
 | 
				
			||||||
        self.info(f"pdirty {'added to' if val else 'removed from'}")
 | 
					        self.info(f'pdirty {"added to" if val else "removed from"}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def ldirty(self) -> bool:
 | 
					    def ldirty(self) -> bool:
 | 
				
			||||||
        return self.subs["ldirty"]
 | 
					        return self.subs['ldirty']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @ldirty.setter
 | 
					    @ldirty.setter
 | 
				
			||||||
    def ldirty(self, val: bool):
 | 
					    def ldirty(self, val: bool):
 | 
				
			||||||
        self.subs["ldirty"] = val
 | 
					        self.subs['ldirty'] = val
 | 
				
			||||||
        self.info(f"ldirty {'added to' if val else 'removed from'}")
 | 
					        self.info(f'ldirty {"added to" if val else "removed from"}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get(self) -> list:
 | 
					    def get(self) -> list:
 | 
				
			||||||
        return [k for k, v in self.subs.items() if v]
 | 
					        return [k for k, v in self.subs.items() if v]
 | 
				
			||||||
 | 
				
			|||||||
@ -26,24 +26,24 @@ class FactoryBuilder:
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    BuilderProgress = IntEnum(
 | 
					    BuilderProgress = IntEnum(
 | 
				
			||||||
        "BuilderProgress", "strip bus command macrobutton vban", start=0
 | 
					        'BuilderProgress', 'strip bus command macrobutton vban', start=0
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, factory, kind: KindMapClass):
 | 
					    def __init__(self, factory, kind: KindMapClass):
 | 
				
			||||||
        self._factory = factory
 | 
					        self._factory = factory
 | 
				
			||||||
        self.kind = kind
 | 
					        self.kind = kind
 | 
				
			||||||
        self._info = (
 | 
					        self._info = (
 | 
				
			||||||
            f"Finished building strips for {self._factory}",
 | 
					            f'Finished building strips for {self._factory}',
 | 
				
			||||||
            f"Finished building buses for {self._factory}",
 | 
					            f'Finished building buses for {self._factory}',
 | 
				
			||||||
            f"Finished building commands for {self._factory}",
 | 
					            f'Finished building commands for {self._factory}',
 | 
				
			||||||
            f"Finished building macrobuttons for {self._factory}",
 | 
					            f'Finished building macrobuttons for {self._factory}',
 | 
				
			||||||
            f"Finished building vban in/out streams for {self._factory}",
 | 
					            f'Finished building vban in/out streams for {self._factory}',
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.logger = logger.getChild(self.__class__.__name__)
 | 
					        self.logger = logger.getChild(self.__class__.__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _pinfo(self, name: str) -> None:
 | 
					    def _pinfo(self, name: str) -> None:
 | 
				
			||||||
        """prints progress status for each step"""
 | 
					        """prints progress status for each step"""
 | 
				
			||||||
        name = name.split("_")[1]
 | 
					        name = name.split('_')[1]
 | 
				
			||||||
        self.logger.info(self._info[int(getattr(self.BuilderProgress, name))])
 | 
					        self.logger.info(self._info[int(getattr(self.BuilderProgress, name))])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def make_strip(self):
 | 
					    def make_strip(self):
 | 
				
			||||||
@ -78,20 +78,20 @@ class FactoryBase(VbanCmd):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def __init__(self, kind_id: str, **kwargs):
 | 
					    def __init__(self, kind_id: str, **kwargs):
 | 
				
			||||||
        defaultkwargs = {
 | 
					        defaultkwargs = {
 | 
				
			||||||
            "ip": None,
 | 
					            'ip': None,
 | 
				
			||||||
            "port": 6980,
 | 
					            'port': 6980,
 | 
				
			||||||
            "streamname": "Command1",
 | 
					            'streamname': 'Command1',
 | 
				
			||||||
            "bps": 0,
 | 
					            'bps': 0,
 | 
				
			||||||
            "channel": 0,
 | 
					            'channel': 0,
 | 
				
			||||||
            "ratelimit": 0.01,
 | 
					            'ratelimit': 0.01,
 | 
				
			||||||
            "timeout": 5,
 | 
					            'timeout': 5,
 | 
				
			||||||
            "outbound": False,
 | 
					            'outbound': False,
 | 
				
			||||||
            "sync": False,
 | 
					            'sync': False,
 | 
				
			||||||
            "pdirty": False,
 | 
					            'pdirty': False,
 | 
				
			||||||
            "ldirty": False,
 | 
					            'ldirty': False,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if "subs" in kwargs:
 | 
					        if 'subs' in kwargs:
 | 
				
			||||||
            defaultkwargs |= kwargs.pop("subs")  # for backwards compatibility
 | 
					            defaultkwargs |= kwargs.pop('subs')  # for backwards compatibility
 | 
				
			||||||
        kwargs = defaultkwargs | kwargs
 | 
					        kwargs = defaultkwargs | kwargs
 | 
				
			||||||
        self.kind = kindmap(kind_id)
 | 
					        self.kind = kindmap(kind_id)
 | 
				
			||||||
        super().__init__(**kwargs)
 | 
					        super().__init__(**kwargs)
 | 
				
			||||||
@ -106,7 +106,7 @@ class FactoryBase(VbanCmd):
 | 
				
			|||||||
        self._configs = None
 | 
					        self._configs = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self) -> str:
 | 
					    def __str__(self) -> str:
 | 
				
			||||||
        return f"Voicemeeter {self.kind}"
 | 
					        return f'Voicemeeter {self.kind}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __repr__(self):
 | 
					    def __repr__(self):
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
@ -198,15 +198,15 @@ def vbancmd_factory(kind_id: str, **kwargs) -> VbanCmd:
 | 
				
			|||||||
    Returns a VbanCmd class of a kind
 | 
					    Returns a VbanCmd class of a kind
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    match kind_id:
 | 
					    match kind_id:
 | 
				
			||||||
        case "basic":
 | 
					        case 'basic':
 | 
				
			||||||
            _factory = BasicFactory
 | 
					            _factory = BasicFactory
 | 
				
			||||||
        case "banana":
 | 
					        case 'banana':
 | 
				
			||||||
            _factory = BananaFactory
 | 
					            _factory = BananaFactory
 | 
				
			||||||
        case "potato":
 | 
					        case 'potato':
 | 
				
			||||||
            _factory = PotatoFactory
 | 
					            _factory = PotatoFactory
 | 
				
			||||||
        case _:
 | 
					        case _:
 | 
				
			||||||
            raise ValueError(f"Unknown Voicemeeter kind '{kind_id}'")
 | 
					            raise ValueError(f"Unknown Voicemeeter kind '{kind_id}'")
 | 
				
			||||||
    return type(f"VbanCmd{kind_id.capitalize()}", (_factory,), {})(kind_id, **kwargs)
 | 
					    return type(f'VbanCmd{kind_id.capitalize()}', (_factory,), {})(kind_id, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def request_vbancmd_obj(kind_id: str, **kwargs) -> VbanCmd:
 | 
					def request_vbancmd_obj(kind_id: str, **kwargs) -> VbanCmd:
 | 
				
			||||||
@ -215,12 +215,12 @@ def request_vbancmd_obj(kind_id: str, **kwargs) -> VbanCmd:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    Returns a reference to a VbanCmd class of a kind
 | 
					    Returns a reference to a VbanCmd class of a kind
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    logger_entry = logger.getChild("factory.request_vbancmd_obj")
 | 
					    logger_entry = logger.getChild('factory.request_vbancmd_obj')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    VBANCMD_obj = None
 | 
					    VBANCMD_obj = None
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        VBANCMD_obj = vbancmd_factory(kind_id, **kwargs)
 | 
					        VBANCMD_obj = vbancmd_factory(kind_id, **kwargs)
 | 
				
			||||||
    except (ValueError, TypeError) as e:
 | 
					    except (ValueError, TypeError) as e:
 | 
				
			||||||
        logger_entry.exception(f"{type(e).__name__}: {e}")
 | 
					        logger_entry.exception(f'{type(e).__name__}: {e}')
 | 
				
			||||||
        raise VBANCMDError(str(e)) from e
 | 
					        raise VBANCMDError(str(e)) from e
 | 
				
			||||||
    return VBANCMD_obj
 | 
					    return VBANCMD_obj
 | 
				
			||||||
 | 
				
			|||||||
@ -93,7 +93,7 @@ class IRemote(metaclass=ABCMeta):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def getter(self, param):
 | 
					    def getter(self, param):
 | 
				
			||||||
        cmd = self._cmd(param)
 | 
					        cmd = self._cmd(param)
 | 
				
			||||||
        self.logger.debug(f"getter: {cmd}")
 | 
					        self.logger.debug(f'getter: {cmd}')
 | 
				
			||||||
        if cmd in self._remote.cache:
 | 
					        if cmd in self._remote.cache:
 | 
				
			||||||
            return self._remote.cache.pop(cmd)
 | 
					            return self._remote.cache.pop(cmd)
 | 
				
			||||||
        if self._remote.sync:
 | 
					        if self._remote.sync:
 | 
				
			||||||
@ -101,14 +101,14 @@ class IRemote(metaclass=ABCMeta):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def setter(self, param, val):
 | 
					    def setter(self, param, val):
 | 
				
			||||||
        """Sends a string request RT packet."""
 | 
					        """Sends a string request RT packet."""
 | 
				
			||||||
        self.logger.debug(f"setter: {self._cmd(param)}={val}")
 | 
					        self.logger.debug(f'setter: {self._cmd(param)}={val}')
 | 
				
			||||||
        self._remote._set_rt(self._cmd(param), val)
 | 
					        self._remote._set_rt(self._cmd(param), val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _cmd(self, param):
 | 
					    def _cmd(self, param):
 | 
				
			||||||
        cmd = (self.identifier,)
 | 
					        cmd = (self.identifier,)
 | 
				
			||||||
        if param:
 | 
					        if param:
 | 
				
			||||||
            cmd += (f".{param}",)
 | 
					            cmd += (f'.{param}',)
 | 
				
			||||||
        return "".join(cmd)
 | 
					        return ''.join(cmd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    @abstractmethod
 | 
					    @abstractmethod
 | 
				
			||||||
@ -124,10 +124,10 @@ class IRemote(metaclass=ABCMeta):
 | 
				
			|||||||
        """Sets all parameters of a dict for the channel."""
 | 
					        """Sets all parameters of a dict for the channel."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def fget(attr, val):
 | 
					        def fget(attr, val):
 | 
				
			||||||
            if attr == "mode":
 | 
					            if attr == 'mode':
 | 
				
			||||||
                return (f"mode.{val}", 1)
 | 
					                return (f'mode.{val}', 1)
 | 
				
			||||||
            elif attr == "knob":
 | 
					            elif attr == 'knob':
 | 
				
			||||||
                return ("", val)
 | 
					                return ('', val)
 | 
				
			||||||
            return (attr, val)
 | 
					            return (attr, val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for attr, val in data.items():
 | 
					        for attr, val in data.items():
 | 
				
			||||||
@ -138,7 +138,7 @@ class IRemote(metaclass=ABCMeta):
 | 
				
			|||||||
                        val = 1 if val else 0
 | 
					                        val = 1 if val else 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    self._remote.cache[self._cmd(attr)] = val
 | 
					                    self._remote.cache[self._cmd(attr)] = val
 | 
				
			||||||
                    self._remote._script += f"{self._cmd(attr)}={val};"
 | 
					                    self._remote._script += f'{self._cmd(attr)}={val};'
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                target = getattr(self, attr)
 | 
					                target = getattr(self, attr)
 | 
				
			||||||
                target.apply(val)
 | 
					                target.apply(val)
 | 
				
			||||||
 | 
				
			|||||||
@ -91,14 +91,14 @@ class PotatoMap(KindMapClass):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
def kind_factory(kind_id):
 | 
					def kind_factory(kind_id):
 | 
				
			||||||
    match kind_id:
 | 
					    match kind_id:
 | 
				
			||||||
        case "basic":
 | 
					        case 'basic':
 | 
				
			||||||
            _kind_map = BasicMap
 | 
					            _kind_map = BasicMap
 | 
				
			||||||
        case "banana":
 | 
					        case 'banana':
 | 
				
			||||||
            _kind_map = BananaMap
 | 
					            _kind_map = BananaMap
 | 
				
			||||||
        case "potato":
 | 
					        case 'potato':
 | 
				
			||||||
            _kind_map = PotatoMap
 | 
					            _kind_map = PotatoMap
 | 
				
			||||||
        case _:
 | 
					        case _:
 | 
				
			||||||
            raise ValueError(f"Unknown Voicemeeter kind {kind_id}")
 | 
					            raise ValueError(f'Unknown Voicemeeter kind {kind_id}')
 | 
				
			||||||
    return _kind_map(name=kind_id)
 | 
					    return _kind_map(name=kind_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -5,32 +5,32 @@ class MacroButton(IRemote):
 | 
				
			|||||||
    """A placeholder class in case this interface is being used interchangeably with the Remote API"""
 | 
					    """A placeholder class in case this interface is being used interchangeably with the Remote API"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return f"{type(self).__name__}{self._remote.kind}{self.index}"
 | 
					        return f'{type(self).__name__}{self._remote.kind}{self.index}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def identifier(self):
 | 
					    def identifier(self):
 | 
				
			||||||
        return f"command.button[{self.index}]"
 | 
					        return f'command.button[{self.index}]'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def state(self) -> bool:
 | 
					    def state(self) -> bool:
 | 
				
			||||||
        self.logger.warning("button.state commands are not supported over VBAN")
 | 
					        self.logger.warning('button.state commands are not supported over VBAN')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @state.setter
 | 
					    @state.setter
 | 
				
			||||||
    def state(self, _):
 | 
					    def state(self, _):
 | 
				
			||||||
        self.logger.warning("button.state commands are not supported over VBAN")
 | 
					        self.logger.warning('button.state commands are not supported over VBAN')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def stateonly(self) -> bool:
 | 
					    def stateonly(self) -> bool:
 | 
				
			||||||
        self.logger.warning("button.stateonly commands are not supported over VBAN")
 | 
					        self.logger.warning('button.stateonly commands are not supported over VBAN')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @stateonly.setter
 | 
					    @stateonly.setter
 | 
				
			||||||
    def stateonly(self, v):
 | 
					    def stateonly(self, v):
 | 
				
			||||||
        self.logger.warning("button.stateonly commands are not supported over VBAN")
 | 
					        self.logger.warning('button.stateonly commands are not supported over VBAN')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def trigger(self) -> bool:
 | 
					    def trigger(self) -> bool:
 | 
				
			||||||
        self.logger.warning("button.trigger commands are not supported over VBAN")
 | 
					        self.logger.warning('button.trigger commands are not supported over VBAN')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @trigger.setter
 | 
					    @trigger.setter
 | 
				
			||||||
    def trigger(self, _):
 | 
					    def trigger(self, _):
 | 
				
			||||||
        self.logger.warning("button.trigger commands are not supported over VBAN")
 | 
					        self.logger.warning('button.trigger commands are not supported over VBAN')
 | 
				
			||||||
 | 
				
			|||||||
@ -9,16 +9,16 @@ def channel_bool_prop(param):
 | 
				
			|||||||
    @partial(cache_bool, param=param)
 | 
					    @partial(cache_bool, param=param)
 | 
				
			||||||
    def fget(self):
 | 
					    def fget(self):
 | 
				
			||||||
        cmd = self._cmd(param)
 | 
					        cmd = self._cmd(param)
 | 
				
			||||||
        self.logger.debug(f"getter: {cmd}")
 | 
					        self.logger.debug(f'getter: {cmd}')
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            not int.from_bytes(
 | 
					            not int.from_bytes(
 | 
				
			||||||
                getattr(
 | 
					                getattr(
 | 
				
			||||||
                    self.public_packet,
 | 
					                    self.public_packet,
 | 
				
			||||||
                    f"{'strip' if 'strip' in type(self).__name__.lower() else 'bus'}state",
 | 
					                    f'{"strip" if "strip" in type(self).__name__.lower() else "bus"}state',
 | 
				
			||||||
                )[self.index],
 | 
					                )[self.index],
 | 
				
			||||||
                "little",
 | 
					                'little',
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            & getattr(self._modes, f"_{param.lower()}")
 | 
					            & getattr(self._modes, f'_{param.lower()}')
 | 
				
			||||||
            == 0
 | 
					            == 0
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -31,15 +31,15 @@ def channel_bool_prop(param):
 | 
				
			|||||||
def channel_label_prop():
 | 
					def channel_label_prop():
 | 
				
			||||||
    """meta function for channel label parameters"""
 | 
					    """meta function for channel label parameters"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @partial(cache_string, param="label")
 | 
					    @partial(cache_string, param='label')
 | 
				
			||||||
    def fget(self) -> str:
 | 
					    def fget(self) -> str:
 | 
				
			||||||
        return getattr(
 | 
					        return getattr(
 | 
				
			||||||
            self.public_packet,
 | 
					            self.public_packet,
 | 
				
			||||||
            f"{'strip' if 'strip' in type(self).__name__.lower() else 'bus'}labels",
 | 
					            f'{"strip" if "strip" in type(self).__name__.lower() else "bus"}labels',
 | 
				
			||||||
        )[self.index]
 | 
					        )[self.index]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def fset(self, val: str):
 | 
					    def fset(self, val: str):
 | 
				
			||||||
        self.setter("label", str(val))
 | 
					        self.setter('label', str(val))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return property(fget, fset)
 | 
					    return property(fget, fset)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -50,10 +50,10 @@ def strip_output_prop(param):
 | 
				
			|||||||
    @partial(cache_bool, param=param)
 | 
					    @partial(cache_bool, param=param)
 | 
				
			||||||
    def fget(self):
 | 
					    def fget(self):
 | 
				
			||||||
        cmd = self._cmd(param)
 | 
					        cmd = self._cmd(param)
 | 
				
			||||||
        self.logger.debug(f"getter: {cmd}")
 | 
					        self.logger.debug(f'getter: {cmd}')
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            not int.from_bytes(self.public_packet.stripstate[self.index], "little")
 | 
					            not int.from_bytes(self.public_packet.stripstate[self.index], 'little')
 | 
				
			||||||
            & getattr(self._modes, f"_bus{param.lower()}")
 | 
					            & getattr(self._modes, f'_bus{param.lower()}')
 | 
				
			||||||
            == 0
 | 
					            == 0
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -69,9 +69,9 @@ def bus_mode_prop(param):
 | 
				
			|||||||
    @partial(cache_bool, param=param)
 | 
					    @partial(cache_bool, param=param)
 | 
				
			||||||
    def fget(self):
 | 
					    def fget(self):
 | 
				
			||||||
        cmd = self._cmd(param)
 | 
					        cmd = self._cmd(param)
 | 
				
			||||||
        self.logger.debug(f"getter: {cmd}")
 | 
					        self.logger.debug(f'getter: {cmd}')
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
            (int.from_bytes(self.public_packet.busstate[self.index], "little") & val)
 | 
					            (int.from_bytes(self.public_packet.busstate[self.index], 'little') & val)
 | 
				
			||||||
            >> 4
 | 
					            >> 4
 | 
				
			||||||
            for val in self._modes.modevals
 | 
					            for val in self._modes.modevals
 | 
				
			||||||
        ] == self.modestates[param]
 | 
					        ] == self.modestates[param]
 | 
				
			||||||
 | 
				
			|||||||
@ -43,7 +43,7 @@ class VbanRtPacket:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def _generate_levels(self, levelarray) -> tuple:
 | 
					    def _generate_levels(self, levelarray) -> tuple:
 | 
				
			||||||
        return tuple(
 | 
					        return tuple(
 | 
				
			||||||
            int.from_bytes(levelarray[i : i + 2], "little")
 | 
					            int.from_bytes(levelarray[i : i + 2], 'little')
 | 
				
			||||||
            for i in range(0, len(levelarray), 2)
 | 
					            for i in range(0, len(levelarray), 2)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -79,13 +79,13 @@ class VbanRtPacket:
 | 
				
			|||||||
            tuple(not val for val in comp(strip_cache, self.strip_levels)),
 | 
					            tuple(not val for val in comp(strip_cache, self.strip_levels)),
 | 
				
			||||||
            tuple(not val for val in comp(bus_cache, self.bus_levels)),
 | 
					            tuple(not val for val in comp(bus_cache, self.bus_levels)),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        return any(any(l) for l in (self._strip_comp, self._bus_comp))
 | 
					        return any(any(li) for li in (self._strip_comp, self._bus_comp))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def voicemeetertype(self) -> str:
 | 
					    def voicemeetertype(self) -> str:
 | 
				
			||||||
        """returns voicemeeter type as a string"""
 | 
					        """returns voicemeeter type as a string"""
 | 
				
			||||||
        type_ = ("basic", "banana", "potato")
 | 
					        type_ = ('basic', 'banana', 'potato')
 | 
				
			||||||
        return type_[int.from_bytes(self._voicemeeterType, "little") - 1]
 | 
					        return type_[int.from_bytes(self._voicemeeterType, 'little') - 1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def voicemeeterversion(self) -> tuple:
 | 
					    def voicemeeterversion(self) -> tuple:
 | 
				
			||||||
@ -93,7 +93,7 @@ class VbanRtPacket:
 | 
				
			|||||||
        return tuple(
 | 
					        return tuple(
 | 
				
			||||||
            reversed(
 | 
					            reversed(
 | 
				
			||||||
                tuple(
 | 
					                tuple(
 | 
				
			||||||
                    int.from_bytes(self._voicemeeterVersion[i : i + 1], "little")
 | 
					                    int.from_bytes(self._voicemeeterVersion[i : i + 1], 'little')
 | 
				
			||||||
                    for i in range(4)
 | 
					                    for i in range(4)
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
@ -102,7 +102,7 @@ class VbanRtPacket:
 | 
				
			|||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def samplerate(self) -> int:
 | 
					    def samplerate(self) -> int:
 | 
				
			||||||
        """returns samplerate as an int"""
 | 
					        """returns samplerate as an int"""
 | 
				
			||||||
        return int.from_bytes(self._samplerate, "little")
 | 
					        return int.from_bytes(self._samplerate, 'little')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def inputlevels(self) -> tuple:
 | 
					    def inputlevels(self) -> tuple:
 | 
				
			||||||
@ -132,56 +132,56 @@ class VbanRtPacket:
 | 
				
			|||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def stripgainlayer1(self) -> tuple:
 | 
					    def stripgainlayer1(self) -> tuple:
 | 
				
			||||||
        return tuple(
 | 
					        return tuple(
 | 
				
			||||||
            int.from_bytes(self._stripGaindB100Layer1[i : i + 2], "little")
 | 
					            int.from_bytes(self._stripGaindB100Layer1[i : i + 2], 'little')
 | 
				
			||||||
            for i in range(0, 16, 2)
 | 
					            for i in range(0, 16, 2)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def stripgainlayer2(self) -> tuple:
 | 
					    def stripgainlayer2(self) -> tuple:
 | 
				
			||||||
        return tuple(
 | 
					        return tuple(
 | 
				
			||||||
            int.from_bytes(self._stripGaindB100Layer2[i : i + 2], "little")
 | 
					            int.from_bytes(self._stripGaindB100Layer2[i : i + 2], 'little')
 | 
				
			||||||
            for i in range(0, 16, 2)
 | 
					            for i in range(0, 16, 2)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def stripgainlayer3(self) -> tuple:
 | 
					    def stripgainlayer3(self) -> tuple:
 | 
				
			||||||
        return tuple(
 | 
					        return tuple(
 | 
				
			||||||
            int.from_bytes(self._stripGaindB100Layer3[i : i + 2], "little")
 | 
					            int.from_bytes(self._stripGaindB100Layer3[i : i + 2], 'little')
 | 
				
			||||||
            for i in range(0, 16, 2)
 | 
					            for i in range(0, 16, 2)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def stripgainlayer4(self) -> tuple:
 | 
					    def stripgainlayer4(self) -> tuple:
 | 
				
			||||||
        return tuple(
 | 
					        return tuple(
 | 
				
			||||||
            int.from_bytes(self._stripGaindB100Layer4[i : i + 2], "little")
 | 
					            int.from_bytes(self._stripGaindB100Layer4[i : i + 2], 'little')
 | 
				
			||||||
            for i in range(0, 16, 2)
 | 
					            for i in range(0, 16, 2)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def stripgainlayer5(self) -> tuple:
 | 
					    def stripgainlayer5(self) -> tuple:
 | 
				
			||||||
        return tuple(
 | 
					        return tuple(
 | 
				
			||||||
            int.from_bytes(self._stripGaindB100Layer5[i : i + 2], "little")
 | 
					            int.from_bytes(self._stripGaindB100Layer5[i : i + 2], 'little')
 | 
				
			||||||
            for i in range(0, 16, 2)
 | 
					            for i in range(0, 16, 2)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def stripgainlayer6(self) -> tuple:
 | 
					    def stripgainlayer6(self) -> tuple:
 | 
				
			||||||
        return tuple(
 | 
					        return tuple(
 | 
				
			||||||
            int.from_bytes(self._stripGaindB100Layer6[i : i + 2], "little")
 | 
					            int.from_bytes(self._stripGaindB100Layer6[i : i + 2], 'little')
 | 
				
			||||||
            for i in range(0, 16, 2)
 | 
					            for i in range(0, 16, 2)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def stripgainlayer7(self) -> tuple:
 | 
					    def stripgainlayer7(self) -> tuple:
 | 
				
			||||||
        return tuple(
 | 
					        return tuple(
 | 
				
			||||||
            int.from_bytes(self._stripGaindB100Layer7[i : i + 2], "little")
 | 
					            int.from_bytes(self._stripGaindB100Layer7[i : i + 2], 'little')
 | 
				
			||||||
            for i in range(0, 16, 2)
 | 
					            for i in range(0, 16, 2)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def stripgainlayer8(self) -> tuple:
 | 
					    def stripgainlayer8(self) -> tuple:
 | 
				
			||||||
        return tuple(
 | 
					        return tuple(
 | 
				
			||||||
            int.from_bytes(self._stripGaindB100Layer8[i : i + 2], "little")
 | 
					            int.from_bytes(self._stripGaindB100Layer8[i : i + 2], 'little')
 | 
				
			||||||
            for i in range(0, 16, 2)
 | 
					            for i in range(0, 16, 2)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -189,7 +189,7 @@ class VbanRtPacket:
 | 
				
			|||||||
    def busgain(self) -> tuple:
 | 
					    def busgain(self) -> tuple:
 | 
				
			||||||
        """returns tuple of bus gains"""
 | 
					        """returns tuple of bus gains"""
 | 
				
			||||||
        return tuple(
 | 
					        return tuple(
 | 
				
			||||||
            int.from_bytes(self._busGaindB100[i : i + 2], "little")
 | 
					            int.from_bytes(self._busGaindB100[i : i + 2], 'little')
 | 
				
			||||||
            for i in range(0, 16, 2)
 | 
					            for i in range(0, 16, 2)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -197,7 +197,7 @@ class VbanRtPacket:
 | 
				
			|||||||
    def striplabels(self) -> tuple:
 | 
					    def striplabels(self) -> tuple:
 | 
				
			||||||
        """returns tuple of strip labels"""
 | 
					        """returns tuple of strip labels"""
 | 
				
			||||||
        return tuple(
 | 
					        return tuple(
 | 
				
			||||||
            self._stripLabelUTF8c60[i : i + 60].decode().split("\x00")[0]
 | 
					            self._stripLabelUTF8c60[i : i + 60].decode().split('\x00')[0]
 | 
				
			||||||
            for i in range(0, 480, 60)
 | 
					            for i in range(0, 480, 60)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -205,7 +205,7 @@ class VbanRtPacket:
 | 
				
			|||||||
    def buslabels(self) -> tuple:
 | 
					    def buslabels(self) -> tuple:
 | 
				
			||||||
        """returns tuple of bus labels"""
 | 
					        """returns tuple of bus labels"""
 | 
				
			||||||
        return tuple(
 | 
					        return tuple(
 | 
				
			||||||
            self._busLabelUTF8c60[i : i + 60].decode().split("\x00")[0]
 | 
					            self._busLabelUTF8c60[i : i + 60].decode().split('\x00')[0]
 | 
				
			||||||
            for i in range(0, 480, 60)
 | 
					            for i in range(0, 480, 60)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -214,15 +214,15 @@ class VbanRtPacket:
 | 
				
			|||||||
class SubscribeHeader:
 | 
					class SubscribeHeader:
 | 
				
			||||||
    """Represents the header an RT Packet Service subscription packet"""
 | 
					    """Represents the header an RT Packet Service subscription packet"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    name = "Register RTP"
 | 
					    name = 'Register RTP'
 | 
				
			||||||
    timeout = 15
 | 
					    timeout = 15
 | 
				
			||||||
    vban: bytes = "VBAN".encode()
 | 
					    vban: bytes = 'VBAN'.encode()
 | 
				
			||||||
    format_sr: bytes = (VBAN_PROTOCOL_SERVICE).to_bytes(1, "little")
 | 
					    format_sr: bytes = (VBAN_PROTOCOL_SERVICE).to_bytes(1, 'little')
 | 
				
			||||||
    format_nbs: bytes = (0).to_bytes(1, "little")
 | 
					    format_nbs: bytes = (0).to_bytes(1, 'little')
 | 
				
			||||||
    format_nbc: bytes = (VBAN_SERVICE_RTPACKETREGISTER).to_bytes(1, "little")
 | 
					    format_nbc: bytes = (VBAN_SERVICE_RTPACKETREGISTER).to_bytes(1, 'little')
 | 
				
			||||||
    format_bit: bytes = (timeout & 0x000000FF).to_bytes(1, "little")  # timeout
 | 
					    format_bit: bytes = (timeout & 0x000000FF).to_bytes(1, 'little')  # timeout
 | 
				
			||||||
    streamname: bytes = name.encode("ascii") + bytes(16 - len(name))
 | 
					    streamname: bytes = name.encode('ascii') + bytes(16 - len(name))
 | 
				
			||||||
    framecounter: bytes = (0).to_bytes(4, "little")
 | 
					    framecounter: bytes = (0).to_bytes(4, 'little')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def header(self):
 | 
					    def header(self):
 | 
				
			||||||
@ -233,9 +233,9 @@ class SubscribeHeader:
 | 
				
			|||||||
        header += self.format_bit
 | 
					        header += self.format_bit
 | 
				
			||||||
        header += self.streamname
 | 
					        header += self.streamname
 | 
				
			||||||
        header += self.framecounter
 | 
					        header += self.framecounter
 | 
				
			||||||
        assert (
 | 
					        assert len(header) == HEADER_SIZE + 4, (
 | 
				
			||||||
            len(header) == HEADER_SIZE + 4
 | 
					            f'expected header size {HEADER_SIZE} bytes + 4 bytes framecounter ({HEADER_SIZE + 4} bytes total)'
 | 
				
			||||||
        ), f"expected header size {HEADER_SIZE} bytes + 4 bytes framecounter ({HEADER_SIZE +4} bytes total)"
 | 
					        )
 | 
				
			||||||
        return header
 | 
					        return header
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -243,13 +243,13 @@ class SubscribeHeader:
 | 
				
			|||||||
class VbanRtPacketHeader:
 | 
					class VbanRtPacketHeader:
 | 
				
			||||||
    """Represents the header of a VBAN RT response packet"""
 | 
					    """Represents the header of a VBAN RT response packet"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    name = "Voicemeeter-RTP"
 | 
					    name = 'Voicemeeter-RTP'
 | 
				
			||||||
    vban: bytes = "VBAN".encode()
 | 
					    vban: bytes = 'VBAN'.encode()
 | 
				
			||||||
    format_sr: bytes = (VBAN_PROTOCOL_SERVICE).to_bytes(1, "little")
 | 
					    format_sr: bytes = (VBAN_PROTOCOL_SERVICE).to_bytes(1, 'little')
 | 
				
			||||||
    format_nbs: bytes = (0).to_bytes(1, "little")
 | 
					    format_nbs: bytes = (0).to_bytes(1, 'little')
 | 
				
			||||||
    format_nbc: bytes = (VBAN_SERVICE_RTPACKET).to_bytes(1, "little")
 | 
					    format_nbc: bytes = (VBAN_SERVICE_RTPACKET).to_bytes(1, 'little')
 | 
				
			||||||
    format_bit: bytes = (0).to_bytes(1, "little")
 | 
					    format_bit: bytes = (0).to_bytes(1, 'little')
 | 
				
			||||||
    streamname: bytes = name.encode("ascii") + bytes(16 - len(name))
 | 
					    streamname: bytes = name.encode('ascii') + bytes(16 - len(name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def header(self):
 | 
					    def header(self):
 | 
				
			||||||
@ -259,7 +259,7 @@ class VbanRtPacketHeader:
 | 
				
			|||||||
        header += self.format_nbc
 | 
					        header += self.format_nbc
 | 
				
			||||||
        header += self.format_bit
 | 
					        header += self.format_bit
 | 
				
			||||||
        header += self.streamname
 | 
					        header += self.streamname
 | 
				
			||||||
        assert len(header) == HEADER_SIZE, f"expected header size {HEADER_SIZE} bytes"
 | 
					        assert len(header) == HEADER_SIZE, f'expected header size {HEADER_SIZE} bytes'
 | 
				
			||||||
        return header
 | 
					        return header
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -270,18 +270,18 @@ class RequestHeader:
 | 
				
			|||||||
    name: str
 | 
					    name: str
 | 
				
			||||||
    bps_index: int
 | 
					    bps_index: int
 | 
				
			||||||
    channel: int
 | 
					    channel: int
 | 
				
			||||||
    vban: bytes = "VBAN".encode()
 | 
					    vban: bytes = 'VBAN'.encode()
 | 
				
			||||||
    nbs: bytes = (0).to_bytes(1, "little")
 | 
					    nbs: bytes = (0).to_bytes(1, 'little')
 | 
				
			||||||
    bit: bytes = (0x10).to_bytes(1, "little")
 | 
					    bit: bytes = (0x10).to_bytes(1, 'little')
 | 
				
			||||||
    framecounter: bytes = (0).to_bytes(4, "little")
 | 
					    framecounter: bytes = (0).to_bytes(4, 'little')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def sr(self):
 | 
					    def sr(self):
 | 
				
			||||||
        return (VBAN_PROTOCOL_TXT + self.bps_index).to_bytes(1, "little")
 | 
					        return (VBAN_PROTOCOL_TXT + self.bps_index).to_bytes(1, 'little')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def nbc(self):
 | 
					    def nbc(self):
 | 
				
			||||||
        return (self.channel).to_bytes(1, "little")
 | 
					        return (self.channel).to_bytes(1, 'little')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def streamname(self):
 | 
					    def streamname(self):
 | 
				
			||||||
@ -296,7 +296,7 @@ class RequestHeader:
 | 
				
			|||||||
        header += self.bit
 | 
					        header += self.bit
 | 
				
			||||||
        header += self.streamname
 | 
					        header += self.streamname
 | 
				
			||||||
        header += self.framecounter
 | 
					        header += self.framecounter
 | 
				
			||||||
        assert (
 | 
					        assert len(header) == HEADER_SIZE + 4, (
 | 
				
			||||||
            len(header) == HEADER_SIZE + 4
 | 
					            f'expected header size {HEADER_SIZE} bytes + 4 bytes framecounter ({HEADER_SIZE + 4} bytes total)'
 | 
				
			||||||
        ), f"expected header size {HEADER_SIZE} bytes + 4 bytes framecounter ({HEADER_SIZE +4} bytes total)"
 | 
					        )
 | 
				
			||||||
        return header
 | 
					        return header
 | 
				
			||||||
 | 
				
			|||||||
@ -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:
 | 
				
			||||||
@ -28,25 +28,25 @@ class Strip(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @limit.setter
 | 
					    @limit.setter
 | 
				
			||||||
    def limit(self, val: int):
 | 
					    def limit(self, val: int):
 | 
				
			||||||
        self.setter("limit", val)
 | 
					        self.setter('limit', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def gain(self) -> float:
 | 
					    def gain(self) -> float:
 | 
				
			||||||
        val = self.getter("gain")
 | 
					        val = self.getter('gain')
 | 
				
			||||||
        if val is None:
 | 
					        if val is None:
 | 
				
			||||||
            val = self.gainlayer[0].gain
 | 
					            val = self.gainlayer[0].gain
 | 
				
			||||||
        return round(val, 1)
 | 
					        return round(val, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @gain.setter
 | 
					    @gain.setter
 | 
				
			||||||
    def gain(self, val: float):
 | 
					    def gain(self, val: float):
 | 
				
			||||||
        self.setter("gain", val)
 | 
					        self.setter('gain', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def fadeto(self, target: float, time_: int):
 | 
					    def fadeto(self, target: float, time_: int):
 | 
				
			||||||
        self.setter("FadeTo", f"({target}, {time_})")
 | 
					        self.setter('FadeTo', f'({target}, {time_})')
 | 
				
			||||||
        time.sleep(self._remote.DELAY)
 | 
					        time.sleep(self._remote.DELAY)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def fadeby(self, change: float, time_: int):
 | 
					    def fadeby(self, change: float, time_: int):
 | 
				
			||||||
        self.setter("FadeBy", f"({change}, {time_})")
 | 
					        self.setter('FadeBy', f'({change}, {time_})')
 | 
				
			||||||
        time.sleep(self._remote.DELAY)
 | 
					        time.sleep(self._remote.DELAY)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -54,18 +54,18 @@ class PhysicalStrip(Strip):
 | 
				
			|||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def make(cls, remote, index):
 | 
					    def make(cls, remote, index):
 | 
				
			||||||
        return type(
 | 
					        return type(
 | 
				
			||||||
            f"PhysicalStrip{remote.kind}",
 | 
					            f'PhysicalStrip{remote.kind}',
 | 
				
			||||||
            (cls,),
 | 
					            (cls,),
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                "comp": StripComp(remote, index),
 | 
					                'comp': StripComp(remote, index),
 | 
				
			||||||
                "gate": StripGate(remote, index),
 | 
					                'gate': StripGate(remote, index),
 | 
				
			||||||
                "denoiser": StripDenoiser(remote, index),
 | 
					                'denoiser': StripDenoiser(remote, index),
 | 
				
			||||||
                "eq": StripEQ(remote, index),
 | 
					                'eq': StripEQ(remote, index),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return f"{type(self).__name__}{self.index}"
 | 
					        return f'{type(self).__name__}{self.index}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def device(self):
 | 
					    def device(self):
 | 
				
			||||||
@ -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:
 | 
				
			||||||
@ -87,7 +87,7 @@ class StripComp(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @knob.setter
 | 
					    @knob.setter
 | 
				
			||||||
    def knob(self, val: float):
 | 
					    def knob(self, val: float):
 | 
				
			||||||
        self.setter("", val)
 | 
					        self.setter('', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def gainin(self) -> float:
 | 
					    def gainin(self) -> float:
 | 
				
			||||||
@ -95,7 +95,7 @@ class StripComp(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @gainin.setter
 | 
					    @gainin.setter
 | 
				
			||||||
    def gainin(self, val: float):
 | 
					    def gainin(self, val: float):
 | 
				
			||||||
        self.setter("GainIn", val)
 | 
					        self.setter('GainIn', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def ratio(self) -> float:
 | 
					    def ratio(self) -> float:
 | 
				
			||||||
@ -103,7 +103,7 @@ class StripComp(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @ratio.setter
 | 
					    @ratio.setter
 | 
				
			||||||
    def ratio(self, val: float):
 | 
					    def ratio(self, val: float):
 | 
				
			||||||
        self.setter("Ratio", val)
 | 
					        self.setter('Ratio', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def threshold(self) -> float:
 | 
					    def threshold(self) -> float:
 | 
				
			||||||
@ -111,7 +111,7 @@ class StripComp(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @threshold.setter
 | 
					    @threshold.setter
 | 
				
			||||||
    def threshold(self, val: float):
 | 
					    def threshold(self, val: float):
 | 
				
			||||||
        self.setter("Threshold", val)
 | 
					        self.setter('Threshold', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def attack(self) -> float:
 | 
					    def attack(self) -> float:
 | 
				
			||||||
@ -119,7 +119,7 @@ class StripComp(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @attack.setter
 | 
					    @attack.setter
 | 
				
			||||||
    def attack(self, val: float):
 | 
					    def attack(self, val: float):
 | 
				
			||||||
        self.setter("Attack", val)
 | 
					        self.setter('Attack', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def release(self) -> float:
 | 
					    def release(self) -> float:
 | 
				
			||||||
@ -127,7 +127,7 @@ class StripComp(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @release.setter
 | 
					    @release.setter
 | 
				
			||||||
    def release(self, val: float):
 | 
					    def release(self, val: float):
 | 
				
			||||||
        self.setter("Release", val)
 | 
					        self.setter('Release', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def knee(self) -> float:
 | 
					    def knee(self) -> float:
 | 
				
			||||||
@ -135,7 +135,7 @@ class StripComp(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @knee.setter
 | 
					    @knee.setter
 | 
				
			||||||
    def knee(self, val: float):
 | 
					    def knee(self, val: float):
 | 
				
			||||||
        self.setter("Knee", val)
 | 
					        self.setter('Knee', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def gainout(self) -> float:
 | 
					    def gainout(self) -> float:
 | 
				
			||||||
@ -143,7 +143,7 @@ class StripComp(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @gainout.setter
 | 
					    @gainout.setter
 | 
				
			||||||
    def gainout(self, val: float):
 | 
					    def gainout(self, val: float):
 | 
				
			||||||
        self.setter("GainOut", val)
 | 
					        self.setter('GainOut', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def makeup(self) -> bool:
 | 
					    def makeup(self) -> bool:
 | 
				
			||||||
@ -151,13 +151,13 @@ class StripComp(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @makeup.setter
 | 
					    @makeup.setter
 | 
				
			||||||
    def makeup(self, val: bool):
 | 
					    def makeup(self, val: bool):
 | 
				
			||||||
        self.setter("makeup", 1 if val else 0)
 | 
					        self.setter('makeup', 1 if val else 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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:
 | 
				
			||||||
@ -165,7 +165,7 @@ class StripGate(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @knob.setter
 | 
					    @knob.setter
 | 
				
			||||||
    def knob(self, val: float):
 | 
					    def knob(self, val: float):
 | 
				
			||||||
        self.setter("", val)
 | 
					        self.setter('', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def threshold(self) -> float:
 | 
					    def threshold(self) -> float:
 | 
				
			||||||
@ -173,7 +173,7 @@ class StripGate(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @threshold.setter
 | 
					    @threshold.setter
 | 
				
			||||||
    def threshold(self, val: float):
 | 
					    def threshold(self, val: float):
 | 
				
			||||||
        self.setter("Threshold", val)
 | 
					        self.setter('Threshold', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def damping(self) -> float:
 | 
					    def damping(self) -> float:
 | 
				
			||||||
@ -181,7 +181,7 @@ class StripGate(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @damping.setter
 | 
					    @damping.setter
 | 
				
			||||||
    def damping(self, val: float):
 | 
					    def damping(self, val: float):
 | 
				
			||||||
        self.setter("Damping", val)
 | 
					        self.setter('Damping', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def bpsidechain(self) -> int:
 | 
					    def bpsidechain(self) -> int:
 | 
				
			||||||
@ -189,7 +189,7 @@ class StripGate(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @bpsidechain.setter
 | 
					    @bpsidechain.setter
 | 
				
			||||||
    def bpsidechain(self, val: int):
 | 
					    def bpsidechain(self, val: int):
 | 
				
			||||||
        self.setter("BPSidechain", val)
 | 
					        self.setter('BPSidechain', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def attack(self) -> float:
 | 
					    def attack(self) -> float:
 | 
				
			||||||
@ -197,7 +197,7 @@ class StripGate(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @attack.setter
 | 
					    @attack.setter
 | 
				
			||||||
    def attack(self, val: float):
 | 
					    def attack(self, val: float):
 | 
				
			||||||
        self.setter("Attack", val)
 | 
					        self.setter('Attack', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def hold(self) -> float:
 | 
					    def hold(self) -> float:
 | 
				
			||||||
@ -205,7 +205,7 @@ class StripGate(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @hold.setter
 | 
					    @hold.setter
 | 
				
			||||||
    def hold(self, val: float):
 | 
					    def hold(self, val: float):
 | 
				
			||||||
        self.setter("Hold", val)
 | 
					        self.setter('Hold', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def release(self) -> float:
 | 
					    def release(self) -> float:
 | 
				
			||||||
@ -213,13 +213,13 @@ class StripGate(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @release.setter
 | 
					    @release.setter
 | 
				
			||||||
    def release(self, val: float):
 | 
					    def release(self, val: float):
 | 
				
			||||||
        self.setter("Release", val)
 | 
					        self.setter('Release', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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:
 | 
				
			||||||
@ -227,13 +227,13 @@ class StripDenoiser(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @knob.setter
 | 
					    @knob.setter
 | 
				
			||||||
    def knob(self, val: float):
 | 
					    def knob(self, val: float):
 | 
				
			||||||
        self.setter("", val)
 | 
					        self.setter('', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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):
 | 
				
			||||||
@ -241,7 +241,7 @@ class StripEQ(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @on.setter
 | 
					    @on.setter
 | 
				
			||||||
    def on(self, val: bool):
 | 
					    def on(self, val: bool):
 | 
				
			||||||
        self.setter("on", 1 if val else 0)
 | 
					        self.setter('on', 1 if val else 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def ab(self):
 | 
					    def ab(self):
 | 
				
			||||||
@ -249,14 +249,14 @@ class StripEQ(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @ab.setter
 | 
					    @ab.setter
 | 
				
			||||||
    def ab(self, val: bool):
 | 
					    def ab(self, val: bool):
 | 
				
			||||||
        self.setter("ab", 1 if val else 0)
 | 
					        self.setter('ab', 1 if val else 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class VirtualStrip(Strip):
 | 
					class VirtualStrip(Strip):
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return f"{type(self).__name__}{self.index}"
 | 
					        return f'{type(self).__name__}{self.index}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mc = channel_bool_prop("mc")
 | 
					    mc = channel_bool_prop('mc')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mono = mc
 | 
					    mono = mc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -266,13 +266,13 @@ class VirtualStrip(Strip):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @k.setter
 | 
					    @k.setter
 | 
				
			||||||
    def k(self, val: int):
 | 
					    def k(self, val: int):
 | 
				
			||||||
        self.setter("karaoke", val)
 | 
					        self.setter('karaoke', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def appgain(self, name: str, gain: float):
 | 
					    def appgain(self, name: str, gain: float):
 | 
				
			||||||
        self.setter("AppGain", f'("{name}", {gain})')
 | 
					        self.setter('AppGain', f'("{name}", {gain})')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def appmute(self, name: str, mute: bool = None):
 | 
					    def appmute(self, name: str, mute: bool = None):
 | 
				
			||||||
        self.setter("AppMute", f'("{name}", {1 if mute else 0})')
 | 
					        self.setter('AppMute', f'("{name}", {1 if mute else 0})')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StripLevel(IRemote):
 | 
					class StripLevel(IRemote):
 | 
				
			||||||
@ -299,7 +299,7 @@ class StripLevel(IRemote):
 | 
				
			|||||||
        if not self._remote.stopped() and self._remote.event.ldirty:
 | 
					        if not self._remote.stopped() and self._remote.event.ldirty:
 | 
				
			||||||
            return tuple(
 | 
					            return tuple(
 | 
				
			||||||
                fget(i)
 | 
					                fget(i)
 | 
				
			||||||
                for i in self._remote.cache["strip_level"][
 | 
					                for i in self._remote.cache['strip_level'][
 | 
				
			||||||
                    self.range[0] : self.range[-1]
 | 
					                    self.range[0] : self.range[-1]
 | 
				
			||||||
                ]
 | 
					                ]
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
@ -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,31 +345,33 @@ 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:
 | 
				
			||||||
        def fget():
 | 
					        def fget():
 | 
				
			||||||
            val = getattr(self.public_packet, f"stripgainlayer{self._i+1}")[self.index]
 | 
					            val = getattr(self.public_packet, f'stripgainlayer{self._i + 1}')[
 | 
				
			||||||
 | 
					                self.index
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
            if 0 <= val <= 1200:
 | 
					            if 0 <= val <= 1200:
 | 
				
			||||||
                return val * 0.01
 | 
					                return val * 0.01
 | 
				
			||||||
            return (((1 << 16) - 1) - val) * -0.01
 | 
					            return (((1 << 16) - 1) - val) * -0.01
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        val = self.getter(f"GainLayer[{self._i}]")
 | 
					        val = self.getter(f'GainLayer[{self._i}]')
 | 
				
			||||||
        return round(val if val else fget(), 1)
 | 
					        return round(val if val else fget(), 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @gain.setter
 | 
					    @gain.setter
 | 
				
			||||||
    def gain(self, val: float):
 | 
					    def gain(self, val: float):
 | 
				
			||||||
        self.setter(f"GainLayer[{self._i}]", val)
 | 
					        self.setter(f'GainLayer[{self._i}]', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _make_gainlayer_mixin(remote, index):
 | 
					def _make_gainlayer_mixin(remote, index):
 | 
				
			||||||
    """Creates a GainLayer mixin"""
 | 
					    """Creates a GainLayer mixin"""
 | 
				
			||||||
    return type(
 | 
					    return type(
 | 
				
			||||||
        f"GainlayerMixin",
 | 
					        'GainlayerMixin',
 | 
				
			||||||
        (),
 | 
					        (),
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "gainlayer": tuple(
 | 
					            'gainlayer': tuple(
 | 
				
			||||||
                GainLayer(remote, index, i) for i in range(remote.kind.num_bus)
 | 
					                GainLayer(remote, index, i) for i in range(remote.kind.num_bus)
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -379,14 +381,14 @@ def _make_gainlayer_mixin(remote, index):
 | 
				
			|||||||
def _make_channelout_mixin(kind):
 | 
					def _make_channelout_mixin(kind):
 | 
				
			||||||
    """Creates a channel out property mixin"""
 | 
					    """Creates a channel out property mixin"""
 | 
				
			||||||
    return type(
 | 
					    return type(
 | 
				
			||||||
        f"ChannelOutMixin{kind}",
 | 
					        f'ChannelOutMixin{kind}',
 | 
				
			||||||
        (),
 | 
					        (),
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            **{
 | 
					            **{
 | 
				
			||||||
                f"A{i}": strip_output_prop(f"A{i}") for i in range(1, kind.phys_out + 1)
 | 
					                f'A{i}': strip_output_prop(f'A{i}') for i in range(1, kind.phys_out + 1)
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            **{
 | 
					            **{
 | 
				
			||||||
                f"B{i}": strip_output_prop(f"B{i}") for i in range(1, kind.virt_out + 1)
 | 
					                f'B{i}': strip_output_prop(f'B{i}') for i in range(1, kind.virt_out + 1)
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
@ -410,12 +412,12 @@ def strip_factory(is_phys_strip, remote, i) -> Union[PhysicalStrip, VirtualStrip
 | 
				
			|||||||
    GAINLAYERMIXIN_cls = _make_gainlayer_mixin(remote, i)
 | 
					    GAINLAYERMIXIN_cls = _make_gainlayer_mixin(remote, i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return type(
 | 
					    return type(
 | 
				
			||||||
        f"{STRIP_cls.__name__}{remote.kind}",
 | 
					        f'{STRIP_cls.__name__}{remote.kind}',
 | 
				
			||||||
        (STRIP_cls, CHANNELOUTMIXIN_cls, GAINLAYERMIXIN_cls),
 | 
					        (STRIP_cls, CHANNELOUTMIXIN_cls, GAINLAYERMIXIN_cls),
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "levels": StripLevel(remote, i),
 | 
					            'levels': StripLevel(remote, i),
 | 
				
			||||||
            **{param: channel_bool_prop(param) for param in ["mono", "solo", "mute"]},
 | 
					            **{param: channel_bool_prop(param) for param in ['mono', 'solo', 'mute']},
 | 
				
			||||||
            "label": channel_label_prop(),
 | 
					            'label': channel_label_prop(),
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    )(remote, i)
 | 
					    )(remote, i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -20,10 +20,10 @@ class Subject:
 | 
				
			|||||||
        """run callbacks on update"""
 | 
					        """run callbacks on update"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for o in self._observers:
 | 
					        for o in self._observers:
 | 
				
			||||||
            if hasattr(o, "on_update"):
 | 
					            if hasattr(o, 'on_update'):
 | 
				
			||||||
                o.on_update(event)
 | 
					                o.on_update(event)
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                if o.__name__ == f"on_{event}":
 | 
					                if o.__name__ == f'on_{event}':
 | 
				
			||||||
                    o()
 | 
					                    o()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def add(self, observer):
 | 
					    def add(self, observer):
 | 
				
			||||||
@ -34,15 +34,15 @@ class Subject:
 | 
				
			|||||||
            for o in iterator:
 | 
					            for o in iterator:
 | 
				
			||||||
                if o not in self._observers:
 | 
					                if o not in self._observers:
 | 
				
			||||||
                    self._observers.append(o)
 | 
					                    self._observers.append(o)
 | 
				
			||||||
                    self.logger.info(f"{o} added to event observers")
 | 
					                    self.logger.info(f'{o} added to event observers')
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    self.logger.error(f"Failed to add {o} to event observers")
 | 
					                    self.logger.error(f'Failed to add {o} to event observers')
 | 
				
			||||||
        except TypeError:
 | 
					        except TypeError:
 | 
				
			||||||
            if observer not in self._observers:
 | 
					            if observer not in self._observers:
 | 
				
			||||||
                self._observers.append(observer)
 | 
					                self._observers.append(observer)
 | 
				
			||||||
                self.logger.info(f"{observer} added to event observers")
 | 
					                self.logger.info(f'{observer} added to event observers')
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                self.logger.error(f"Failed to add {observer} to event observers")
 | 
					                self.logger.error(f'Failed to add {observer} to event observers')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    register = add
 | 
					    register = add
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -54,15 +54,15 @@ class Subject:
 | 
				
			|||||||
            for o in iterator:
 | 
					            for o in iterator:
 | 
				
			||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    self._observers.remove(o)
 | 
					                    self._observers.remove(o)
 | 
				
			||||||
                    self.logger.info(f"{o} removed from event observers")
 | 
					                    self.logger.info(f'{o} removed from event observers')
 | 
				
			||||||
                except ValueError:
 | 
					                except ValueError:
 | 
				
			||||||
                    self.logger.error(f"Failed to remove {o} from event observers")
 | 
					                    self.logger.error(f'Failed to remove {o} from event observers')
 | 
				
			||||||
        except TypeError:
 | 
					        except TypeError:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                self._observers.remove(observer)
 | 
					                self._observers.remove(observer)
 | 
				
			||||||
                self.logger.info(f"{observer} removed from event observers")
 | 
					                self.logger.info(f'{observer} removed from event observers')
 | 
				
			||||||
            except ValueError:
 | 
					            except ValueError:
 | 
				
			||||||
                self.logger.error(f"Failed to remove {observer} from event observers")
 | 
					                self.logger.error(f'Failed to remove {observer} from event observers')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    deregister = remove
 | 
					    deregister = remove
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,3 @@
 | 
				
			|||||||
from enum import IntEnum
 | 
					 | 
				
			||||||
from typing import Iterator
 | 
					from typing import Iterator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -42,15 +41,15 @@ def script(func):
 | 
				
			|||||||
    def wrapper(*args):
 | 
					    def wrapper(*args):
 | 
				
			||||||
        remote, script = args
 | 
					        remote, script = args
 | 
				
			||||||
        if isinstance(script, dict):
 | 
					        if isinstance(script, dict):
 | 
				
			||||||
            params = ""
 | 
					            params = ''
 | 
				
			||||||
            for key, val in script.items():
 | 
					            for key, val in script.items():
 | 
				
			||||||
                obj, m2, *rem = key.split("-")
 | 
					                obj, m2, *rem = key.split('-')
 | 
				
			||||||
                index = int(m2) if m2.isnumeric() else int(*rem)
 | 
					                index = int(m2) if m2.isnumeric() else int(*rem)
 | 
				
			||||||
                params += ";".join(
 | 
					                params += ';'.join(
 | 
				
			||||||
                    f"{obj}{f'.{m2}stream' if not m2.isnumeric() else ''}[{index}].{k}={int(v) if isinstance(v, bool) else v}"
 | 
					                    f'{obj}{f".{m2}stream" if not m2.isnumeric() else ""}[{index}].{k}={int(v) if isinstance(v, bool) else v}'
 | 
				
			||||||
                    for k, v in val.items()
 | 
					                    for k, v in val.items()
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                params += ";"
 | 
					                params += ';'
 | 
				
			||||||
            script = params
 | 
					            script = params
 | 
				
			||||||
        return func(remote, script)
 | 
					        return func(remote, script)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -83,6 +82,3 @@ def deep_merge(dict1, dict2):
 | 
				
			|||||||
            yield k, dict1[k]
 | 
					            yield k, dict1[k]
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            yield k, dict2[k]
 | 
					            yield k, dict2[k]
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Socket = IntEnum("Socket", "register request response", start=0)
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,7 @@ class VbanStream(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def identifier(self) -> str:
 | 
					    def identifier(self) -> str:
 | 
				
			||||||
        return f"vban.{self.direction}stream[{self.index}]"
 | 
					        return f'vban.{self.direction}stream[{self.index}]'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def on(self) -> bool:
 | 
					    def on(self) -> bool:
 | 
				
			||||||
@ -25,7 +25,7 @@ class VbanStream(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @on.setter
 | 
					    @on.setter
 | 
				
			||||||
    def on(self, val: bool):
 | 
					    def on(self, val: bool):
 | 
				
			||||||
        self.setter("on", 1 if val else 0)
 | 
					        self.setter('on', 1 if val else 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def name(self) -> str:
 | 
					    def name(self) -> str:
 | 
				
			||||||
@ -33,7 +33,7 @@ class VbanStream(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @name.setter
 | 
					    @name.setter
 | 
				
			||||||
    def name(self, val: str):
 | 
					    def name(self, val: str):
 | 
				
			||||||
        self.setter("name", val)
 | 
					        self.setter('name', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def ip(self) -> str:
 | 
					    def ip(self) -> str:
 | 
				
			||||||
@ -41,7 +41,7 @@ class VbanStream(IRemote):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @ip.setter
 | 
					    @ip.setter
 | 
				
			||||||
    def ip(self, val: str):
 | 
					    def ip(self, val: str):
 | 
				
			||||||
        self.setter("ip", val)
 | 
					        self.setter('ip', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def port(self) -> int:
 | 
					    def port(self) -> int:
 | 
				
			||||||
@ -51,9 +51,9 @@ class VbanStream(IRemote):
 | 
				
			|||||||
    def port(self, val: int):
 | 
					    def port(self, val: int):
 | 
				
			||||||
        if not 1024 <= val <= 65535:
 | 
					        if not 1024 <= val <= 65535:
 | 
				
			||||||
            self.logger.warning(
 | 
					            self.logger.warning(
 | 
				
			||||||
                f"port got: {val} but expected a value from 1024 to 65535"
 | 
					                f'port got: {val} but expected a value from 1024 to 65535'
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        self.setter("port", val)
 | 
					        self.setter('port', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def sr(self) -> int:
 | 
					    def sr(self) -> int:
 | 
				
			||||||
@ -63,8 +63,8 @@ class VbanStream(IRemote):
 | 
				
			|||||||
    def sr(self, val: int):
 | 
					    def sr(self, val: int):
 | 
				
			||||||
        opts = (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000)
 | 
					        opts = (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000)
 | 
				
			||||||
        if val not in opts:
 | 
					        if val not in opts:
 | 
				
			||||||
            self.logger.warning(f"sr got: {val} but expected a value in {opts}")
 | 
					            self.logger.warning(f'sr got: {val} but expected a value in {opts}')
 | 
				
			||||||
        self.setter("sr", val)
 | 
					        self.setter('sr', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def channel(self) -> int:
 | 
					    def channel(self) -> int:
 | 
				
			||||||
@ -73,8 +73,8 @@ class VbanStream(IRemote):
 | 
				
			|||||||
    @channel.setter
 | 
					    @channel.setter
 | 
				
			||||||
    def channel(self, val: int):
 | 
					    def channel(self, val: int):
 | 
				
			||||||
        if not 1 <= val <= 8:
 | 
					        if not 1 <= val <= 8:
 | 
				
			||||||
            self.logger.warning(f"channel got: {val} but expected a value from 1 to 8")
 | 
					            self.logger.warning(f'channel got: {val} but expected a value from 1 to 8')
 | 
				
			||||||
        self.setter("channel", val)
 | 
					        self.setter('channel', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def bit(self) -> int:
 | 
					    def bit(self) -> int:
 | 
				
			||||||
@ -83,8 +83,8 @@ class VbanStream(IRemote):
 | 
				
			|||||||
    @bit.setter
 | 
					    @bit.setter
 | 
				
			||||||
    def bit(self, val: int):
 | 
					    def bit(self, val: int):
 | 
				
			||||||
        if val not in (16, 24):
 | 
					        if val not in (16, 24):
 | 
				
			||||||
            self.logger.warning(f"bit got: {val} but expected value 16 or 24")
 | 
					            self.logger.warning(f'bit got: {val} but expected value 16 or 24')
 | 
				
			||||||
        self.setter("bit", 1 if (val == 16) else 2)
 | 
					        self.setter('bit', 1 if (val == 16) else 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def quality(self) -> int:
 | 
					    def quality(self) -> int:
 | 
				
			||||||
@ -93,8 +93,8 @@ class VbanStream(IRemote):
 | 
				
			|||||||
    @quality.setter
 | 
					    @quality.setter
 | 
				
			||||||
    def quality(self, val: int):
 | 
					    def quality(self, val: int):
 | 
				
			||||||
        if not 0 <= val <= 4:
 | 
					        if not 0 <= val <= 4:
 | 
				
			||||||
            self.logger.warning(f"quality got: {val} but expected a value from 0 to 4")
 | 
					            self.logger.warning(f'quality got: {val} but expected a value from 0 to 4')
 | 
				
			||||||
        self.setter("quality", val)
 | 
					        self.setter('quality', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def route(self) -> int:
 | 
					    def route(self) -> int:
 | 
				
			||||||
@ -103,8 +103,8 @@ class VbanStream(IRemote):
 | 
				
			|||||||
    @route.setter
 | 
					    @route.setter
 | 
				
			||||||
    def route(self, val: int):
 | 
					    def route(self, val: int):
 | 
				
			||||||
        if not 0 <= val <= 8:
 | 
					        if not 0 <= val <= 8:
 | 
				
			||||||
            self.logger.warning(f"route got: {val} but expected a value from 0 to 8")
 | 
					            self.logger.warning(f'route got: {val} but expected a value from 0 to 8')
 | 
				
			||||||
        self.setter("route", val)
 | 
					        self.setter('route', val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class VbanInstream(VbanStream):
 | 
					class VbanInstream(VbanStream):
 | 
				
			||||||
@ -115,11 +115,11 @@ class VbanInstream(VbanStream):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return f"{type(self).__name__}{self._remote.kind}{self.index}"
 | 
					        return f'{type(self).__name__}{self._remote.kind}{self.index}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def direction(self) -> str:
 | 
					    def direction(self) -> str:
 | 
				
			||||||
        return "in"
 | 
					        return 'in'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def sr(self) -> int:
 | 
					    def sr(self) -> int:
 | 
				
			||||||
@ -154,11 +154,11 @@ class VbanOutstream(VbanStream):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return f"{type(self).__name__}{self._remote.kind}{self.index}"
 | 
					        return f'{type(self).__name__}{self._remote.kind}{self.index}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def direction(self) -> str:
 | 
					    def direction(self) -> str:
 | 
				
			||||||
        return "out"
 | 
					        return 'out'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class VbanAudioOutstream(VbanOutstream):
 | 
					class VbanAudioOutstream(VbanOutstream):
 | 
				
			||||||
@ -174,22 +174,22 @@ def _make_stream_pair(remote, kind):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def _make_cls(i, direction):
 | 
					    def _make_cls(i, direction):
 | 
				
			||||||
        match direction:
 | 
					        match direction:
 | 
				
			||||||
            case "in":
 | 
					            case 'in':
 | 
				
			||||||
                if i < num_instream:
 | 
					                if i < num_instream:
 | 
				
			||||||
                    return VbanAudioInstream(remote, i)
 | 
					                    return VbanAudioInstream(remote, i)
 | 
				
			||||||
                elif i < num_instream + num_midi:
 | 
					                elif i < num_instream + num_midi:
 | 
				
			||||||
                    return VbanMidiInstream(remote, i)
 | 
					                    return VbanMidiInstream(remote, i)
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    return VbanTextInstream(remote, i)
 | 
					                    return VbanTextInstream(remote, i)
 | 
				
			||||||
            case "out":
 | 
					            case 'out':
 | 
				
			||||||
                if i < num_outstream:
 | 
					                if i < num_outstream:
 | 
				
			||||||
                    return VbanAudioOutstream(remote, i)
 | 
					                    return VbanAudioOutstream(remote, i)
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    return VbanMidiOutstream(remote, i)
 | 
					                    return VbanMidiOutstream(remote, i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        tuple(_make_cls(i, "in") for i in range(num_instream + num_midi + num_text)),
 | 
					        tuple(_make_cls(i, 'in') for i in range(num_instream + num_midi + num_text)),
 | 
				
			||||||
        tuple(_make_cls(i, "out") for i in range(num_outstream + num_midi)),
 | 
					        tuple(_make_cls(i, 'out') for i in range(num_outstream + num_midi)),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -212,7 +212,7 @@ class Vban:
 | 
				
			|||||||
        """if VBAN disabled there can be no communication with it"""
 | 
					        """if VBAN disabled there can be no communication with it"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def disable(self):
 | 
					    def disable(self):
 | 
				
			||||||
        self.remote._set_rt("vban.Enable", 0)
 | 
					        self.remote._set_rt('vban.Enable', 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def vban_factory(remote) -> Vban:
 | 
					def vban_factory(remote) -> Vban:
 | 
				
			||||||
@ -222,7 +222,7 @@ def vban_factory(remote) -> Vban:
 | 
				
			|||||||
    Returns a class that represents the VBAN module.
 | 
					    Returns a class that represents the VBAN module.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    VBAN_cls = Vban
 | 
					    VBAN_cls = Vban
 | 
				
			||||||
    return type(f"{VBAN_cls.__name__}", (VBAN_cls,), {})(remote)
 | 
					    return type(f'{VBAN_cls.__name__}', (VBAN_cls,), {})(remote)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def request_vban_obj(remote) -> Vban:
 | 
					def request_vban_obj(remote) -> Vban:
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ from .error import VBANCMDError
 | 
				
			|||||||
from .event import Event
 | 
					from .event import Event
 | 
				
			||||||
from .packet import RequestHeader
 | 
					from .packet import RequestHeader
 | 
				
			||||||
from .subject import Subject
 | 
					from .subject import Subject
 | 
				
			||||||
from .util import Socket, deep_merge, script
 | 
					from .util import deep_merge, script
 | 
				
			||||||
from .worker import Producer, Subscriber, Updater
 | 
					from .worker import Producer, Subscriber, Updater
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = logging.getLogger(__name__)
 | 
					logger = logging.getLogger(__name__)
 | 
				
			||||||
@ -31,8 +31,8 @@ class VbanCmd(metaclass=ABCMeta):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def __init__(self, **kwargs):
 | 
					    def __init__(self, **kwargs):
 | 
				
			||||||
        self.logger = logger.getChild(self.__class__.__name__)
 | 
					        self.logger = logger.getChild(self.__class__.__name__)
 | 
				
			||||||
        self.event = Event({k: kwargs.pop(k) for k in ("pdirty", "ldirty")})
 | 
					        self.event = Event({k: kwargs.pop(k) for k in ('pdirty', 'ldirty')})
 | 
				
			||||||
        if not kwargs["ip"]:
 | 
					        if not kwargs['ip']:
 | 
				
			||||||
            kwargs |= self._conn_from_toml()
 | 
					            kwargs |= self._conn_from_toml()
 | 
				
			||||||
        for attr, val in kwargs.items():
 | 
					        for attr, val in kwargs.items():
 | 
				
			||||||
            setattr(self, attr, val)
 | 
					            setattr(self, attr, val)
 | 
				
			||||||
@ -42,9 +42,7 @@ class VbanCmd(metaclass=ABCMeta):
 | 
				
			|||||||
            bps_index=self.BPS_OPTS.index(self.bps),
 | 
					            bps_index=self.BPS_OPTS.index(self.bps),
 | 
				
			||||||
            channel=self.channel,
 | 
					            channel=self.channel,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.socks = tuple(
 | 
					        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 | 
				
			||||||
            socket.socket(socket.AF_INET, socket.SOCK_DGRAM) for _ in Socket
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.subject = self.observer = Subject()
 | 
					        self.subject = self.observer = Subject()
 | 
				
			||||||
        self.cache = {}
 | 
					        self.cache = {}
 | 
				
			||||||
        self._pdirty = False
 | 
					        self._pdirty = False
 | 
				
			||||||
@ -65,29 +63,30 @@ class VbanCmd(metaclass=ABCMeta):
 | 
				
			|||||||
            import tomli as tomllib
 | 
					            import tomli as tomllib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def get_filepath():
 | 
					        def get_filepath():
 | 
				
			||||||
            filepaths = [
 | 
					            for pn in (
 | 
				
			||||||
                Path.cwd() / "vban.toml",
 | 
					                Path.cwd() / 'vban.toml',
 | 
				
			||||||
                Path.cwd() / "configs" / "vban.toml",
 | 
					                Path.cwd() / 'configs' / 'vban.toml',
 | 
				
			||||||
                Path.home() / ".config" / "vban-cmd" / "vban.toml",
 | 
					                Path.home() / '.config' / 'vban-cmd' / 'vban.toml',
 | 
				
			||||||
                Path.home() / "Documents" / "Voicemeeter" / "configs" / "vban.toml",
 | 
					                Path.home() / 'Documents' / 'Voicemeeter' / 'configs' / 'vban.toml',
 | 
				
			||||||
            ]
 | 
					            ):
 | 
				
			||||||
            for filepath in filepaths:
 | 
					                if pn.exists():
 | 
				
			||||||
                if filepath.exists():
 | 
					                    return pn
 | 
				
			||||||
                    return filepath
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if filepath := get_filepath():
 | 
					        if not (filepath := get_filepath()):
 | 
				
			||||||
            with open(filepath, "rb") as f:
 | 
					            raise VBANCMDError('no ip provided and no vban.toml located.')
 | 
				
			||||||
                conn = tomllib.load(f)
 | 
					        try:
 | 
				
			||||||
                assert (
 | 
					            with open(filepath, 'rb') as f:
 | 
				
			||||||
                    "connection" in conn and "ip" in conn["connection"]
 | 
					                return tomllib.load(f)['connection']
 | 
				
			||||||
                ), "expected [connection][ip] in vban config"
 | 
					        except tomllib.TomlDecodeError as e:
 | 
				
			||||||
            return conn["connection"]
 | 
					            raise VBANCMDError(f'Error decoding {filepath}: {e}') from e
 | 
				
			||||||
        raise VBANCMDError("no ip provided and no vban.toml located.")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __enter__(self):
 | 
					    def __enter__(self):
 | 
				
			||||||
        self.login()
 | 
					        self.login()
 | 
				
			||||||
        return self
 | 
					        return self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __exit__(self, exc_type, exc_value, exc_traceback) -> None:
 | 
				
			||||||
 | 
					        self.logout()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def login(self) -> None:
 | 
					    def login(self) -> None:
 | 
				
			||||||
        """Starts the subscriber and updater threads (unless in outbound mode)"""
 | 
					        """Starts the subscriber and updater threads (unless in outbound mode)"""
 | 
				
			||||||
        if not self.outbound:
 | 
					        if not self.outbound:
 | 
				
			||||||
@ -110,31 +109,41 @@ class VbanCmd(metaclass=ABCMeta):
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def logout(self) -> None:
 | 
				
			||||||
 | 
					        if not self.stopped():
 | 
				
			||||||
 | 
					            self.logger.debug('events thread shutdown started')
 | 
				
			||||||
 | 
					            self.stop_event.set()
 | 
				
			||||||
 | 
					            if self.producer is not None:
 | 
				
			||||||
 | 
					                for t in (self.producer, self.subscriber):
 | 
				
			||||||
 | 
					                    t.join()
 | 
				
			||||||
 | 
					        self.sock.close()
 | 
				
			||||||
 | 
					        self.logger.info(f'{type(self).__name__}: Successfully logged out of {self}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def stopped(self):
 | 
					    def stopped(self):
 | 
				
			||||||
        return self.stop_event is None or 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."""
 | 
				
			||||||
        self.socks[Socket.request].sendto(
 | 
					        self.sock.sendto(
 | 
				
			||||||
            self.packet_request.header + f"{cmd}={val};".encode(),
 | 
					            self.packet_request.header + f'{cmd}={val};'.encode(),
 | 
				
			||||||
            (socket.gethostbyname(self.ip), self.port),
 | 
					            (socket.gethostbyname(self.ip), self.port),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.packet_request.framecounter = (
 | 
					        self.packet_request.framecounter = (
 | 
				
			||||||
            int.from_bytes(self.packet_request.framecounter, "little") + 1
 | 
					            int.from_bytes(self.packet_request.framecounter, 'little') + 1
 | 
				
			||||||
        ).to_bytes(4, "little")
 | 
					        ).to_bytes(4, 'little')
 | 
				
			||||||
        self.cache[cmd] = val
 | 
					        self.cache[cmd] = val
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @script
 | 
					    @script
 | 
				
			||||||
    def sendtext(self, script):
 | 
					    def sendtext(self, script):
 | 
				
			||||||
        """Sends a multiple parameter string over a network."""
 | 
					        """Sends a multiple parameter string over a network."""
 | 
				
			||||||
        self.socks[Socket.request].sendto(
 | 
					        self.sock.sendto(
 | 
				
			||||||
            self.packet_request.header + script.encode(),
 | 
					            self.packet_request.header + script.encode(),
 | 
				
			||||||
            (socket.gethostbyname(self.ip), self.port),
 | 
					            (socket.gethostbyname(self.ip), self.port),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.packet_request.framecounter = (
 | 
					        self.packet_request.framecounter = (
 | 
				
			||||||
            int.from_bytes(self.packet_request.framecounter, "little") + 1
 | 
					            int.from_bytes(self.packet_request.framecounter, 'little') + 1
 | 
				
			||||||
        ).to_bytes(4, "little")
 | 
					        ).to_bytes(4, 'little')
 | 
				
			||||||
        self.logger.debug(f"sendtext: {script}")
 | 
					        self.logger.debug(f'sendtext: {script}')
 | 
				
			||||||
        time.sleep(self.DELAY)
 | 
					        time.sleep(self.DELAY)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
@ -145,7 +154,7 @@ class VbanCmd(metaclass=ABCMeta):
 | 
				
			|||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def version(self) -> str:
 | 
					    def version(self) -> str:
 | 
				
			||||||
        """Returns Voicemeeter's version as a string"""
 | 
					        """Returns Voicemeeter's version as a string"""
 | 
				
			||||||
        return "{0}.{1}.{2}.{3}".format(*self.public_packet.voicemeeterversion)
 | 
					        return '{0}.{1}.{2}.{3}'.format(*self.public_packet.voicemeeterversion)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def pdirty(self):
 | 
					    def pdirty(self):
 | 
				
			||||||
@ -184,16 +193,16 @@ class VbanCmd(metaclass=ABCMeta):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def target(key):
 | 
					        def target(key):
 | 
				
			||||||
            match key.split("-"):
 | 
					            match key.split('-'):
 | 
				
			||||||
                case ["strip" | "bus" as kls, index] if index.isnumeric():
 | 
					                case ['strip' | 'bus' as kls, index] if index.isnumeric():
 | 
				
			||||||
                    target = getattr(self, kls)
 | 
					                    target = getattr(self, kls)
 | 
				
			||||||
                case [
 | 
					                case [
 | 
				
			||||||
                    "vban",
 | 
					                    'vban',
 | 
				
			||||||
                    "in" | "instream" | "out" | "outstream" as direction,
 | 
					                    'in' | 'instream' | 'out' | 'outstream' as direction,
 | 
				
			||||||
                    index,
 | 
					                    index,
 | 
				
			||||||
                ] if index.isnumeric():
 | 
					                ] if index.isnumeric():
 | 
				
			||||||
                    target = getattr(
 | 
					                    target = getattr(
 | 
				
			||||||
                        self.vban, f"{direction.removesuffix('stream')}stream"
 | 
					                        self.vban, f'{direction.removesuffix("stream")}stream'
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                case _:
 | 
					                case _:
 | 
				
			||||||
                    ERR_MSG = f"invalid config key '{key}'"
 | 
					                    ERR_MSG = f"invalid config key '{key}'"
 | 
				
			||||||
@ -207,36 +216,23 @@ class VbanCmd(metaclass=ABCMeta):
 | 
				
			|||||||
        """applies a config from memory"""
 | 
					        """applies a config from memory"""
 | 
				
			||||||
        ERR_MSG = (
 | 
					        ERR_MSG = (
 | 
				
			||||||
            f"No config with name '{name}' is loaded into memory",
 | 
					            f"No config with name '{name}' is loaded into memory",
 | 
				
			||||||
            f"Known configs: {list(self.configs.keys())}",
 | 
					            f'Known configs: {list(self.configs.keys())}',
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            config = self.configs[name]
 | 
					            config = self.configs[name]
 | 
				
			||||||
        except KeyError as e:
 | 
					        except KeyError as e:
 | 
				
			||||||
            self.logger.error(("\n").join(ERR_MSG))
 | 
					            self.logger.error(('\n').join(ERR_MSG))
 | 
				
			||||||
            raise VBANCMDError(("\n").join(ERR_MSG)) from e
 | 
					            raise VBANCMDError(('\n').join(ERR_MSG)) from e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if "extends" in config:
 | 
					        if 'extends' in config:
 | 
				
			||||||
            extended = config["extends"]
 | 
					            extended = config['extends']
 | 
				
			||||||
            config = {
 | 
					            config = {
 | 
				
			||||||
                k: v
 | 
					                k: v
 | 
				
			||||||
                for k, v in deep_merge(self.configs[extended], config)
 | 
					                for k, v in deep_merge(self.configs[extended], config)
 | 
				
			||||||
                if k not in ("extends")
 | 
					                if k not in ('extends')
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            self.logger.debug(
 | 
					            self.logger.debug(
 | 
				
			||||||
                f"profile '{name}' extends '{extended}', profiles merged.."
 | 
					                f"profile '{name}' extends '{extended}', profiles merged.."
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        self.apply(config)
 | 
					        self.apply(config)
 | 
				
			||||||
        self.logger.info(f"Profile '{name}' applied!")
 | 
					        self.logger.info(f"Profile '{name}' applied!")
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def logout(self) -> None:
 | 
					 | 
				
			||||||
        if not self.stopped():
 | 
					 | 
				
			||||||
            self.logger.debug("events thread shutdown started")
 | 
					 | 
				
			||||||
            self.stop_event.set()
 | 
					 | 
				
			||||||
            if self.producer is not None:
 | 
					 | 
				
			||||||
                for t in (self.producer, self.subscriber):
 | 
					 | 
				
			||||||
                    t.join()
 | 
					 | 
				
			||||||
        [sock.close() for sock in self.socks]
 | 
					 | 
				
			||||||
        self.logger.info(f"{type(self).__name__}: Successfully logged out of {self}")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __exit__(self, exc_type, exc_value, exc_traceback) -> None:
 | 
					 | 
				
			||||||
        self.logout()
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,6 @@ from typing import Optional
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from .error import VBANCMDConnectionError
 | 
					from .error import VBANCMDConnectionError
 | 
				
			||||||
from .packet import HEADER_SIZE, SubscribeHeader, VbanRtPacket, VbanRtPacketHeader
 | 
					from .packet import HEADER_SIZE, SubscribeHeader, VbanRtPacket, VbanRtPacketHeader
 | 
				
			||||||
from .util import Socket
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = logging.getLogger(__name__)
 | 
					logger = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -15,7 +14,7 @@ class Subscriber(threading.Thread):
 | 
				
			|||||||
    """fire a subscription packet every 10 seconds"""
 | 
					    """fire a subscription packet every 10 seconds"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, remote, stop_event):
 | 
					    def __init__(self, remote, stop_event):
 | 
				
			||||||
        super().__init__(name="subscriber", daemon=False)
 | 
					        super().__init__(name='subscriber', daemon=False)
 | 
				
			||||||
        self._remote = remote
 | 
					        self._remote = remote
 | 
				
			||||||
        self.stop_event = stop_event
 | 
					        self.stop_event = stop_event
 | 
				
			||||||
        self.logger = logger.getChild(self.__class__.__name__)
 | 
					        self.logger = logger.getChild(self.__class__.__name__)
 | 
				
			||||||
@ -24,20 +23,20 @@ class Subscriber(threading.Thread):
 | 
				
			|||||||
    def run(self):
 | 
					    def run(self):
 | 
				
			||||||
        while not self.stopped():
 | 
					        while not self.stopped():
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                self._remote.socks[Socket.register].sendto(
 | 
					                self._remote.sock.sendto(
 | 
				
			||||||
                    self.packet.header,
 | 
					                    self.packet.header,
 | 
				
			||||||
                    (socket.gethostbyname(self._remote.ip), self._remote.port),
 | 
					                    (socket.gethostbyname(self._remote.ip), self._remote.port),
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                self.packet.framecounter = (
 | 
					                self.packet.framecounter = (
 | 
				
			||||||
                    int.from_bytes(self.packet.framecounter, "little") + 1
 | 
					                    int.from_bytes(self.packet.framecounter, 'little') + 1
 | 
				
			||||||
                ).to_bytes(4, "little")
 | 
					                ).to_bytes(4, 'little')
 | 
				
			||||||
                self.wait_until_stopped(10)
 | 
					                self.wait_until_stopped(10)
 | 
				
			||||||
            except socket.gaierror as e:
 | 
					            except socket.gaierror as e:
 | 
				
			||||||
                self.logger.exception(f"{type(e).__name__}: {e}")
 | 
					                self.logger.exception(f'{type(e).__name__}: {e}')
 | 
				
			||||||
                raise VBANCMDConnectionError(
 | 
					                raise VBANCMDConnectionError(
 | 
				
			||||||
                    f"unable to resolve hostname {self._remote.ip}"
 | 
					                    f'unable to resolve hostname {self._remote.ip}'
 | 
				
			||||||
                ) from e
 | 
					                ) from e
 | 
				
			||||||
        self.logger.debug(f"terminating {self.name} thread")
 | 
					        self.logger.debug(f'terminating {self.name} thread')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def stopped(self):
 | 
					    def stopped(self):
 | 
				
			||||||
        return self.stop_event.is_set()
 | 
					        return self.stop_event.is_set()
 | 
				
			||||||
@ -54,20 +53,17 @@ class Producer(threading.Thread):
 | 
				
			|||||||
    """Continously send job queue to the Updater thread at a rate of self._remote.ratelimit."""
 | 
					    """Continously send job queue to the Updater thread at a rate of self._remote.ratelimit."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, remote, queue, stop_event):
 | 
					    def __init__(self, remote, queue, stop_event):
 | 
				
			||||||
        super().__init__(name="producer", daemon=False)
 | 
					        super().__init__(name='producer', daemon=False)
 | 
				
			||||||
        self._remote = remote
 | 
					        self._remote = remote
 | 
				
			||||||
        self.queue = queue
 | 
					        self.queue = queue
 | 
				
			||||||
        self.stop_event = stop_event
 | 
					        self.stop_event = stop_event
 | 
				
			||||||
        self.logger = logger.getChild(self.__class__.__name__)
 | 
					        self.logger = logger.getChild(self.__class__.__name__)
 | 
				
			||||||
        self.packet_expected = VbanRtPacketHeader()
 | 
					        self.packet_expected = VbanRtPacketHeader()
 | 
				
			||||||
        self._remote.socks[Socket.response].settimeout(self._remote.timeout)
 | 
					        self._remote.sock.settimeout(self._remote.timeout)
 | 
				
			||||||
        self._remote.socks[Socket.response].bind(
 | 
					 | 
				
			||||||
            (socket.gethostbyname(socket.gethostname()), self._remote.port)
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self._remote._public_packet = self._get_rt()
 | 
					        self._remote._public_packet = self._get_rt()
 | 
				
			||||||
        (
 | 
					        (
 | 
				
			||||||
            self._remote.cache["strip_level"],
 | 
					            self._remote.cache['strip_level'],
 | 
				
			||||||
            self._remote.cache["bus_level"],
 | 
					            self._remote.cache['bus_level'],
 | 
				
			||||||
        ) = self._remote._get_levels(self._remote.public_packet)
 | 
					        ) = self._remote._get_levels(self._remote.public_packet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_rt(self) -> VbanRtPacket:
 | 
					    def _get_rt(self) -> VbanRtPacket:
 | 
				
			||||||
@ -83,7 +79,7 @@ class Producer(threading.Thread):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def _fetch_rt_packet(self) -> Optional[VbanRtPacket]:
 | 
					    def _fetch_rt_packet(self) -> Optional[VbanRtPacket]:
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            data, _ = self._remote.socks[Socket.response].recvfrom(2048)
 | 
					            data, _ = self._remote.sock.recvfrom(2048)
 | 
				
			||||||
            # do we have packet data?
 | 
					            # do we have packet data?
 | 
				
			||||||
            if len(data) > HEADER_SIZE:
 | 
					            if len(data) > HEADER_SIZE:
 | 
				
			||||||
                # is the packet of type VBAN RT response?
 | 
					                # is the packet of type VBAN RT response?
 | 
				
			||||||
@ -114,9 +110,9 @@ class Producer(threading.Thread):
 | 
				
			|||||||
                        _busLabelUTF8c60=data[932:1412],
 | 
					                        _busLabelUTF8c60=data[932:1412],
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
        except TimeoutError as e:
 | 
					        except TimeoutError as e:
 | 
				
			||||||
            self.logger.exception(f"{type(e).__name__}: {e}")
 | 
					            self.logger.exception(f'{type(e).__name__}: {e}')
 | 
				
			||||||
            raise VBANCMDConnectionError(
 | 
					            raise VBANCMDConnectionError(
 | 
				
			||||||
                f"timeout waiting for RtPacket from {self._remote.ip}"
 | 
					                f'timeout waiting for RtPacket from {self._remote.ip}'
 | 
				
			||||||
            ) from e
 | 
					            ) from e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def stopped(self):
 | 
					    def stopped(self):
 | 
				
			||||||
@ -127,7 +123,7 @@ class Producer(threading.Thread):
 | 
				
			|||||||
            _pp = self._get_rt()
 | 
					            _pp = self._get_rt()
 | 
				
			||||||
            pdirty = _pp.pdirty(self._remote.public_packet)
 | 
					            pdirty = _pp.pdirty(self._remote.public_packet)
 | 
				
			||||||
            ldirty = _pp.ldirty(
 | 
					            ldirty = _pp.ldirty(
 | 
				
			||||||
                self._remote.cache["strip_level"], self._remote.cache["bus_level"]
 | 
					                self._remote.cache['strip_level'], self._remote.cache['bus_level']
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if pdirty or ldirty:
 | 
					            if pdirty or ldirty:
 | 
				
			||||||
@ -136,11 +132,11 @@ class Producer(threading.Thread):
 | 
				
			|||||||
            self._remote._ldirty = ldirty
 | 
					            self._remote._ldirty = ldirty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if self._remote.event.pdirty:
 | 
					            if self._remote.event.pdirty:
 | 
				
			||||||
                self.queue.put("pdirty")
 | 
					                self.queue.put('pdirty')
 | 
				
			||||||
            if self._remote.event.ldirty:
 | 
					            if self._remote.event.ldirty:
 | 
				
			||||||
                self.queue.put("ldirty")
 | 
					                self.queue.put('ldirty')
 | 
				
			||||||
            time.sleep(self._remote.ratelimit)
 | 
					            time.sleep(self._remote.ratelimit)
 | 
				
			||||||
        self.logger.debug(f"terminating {self.name} thread")
 | 
					        self.logger.debug(f'terminating {self.name} thread')
 | 
				
			||||||
        self.queue.put(None)
 | 
					        self.queue.put(None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -152,7 +148,7 @@ class Updater(threading.Thread):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, remote, queue):
 | 
					    def __init__(self, remote, queue):
 | 
				
			||||||
        super().__init__(name="updater", daemon=True)
 | 
					        super().__init__(name='updater', daemon=True)
 | 
				
			||||||
        self._remote = remote
 | 
					        self._remote = remote
 | 
				
			||||||
        self.queue = queue
 | 
					        self.queue = queue
 | 
				
			||||||
        self.logger = logger.getChild(self.__class__.__name__)
 | 
					        self.logger = logger.getChild(self.__class__.__name__)
 | 
				
			||||||
@ -166,19 +162,19 @@ class Updater(threading.Thread):
 | 
				
			|||||||
        Generate _strip_comp, _bus_comp and update level cache if ldirty.
 | 
					        Generate _strip_comp, _bus_comp and update level cache if ldirty.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        while event := self.queue.get():
 | 
					        while event := self.queue.get():
 | 
				
			||||||
            if event == "pdirty" and self._remote.pdirty:
 | 
					            if event == 'pdirty' and self._remote.pdirty:
 | 
				
			||||||
                self._remote.subject.notify(event)
 | 
					                self._remote.subject.notify(event)
 | 
				
			||||||
            elif event == "ldirty" and self._remote.ldirty:
 | 
					            elif event == 'ldirty' and self._remote.ldirty:
 | 
				
			||||||
                self._remote._strip_comp, self._remote._bus_comp = (
 | 
					                self._remote._strip_comp, self._remote._bus_comp = (
 | 
				
			||||||
                    self._remote._public_packet._strip_comp,
 | 
					                    self._remote._public_packet._strip_comp,
 | 
				
			||||||
                    self._remote._public_packet._bus_comp,
 | 
					                    self._remote._public_packet._bus_comp,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                (
 | 
					                (
 | 
				
			||||||
                    self._remote.cache["strip_level"],
 | 
					                    self._remote.cache['strip_level'],
 | 
				
			||||||
                    self._remote.cache["bus_level"],
 | 
					                    self._remote.cache['bus_level'],
 | 
				
			||||||
                ) = (
 | 
					                ) = (
 | 
				
			||||||
                    self._remote._public_packet.inputlevels,
 | 
					                    self._remote._public_packet.inputlevels,
 | 
				
			||||||
                    self._remote._public_packet.outputlevels,
 | 
					                    self._remote._public_packet.outputlevels,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                self._remote.subject.notify(event)
 | 
					                self._remote.subject.notify(event)
 | 
				
			||||||
        self.logger.debug(f"terminating {self.name} thread")
 | 
					        self.logger.debug(f'terminating {self.name} thread')
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user