mx-remote 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mx_remote/Interface.py +1656 -0
- mx_remote/Uid.py +137 -0
- mx_remote/__init__.py +12 -0
- mx_remote/api/BayConfig.py +53 -0
- mx_remote/api/__init__.py +8 -0
- mx_remote/const.py +20 -0
- mx_remote/main.py +117 -0
- mx_remote/proto/BayConfig.py +104 -0
- mx_remote/proto/Constants.py +382 -0
- mx_remote/proto/Data.py +142 -0
- mx_remote/proto/Factory.py +168 -0
- mx_remote/proto/FrameAmpDolbySettings.py +51 -0
- mx_remote/proto/FrameAmpZoneSettings.py +123 -0
- mx_remote/proto/FrameBase.py +80 -0
- mx_remote/proto/FrameBayConfig.py +47 -0
- mx_remote/proto/FrameBayConfigSecondary.py +43 -0
- mx_remote/proto/FrameBayHide.py +53 -0
- mx_remote/proto/FrameBayStatus.py +51 -0
- mx_remote/proto/FrameConnectStatus.py +38 -0
- mx_remote/proto/FrameDiscover.py +21 -0
- mx_remote/proto/FrameEDID.py +20 -0
- mx_remote/proto/FrameEDIDProfile.py +24 -0
- mx_remote/proto/FrameFilterStatus.py +39 -0
- mx_remote/proto/FrameFirmwareVersion.py +40 -0
- mx_remote/proto/FrameHeader.py +92 -0
- mx_remote/proto/FrameHello.py +77 -0
- mx_remote/proto/FrameLinks.py +45 -0
- mx_remote/proto/FrameMeshOperation.py +66 -0
- mx_remote/proto/FrameMirrorStatus.py +49 -0
- mx_remote/proto/FrameNetworkStatus.py +163 -0
- mx_remote/proto/FramePDUState.py +71 -0
- mx_remote/proto/FramePowerChange.py +38 -0
- mx_remote/proto/FrameRCAction.py +50 -0
- mx_remote/proto/FrameRCIr.py +17 -0
- mx_remote/proto/FrameRCKey.py +40 -0
- mx_remote/proto/FrameReboot.py +26 -0
- mx_remote/proto/FrameRoutingChange.py +66 -0
- mx_remote/proto/FrameSetName.py +27 -0
- mx_remote/proto/FrameSignalStatus.py +46 -0
- mx_remote/proto/FrameSignalStatusNew.py +285 -0
- mx_remote/proto/FrameSysTemperature.py +50 -0
- mx_remote/proto/FrameTXRCAction.py +58 -0
- mx_remote/proto/FrameTopology.py +36 -0
- mx_remote/proto/FrameV2IPDeviceConfiguration.py +86 -0
- mx_remote/proto/FrameV2IPLink.py +16 -0
- mx_remote/proto/FrameV2IPSetMaster.py +16 -0
- mx_remote/proto/FrameV2IPSourceSwitch.py +84 -0
- mx_remote/proto/FrameV2IPSources.py +43 -0
- mx_remote/proto/FrameV2IPStats.py +55 -0
- mx_remote/proto/FrameV2IPStreamDetails.py +46 -0
- mx_remote/proto/FrameVolume.py +62 -0
- mx_remote/proto/FrameVolumeDown.py +28 -0
- mx_remote/proto/FrameVolumeSet.py +81 -0
- mx_remote/proto/FrameVolumeUp.py +28 -0
- mx_remote/proto/LinkConfig.py +121 -0
- mx_remote/proto/PDUState.py +95 -0
- mx_remote/proto/Svd.py +69 -0
- mx_remote/proto/V2IPConfig.py +71 -0
- mx_remote/proto/V2IPStats.py +126 -0
- mx_remote/proto/__init__.py +8 -0
- mx_remote/proto/svd.csv +157 -0
- mx_remote/remote/Bay.py +923 -0
- mx_remote/remote/ConnectionAsync.py +132 -0
- mx_remote/remote/Device.py +530 -0
- mx_remote/remote/Link.py +187 -0
- mx_remote/remote/PDU.py +152 -0
- mx_remote/remote/Remote.py +300 -0
- mx_remote/remote/State.py +28 -0
- mx_remote/remote/V2IP.py +83 -0
- mx_remote-2.0.0.dist-info/METADATA +90 -0
- mx_remote-2.0.0.dist-info/RECORD +74 -0
- mx_remote-2.0.0.dist-info/WHEEL +4 -0
- mx_remote-2.0.0.dist-info/entry_points.txt +2 -0
- mx_remote-2.0.0.dist-info/licenses/LICENSE +11 -0
mx_remote/Uid.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
##################################################
|
|
2
|
+
## MX Remote Python Interface ##
|
|
3
|
+
## ##
|
|
4
|
+
## author: Lars Op den Kamp (lars@opdenkamp.eu) ##
|
|
5
|
+
## copyright (c) 2024 Op den Kamp IT Solutions ##
|
|
6
|
+
##################################################
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import json
|
|
10
|
+
_LOGGER = logging.getLogger(__name__)
|
|
11
|
+
class MxrDeviceUid:
|
|
12
|
+
''' Unique ID of an mx_remote device on the network'''
|
|
13
|
+
|
|
14
|
+
def __init__(self, value: object) -> None:
|
|
15
|
+
if isinstance(value, MxrDeviceUid):
|
|
16
|
+
self._value = value._value
|
|
17
|
+
return
|
|
18
|
+
if isinstance(value, str):
|
|
19
|
+
spl = value.split(".")
|
|
20
|
+
if len(spl) < 4:
|
|
21
|
+
raise Exception(f"invalid uid {value}")
|
|
22
|
+
self._value = []
|
|
23
|
+
for part in spl:
|
|
24
|
+
self._value += int(part, 16).to_bytes(4, 'little')
|
|
25
|
+
self._value = bytes(self._value)
|
|
26
|
+
elif isinstance(value, bytes):
|
|
27
|
+
if len(value) == 0:
|
|
28
|
+
self._value = None
|
|
29
|
+
elif len(value) < 16:
|
|
30
|
+
raise Exception(f"invalid uid length {len(value)}")
|
|
31
|
+
else:
|
|
32
|
+
self._value = value
|
|
33
|
+
else:
|
|
34
|
+
raise Exception(f"invalid uid type {str(type(value))}")
|
|
35
|
+
|
|
36
|
+
def toJson(self):
|
|
37
|
+
return json.dumps(self, default=lambda o: o.__dict__)
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def value(self) -> str:
|
|
41
|
+
''' value as human readble string '''
|
|
42
|
+
return ''.join('%02x'%i for i in reversed(self._value[0:4])) + '.' + \
|
|
43
|
+
''.join('%02x'%i for i in reversed(self._value[4:8])) + '.' + \
|
|
44
|
+
''.join('%02x'%i for i in reversed(self._value[8:12])) + '.' + \
|
|
45
|
+
''.join('%02x'%i for i in reversed(self._value[12:16]))
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def empty(self) -> bool:
|
|
49
|
+
''' True if all 0 '''
|
|
50
|
+
for v in self._value:
|
|
51
|
+
if int(v) != 0:
|
|
52
|
+
return False
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def byte_value(self) -> bytes:
|
|
57
|
+
''' value as bytes '''
|
|
58
|
+
return self._value
|
|
59
|
+
|
|
60
|
+
def __str__(self) -> str:
|
|
61
|
+
return self.value
|
|
62
|
+
|
|
63
|
+
def __repr__(self) -> str:
|
|
64
|
+
return str(self)
|
|
65
|
+
|
|
66
|
+
def __hash__(self) -> int:
|
|
67
|
+
return hash(self.value)
|
|
68
|
+
|
|
69
|
+
def __eq__(self, value: object) -> bool:
|
|
70
|
+
if isinstance(value, str) or isinstance(value, MxrDeviceUid):
|
|
71
|
+
return str(self) == str(value)
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
def __hash__(self) -> int:
|
|
75
|
+
return hash(str(self))
|
|
76
|
+
|
|
77
|
+
class MxrBayUidOld:
|
|
78
|
+
def __init__(self, serial:str, port_name:str) -> None:
|
|
79
|
+
self._serial = serial
|
|
80
|
+
self._port_name = port_name
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def serial(self) -> str:
|
|
84
|
+
return self._serial
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def port_name(self) -> str:
|
|
88
|
+
return self._port_name
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def empty(self) -> bool:
|
|
92
|
+
return (len(self.serial) == 0) or (len(self.port_name) == 0)
|
|
93
|
+
|
|
94
|
+
def __str__(self) -> str:
|
|
95
|
+
return f"{self.serial}:{self.port_name}"
|
|
96
|
+
|
|
97
|
+
def __eq__(self, value: object) -> bool:
|
|
98
|
+
if isinstance(value, str):
|
|
99
|
+
return (str(self) == value)
|
|
100
|
+
if isinstance(value, MxrBayUidOld):
|
|
101
|
+
return (self.serial == value.serial) and (self.port_name == value.port_name)
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
def __hash__(self) -> int:
|
|
105
|
+
return hash(str(self))
|
|
106
|
+
|
|
107
|
+
class MxrBayUid:
|
|
108
|
+
def __init__(self, device:MxrDeviceUid, port_number:int) -> None:
|
|
109
|
+
self._device = device
|
|
110
|
+
self._port = port_number
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def device(self) -> MxrDeviceUid:
|
|
114
|
+
return self._device
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def port(self) -> int:
|
|
118
|
+
return self._port
|
|
119
|
+
|
|
120
|
+
def __hash__(self) -> int:
|
|
121
|
+
return hash(str(self))
|
|
122
|
+
|
|
123
|
+
def __str__(self) -> str:
|
|
124
|
+
return f"{str(self.device)}:{self.port}"
|
|
125
|
+
|
|
126
|
+
def __repr__(self) -> str:
|
|
127
|
+
return str(self)
|
|
128
|
+
|
|
129
|
+
def __eq__(self, value: object) -> bool:
|
|
130
|
+
if isinstance(value, str):
|
|
131
|
+
return (str(self) == value)
|
|
132
|
+
if not isinstance(value, MxrBayUid):
|
|
133
|
+
return False
|
|
134
|
+
return (self.device == value.device) and (self.port == value.port)
|
|
135
|
+
|
|
136
|
+
def __hash__(self) -> int:
|
|
137
|
+
return hash(str(self))
|
mx_remote/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
##################################################
|
|
2
|
+
## MX Remote Python Interface ##
|
|
3
|
+
## ##
|
|
4
|
+
## author: Lars Op den Kamp (lars@opdenkamp.eu) ##
|
|
5
|
+
## copyright (c) 2024 Op den Kamp IT Solutions ##
|
|
6
|
+
##################################################
|
|
7
|
+
|
|
8
|
+
from .remote.Remote import Remote
|
|
9
|
+
from .Interface import *
|
|
10
|
+
from .const import VERSION
|
|
11
|
+
from .proto.Constants import MXR_PROTOCOL_VERSION
|
|
12
|
+
from .main import mxr_console, mxr_main, proto_parser
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
##################################################
|
|
2
|
+
## MX Remote Python Interface ##
|
|
3
|
+
## ##
|
|
4
|
+
## author: Lars Op den Kamp (lars@opdenkamp.eu) ##
|
|
5
|
+
## copyright (c) 2024 Op den Kamp IT Solutions ##
|
|
6
|
+
##################################################
|
|
7
|
+
|
|
8
|
+
class BayConfig:
|
|
9
|
+
''' Bay details from the api '''
|
|
10
|
+
def __init__(self, bay):
|
|
11
|
+
self.bay = bay
|
|
12
|
+
self._data = None
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def name(self) -> str:
|
|
16
|
+
if (self._data is None) or ('Name' not in self._data.keys()):
|
|
17
|
+
return None
|
|
18
|
+
return self._data['Name']
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def bay_type(self) -> str:
|
|
22
|
+
if (self._data is None) or ('Name' not in self._data.keys()):
|
|
23
|
+
return "Unknown"
|
|
24
|
+
return self._data['Type']
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def status(self) -> int:
|
|
28
|
+
if (self._data is None) or ('Status' not in self._data.keys()):
|
|
29
|
+
return -1
|
|
30
|
+
return int(self._data['Status'])
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def hidden(self) -> bool:
|
|
34
|
+
return (self._data is not None) and ('Hidden' in self._data.keys()) and (self._data['Hidden'])
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def is_hdmi(self) -> bool:
|
|
38
|
+
return (self.bay_type[0:4] == 'HDMI')
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def is_hdbt(self) -> bool:
|
|
42
|
+
return (self.bay_type[0:4] == 'HDBT')
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def is_audio(self) -> bool:
|
|
46
|
+
return (self.bay_type[0:5] == 'AUDIO')
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def cec_version(self) -> int:
|
|
50
|
+
if (self._data is None) or ('CEC_version' not in self._data.keys()):
|
|
51
|
+
return None
|
|
52
|
+
return int(self._data['CEC_version'])
|
|
53
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
##################################################
|
|
2
|
+
## MX Remote Python Interface ##
|
|
3
|
+
## ##
|
|
4
|
+
## author: Lars Op den Kamp (lars@opdenkamp.eu) ##
|
|
5
|
+
## copyright (c) 2024 Op den Kamp IT Solutions ##
|
|
6
|
+
##################################################
|
|
7
|
+
|
|
8
|
+
from .BayConfig import BayConfig
|
mx_remote/const.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
##################################################
|
|
2
|
+
## MX Remote Python Interface ##
|
|
3
|
+
## ##
|
|
4
|
+
## author: Lars Op den Kamp (lars@opdenkamp.eu) ##
|
|
5
|
+
## copyright (c) 2024 Op den Kamp IT Solutions ##
|
|
6
|
+
##################################################
|
|
7
|
+
|
|
8
|
+
""" constant definitions for mx_remote """
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
VERSION = '2.0.0'
|
|
12
|
+
__version__ = VERSION
|
|
13
|
+
|
|
14
|
+
MX_BCAST_UDP_IP = '10.8.8.255'
|
|
15
|
+
MX_BCAST_UDP_PORT = 8811
|
|
16
|
+
|
|
17
|
+
MX_MCAST_UDP_IP = '224.8.8.8'
|
|
18
|
+
MX_MCAST_UDP_PORT = 8812
|
|
19
|
+
|
|
20
|
+
BASE_PATH = os.path.dirname(__file__)
|
mx_remote/main.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
##################################################
|
|
2
|
+
## MX Remote Python Interface ##
|
|
3
|
+
## ##
|
|
4
|
+
## author: Lars Op den Kamp (lars@opdenkamp.eu) ##
|
|
5
|
+
## copyright (c) 2024 Op den Kamp IT Solutions ##
|
|
6
|
+
##################################################
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import asyncio
|
|
10
|
+
import logging
|
|
11
|
+
import mx_remote
|
|
12
|
+
import os
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
_LOGGER = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
# import pdb_attach
|
|
18
|
+
# pdb_attach.listen(50000)
|
|
19
|
+
|
|
20
|
+
def proto_parser(logger:logging.Logger, file:str, filter:str|None) -> None:
|
|
21
|
+
"""
|
|
22
|
+
protocol parser entry point
|
|
23
|
+
|
|
24
|
+
Process a file captured by MatrixOS or Wireshark
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
logger (logging.Logger): logger to use to output processed data from the capture file
|
|
28
|
+
file (str): path to the file to process
|
|
29
|
+
filter (str): IP address filter (optional)
|
|
30
|
+
"""
|
|
31
|
+
if os.path.isfile(file):
|
|
32
|
+
raise Exception(f"cannot find '{file}'")
|
|
33
|
+
|
|
34
|
+
with open(file, "r") as f:
|
|
35
|
+
data = f.read().split("\n")
|
|
36
|
+
remote = mx_remote.Remote(open_connection=False)
|
|
37
|
+
logger.debug(f"processing data from {file}")
|
|
38
|
+
for line in data:
|
|
39
|
+
spl = line.split(",")
|
|
40
|
+
if len(spl) == 3:
|
|
41
|
+
ts = spl[0]
|
|
42
|
+
source = spl[1]
|
|
43
|
+
if filter is not None and source != filter:
|
|
44
|
+
continue
|
|
45
|
+
frame = bytes.fromhex(spl[2])
|
|
46
|
+
try:
|
|
47
|
+
logger.debug(f"[[frame {ts}]]")
|
|
48
|
+
remote.on_datagram_received(frame, (source, 8811))
|
|
49
|
+
except Exception as e:
|
|
50
|
+
logger.warning(f"source: {source} frame FAILED: {e}")
|
|
51
|
+
|
|
52
|
+
def mxr_main( extra_args_callback:callable=None,
|
|
53
|
+
log_level_callback:callable=None,
|
|
54
|
+
entry_callback:callable=None,
|
|
55
|
+
callback_param:Any=None) -> None:
|
|
56
|
+
"""
|
|
57
|
+
mx_remote main entry point for stand alone applications
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
extra_args_callback (callable): callback to add extra arguments into the arguments parser
|
|
61
|
+
log_level_callback (callable): callback to set the default logging level
|
|
62
|
+
entry_callback (callable): callback to override the main entry point of the application
|
|
63
|
+
callback_param (Any): parameter passed to callbacks
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
# command line arguments
|
|
67
|
+
argparser = argparse.ArgumentParser(description="MX Remote Manager / Debugger")
|
|
68
|
+
argparser.add_argument("-i", dest='input', help='capture file to process', required=False)
|
|
69
|
+
argparser.add_argument("-f", dest='filter', help='ip address to process in the capture file', required=False)
|
|
70
|
+
argparser.add_argument("-o", dest='output', help='write output to a file', required=False)
|
|
71
|
+
argparser.add_argument("-l", dest='local_ip', help='local ip address of the network interface to use', required=False)
|
|
72
|
+
argparser.add_argument("-b", dest='broadcast', help='use broadcast mode instead of multicast mode', required=False, type=bool)
|
|
73
|
+
if (extra_args_callback is not None):
|
|
74
|
+
extra_args_callback(callback_param, argparser)
|
|
75
|
+
args = argparser.parse_args()
|
|
76
|
+
|
|
77
|
+
if (log_level_callback is not None):
|
|
78
|
+
default_level = log_level_callback(callback_param, args)
|
|
79
|
+
else:
|
|
80
|
+
default_level = logging.DEBUG
|
|
81
|
+
|
|
82
|
+
# log output
|
|
83
|
+
if (args.output is None):
|
|
84
|
+
# console
|
|
85
|
+
logging.basicConfig(level=default_level, format='%(asctime)s [%(levelname)s] %(message)s', force=True)
|
|
86
|
+
else:
|
|
87
|
+
# file
|
|
88
|
+
logging.basicConfig(
|
|
89
|
+
level=default_level,
|
|
90
|
+
format="%(asctime)s - %(message)s",
|
|
91
|
+
datefmt='%d-%b-%y %H:%M:%S',
|
|
92
|
+
force=True,
|
|
93
|
+
handlers=[
|
|
94
|
+
logging.FileHandler(args.output),
|
|
95
|
+
logging.StreamHandler()
|
|
96
|
+
])
|
|
97
|
+
|
|
98
|
+
if (args.input is not None):
|
|
99
|
+
proto_parser(_LOGGER, args.input, args.filter)
|
|
100
|
+
else:
|
|
101
|
+
if (entry_callback is not None) and entry_callback(callback_param, args):
|
|
102
|
+
# handled externally
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
# run the console app
|
|
106
|
+
loop = asyncio.get_event_loop()
|
|
107
|
+
try:
|
|
108
|
+
mx = mx_remote.Remote(local_ip=args.local_ip, broadcast=(args.broadcast is not None and args.broadcast))
|
|
109
|
+
loop.run_until_complete(mx.start_async())
|
|
110
|
+
loop.run_forever()
|
|
111
|
+
except KeyboardInterrupt:
|
|
112
|
+
pass
|
|
113
|
+
loop.run_until_complete(mx.close())
|
|
114
|
+
loop.close()
|
|
115
|
+
|
|
116
|
+
def mxr_console() -> None:
|
|
117
|
+
mxr_main()
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
##################################################
|
|
2
|
+
## MX Remote Python Interface ##
|
|
3
|
+
## ##
|
|
4
|
+
## author: Lars Op den Kamp (lars@opdenkamp.eu) ##
|
|
5
|
+
## copyright (c) 2024 Op den Kamp IT Solutions ##
|
|
6
|
+
##################################################
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
from .Constants import BayStatusMask
|
|
10
|
+
import struct
|
|
11
|
+
|
|
12
|
+
class BayConfig:
|
|
13
|
+
''' Bay configuration for a remote device '''
|
|
14
|
+
def __init__(self, payload:bytes):
|
|
15
|
+
self.payload = payload
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def port(self) -> int:
|
|
19
|
+
# port number
|
|
20
|
+
return int(self.payload[0])
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def modenum(self) -> int:
|
|
24
|
+
# port mode number
|
|
25
|
+
return int(self.payload[1])
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def mode(self) -> str:
|
|
29
|
+
# port mode
|
|
30
|
+
nb = self.modenum
|
|
31
|
+
if nb == 0:
|
|
32
|
+
return 'Input'
|
|
33
|
+
if nb == 1:
|
|
34
|
+
return 'Output'
|
|
35
|
+
return 'Unknown'
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def is_input(self) -> bool:
|
|
39
|
+
# input bay
|
|
40
|
+
return self.modenum == 0
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def is_output(self) -> bool:
|
|
44
|
+
# output bay
|
|
45
|
+
return self.modenum == 1
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def bay(self) -> int:
|
|
49
|
+
# bay number
|
|
50
|
+
return int(self.payload[2])
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def video_source(self) -> int:
|
|
54
|
+
# video source bay number
|
|
55
|
+
return int(self.payload[3])
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def edid_profile(self) -> int:
|
|
59
|
+
return ((int(self.payload[4]) & 0xF) << 8) | int(self.payload[3])
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def rc_type(self) -> int:
|
|
63
|
+
return ((int(self.payload[4]) > 4) & 0xF)
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def audio_source(self) -> int:
|
|
67
|
+
# audio source bay number
|
|
68
|
+
return int(self.payload[4])
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def bay_name(self) -> str:
|
|
72
|
+
# bay name
|
|
73
|
+
return self.payload[5:21].split(b'\0',1)[0].decode('ascii')
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def user_name(self) -> str:
|
|
77
|
+
# user set name
|
|
78
|
+
return self.payload[21:37].split(b'\0',1)[0].decode('ascii')
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def signal_type(self) -> str:
|
|
82
|
+
# video signal type
|
|
83
|
+
return self.payload[37:53].split(b'\0',1)[0].decode('ascii')
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def status(self) -> BayStatusMask:
|
|
87
|
+
# bay status
|
|
88
|
+
return BayStatusMask(struct.unpack('<L', self.payload[53:57])[0])
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def features(self) -> int:
|
|
92
|
+
# features mask
|
|
93
|
+
return struct.unpack('<L', self.payload[57:61])[0]
|
|
94
|
+
|
|
95
|
+
def __str__(self) -> str:
|
|
96
|
+
return f"{self.mode} {self.bay + 1} (port {self.port}): {self.user_name} - {self.signal_type}"
|
|
97
|
+
|
|
98
|
+
def bay_match(self, other: BayConfig) -> bool:
|
|
99
|
+
# check whether other is a configuration for the same bay as this one
|
|
100
|
+
return (self.port == other.port) and \
|
|
101
|
+
(self.modenum == other.modenum) and \
|
|
102
|
+
(self.bay == other.bay) and \
|
|
103
|
+
(self.features == other.features)
|
|
104
|
+
|