hdmimatrix 0.0.1__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mark Lynch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: hdmimatrix
3
+ Version: 0.0.1
4
+ Summary: Python HDMI Matrix control library for AVGear and possibly other HDMI matrix controllers
5
+ Author-email: Mark Lynch <marklynch@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/marklynch/hdmimatrix
8
+ Project-URL: Issues, https://github.com/marklynch/hdmimatrix/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Dynamic: license-file
15
+
16
+ Python library to control an AVGear HDMI Matrix - specifically a TMX44PRO AVK as but it may work for others and happy to accept pull requests or suggestions for other.
17
+
18
+ Inspiration from:
19
+ https://github.com/koolsb/pyblackbird/
@@ -0,0 +1,4 @@
1
+ Python library to control an AVGear HDMI Matrix - specifically a TMX44PRO AVK as but it may work for others and happy to accept pull requests or suggestions for other.
2
+
3
+ Inspiration from:
4
+ https://github.com/koolsb/pyblackbird/
@@ -0,0 +1 @@
1
+ from .hdmimatrix import HDMIMatrix
@@ -0,0 +1,221 @@
1
+ import socket
2
+ import logging
3
+ import time
4
+ from enum import Enum
5
+
6
+
7
+ SOCKET_RECV_BUFFER = 2048 # size of socket recieve buffer
8
+ SOCKET_TIMEOUT = 5.0
9
+ SOCKET_END_OF_DATA_TIMEOUT = 0.5 # if no data recieved assume end of message
10
+ SOCKET_RECEIVE_DELAY = 0.05 # delay between recieves
11
+
12
+ class Commands(Enum):
13
+ POWERON = "PowerON."
14
+ POWEROFF = "PowerOFF."
15
+ NAME = "/*Name."
16
+ TYPE = "/*Type."
17
+ VERSION = "/^Version."
18
+ STATUS = "STA."
19
+
20
+ class HDMIMatrix:
21
+ """Controller for AVGear (and possibly other) HDMI Matrix switches"""
22
+
23
+ def __init__(self, host: str = "192.168.0.178", port: int = 4001,
24
+ logger: logging.Logger = None):
25
+ """
26
+ Initialize the matrix switch controller
27
+
28
+ Args:
29
+ host: IP address for TCP connection (default 192.168.0.178)
30
+ port: TCP port (default 4001)
31
+ """
32
+ self.host = host
33
+ self.port = port
34
+ self.connection = None
35
+
36
+ # TODO - make this be configurable based on the matrix type
37
+ # eg 4x4 or 8x8
38
+ self._input_count = 4
39
+ self._output_count = 4
40
+
41
+ # Initialise logging if logger is not passed in.
42
+ if logger is None:
43
+ self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
44
+ self.logger.setLevel('DEBUG')
45
+
46
+ # Create formatter
47
+ formatter = logging.Formatter(
48
+ '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
49
+ datefmt='%Y-%m-%d %H:%M:%S'
50
+ )
51
+ # Console handler
52
+ console_handler = logging.StreamHandler()
53
+ console_handler.setFormatter(formatter)
54
+ self.logger.addHandler(console_handler)
55
+ else:
56
+ self.logger = logger
57
+
58
+ @property
59
+ def input_count(self):
60
+ return self._input_count
61
+
62
+ @input_count.setter
63
+ def input_count(self, value:int):
64
+ raise RuntimeError(f"input_count is read-only — attempted to set it to {value}")
65
+
66
+ @property
67
+ def output_count(self):
68
+ return self._output_count
69
+ @output_count.setter
70
+ def output_count(self, value:int):
71
+ raise RuntimeError(f"output_count is read-only — attempted to set it to {value}")
72
+
73
+
74
+ # Connection methods
75
+ def connect(self) -> bool:
76
+ """Establish TCP/IP connection to the matrix switch"""
77
+ try:
78
+ self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
79
+ self.connection.settimeout(SOCKET_TIMEOUT)
80
+ self.connection.connect((self.host, self.port))
81
+ self.logger.info(f"Connected to {self.host}:{self.port}")
82
+
83
+ # Read any data the welcome data to clear the buffer
84
+ data = self.connection.recv(SOCKET_RECV_BUFFER)
85
+ self.logger.debug(f"Discarding: {data}")
86
+
87
+ return True
88
+
89
+ except Exception as e:
90
+ self.logger.error(f"Connection failed: {e}")
91
+ return False
92
+
93
+ def disconnect(self):
94
+ """Close the connection"""
95
+ if self.connection:
96
+ self.connection.close()
97
+ self.connection = None
98
+ self.logger.info("Disconnected")
99
+
100
+
101
+ # Information Methods
102
+
103
+ def get_device_name(self):
104
+ return self._process_request(Commands.NAME.value.encode('ascii'))
105
+
106
+ def get_device_status(self):
107
+ return self._process_request(Commands.STATUS.value.encode('ascii'))
108
+
109
+ def get_device_type(self):
110
+ return self._process_request(Commands.TYPE.value.encode('ascii'))
111
+
112
+ def get_device_version(self):
113
+ return self._process_request(Commands.VERSION.value.encode('ascii'))
114
+
115
+ # Command Methods
116
+
117
+ def power_off(self):
118
+ """
119
+ Power off the HDMI matrix
120
+ """
121
+ return self._process_request(Commands.POWEROFF.value.encode('ascii'))
122
+
123
+ def power_on(self):
124
+ """
125
+ Power On the HDMI matrix
126
+ """
127
+ return self._process_request(Commands.POWERON.value.encode('ascii'))
128
+
129
+ def route_input_to_output(self, input:int, output:int):
130
+ """
131
+ Select HDMI input to route to HDMI output
132
+ """
133
+ if not 0 <= input <= self.input_count:
134
+ raise ValueError(f"Input must be between 1 and {self.input_count}")
135
+
136
+ if not 0 <= output <= self.output_count:
137
+ raise ValueError(f"Output must be between 1 and {self.output_count}")
138
+
139
+ return self._process_request(f"OUT{output:02d}:{input:02d}.".encode('ascii'))
140
+
141
+
142
+ # Internal methods
143
+ def _process_request(self, request: bytes):
144
+
145
+ # Send the command
146
+ self.connection.send(request)
147
+ self.logger.debug(f'Send Command: {request}')
148
+
149
+ # Read all the responses back
150
+ response = self._read_response()
151
+
152
+ # self.logger.debug(response)
153
+ return response
154
+
155
+
156
+ def _read_response(self, timeout: float = 2.0) -> str:
157
+ """
158
+ Read all available response data from the device - this uses a timeout
159
+ based method as there is no protocol format and output can be multiple
160
+ lines.
161
+
162
+ Args:
163
+ timeout: Total timeout in seconds
164
+
165
+ Returns:
166
+ str: Complete response string or empty string if no response
167
+ """
168
+
169
+ if not self.connection:
170
+ return ""
171
+
172
+ try:
173
+ # Set socket to non-blocking mode temporarily
174
+ original_timeout = self.connection.gettimeout()
175
+ self.connection.settimeout(0.1) # Short timeout for individual reads
176
+
177
+ response_parts = []
178
+ start_time = time.time()
179
+ last_data_time = start_time
180
+
181
+ while (time.time() - start_time) < timeout:
182
+ try:
183
+ # Try to read data
184
+ data = self.connection.recv(SOCKET_RECV_BUFFER)
185
+ if data:
186
+ response_parts.append(data.decode('ascii', errors='ignore'))
187
+ last_data_time = time.time()
188
+ self.logger.debug(f"Received data chunk: {repr(data)}")
189
+ else:
190
+ # No data received, check if we should continue waiting
191
+ if response_parts and (time.time() - last_data_time) > SOCKET_END_OF_DATA_TIMEOUT:
192
+ # We got some data but nothing new for 0.5 seconds
193
+ break
194
+ time.sleep(SOCKET_RECEIVE_DELAY) # Small delay before next attempt
195
+
196
+ except socket.timeout:
197
+ # No data available right now
198
+ if response_parts and (time.time() - last_data_time) > SOCKET_END_OF_DATA_TIMEOUT:
199
+ # We got some data but nothing new for 0.5 seconds
200
+ break
201
+ time.sleep(SOCKET_RECEIVE_DELAY) # Small delay before next attempt
202
+ continue
203
+
204
+ except Exception as e:
205
+ self.logger.error(f"Error during read: {e}")
206
+ break
207
+
208
+ # Restore original timeout
209
+ self.connection.settimeout(original_timeout)
210
+
211
+ complete_response = ''.join(response_parts).strip()
212
+ if complete_response:
213
+ self.logger.debug(f"Complete response: {repr(complete_response)}")
214
+ return complete_response
215
+ else:
216
+ self.logger.debug("No response received")
217
+ return ""
218
+
219
+ except Exception as e:
220
+ self.logger.error(f"Error reading response: {e}")
221
+ return ""
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: hdmimatrix
3
+ Version: 0.0.1
4
+ Summary: Python HDMI Matrix control library for AVGear and possibly other HDMI matrix controllers
5
+ Author-email: Mark Lynch <marklynch@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/marklynch/hdmimatrix
8
+ Project-URL: Issues, https://github.com/marklynch/hdmimatrix/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Dynamic: license-file
15
+
16
+ Python library to control an AVGear HDMI Matrix - specifically a TMX44PRO AVK as but it may work for others and happy to accept pull requests or suggestions for other.
17
+
18
+ Inspiration from:
19
+ https://github.com/koolsb/pyblackbird/
@@ -0,0 +1,9 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ hdmimatrix/__init__.py
5
+ hdmimatrix/hdmimatrix.py
6
+ hdmimatrix.egg-info/PKG-INFO
7
+ hdmimatrix.egg-info/SOURCES.txt
8
+ hdmimatrix.egg-info/dependency_links.txt
9
+ hdmimatrix.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ hdmimatrix
@@ -0,0 +1,19 @@
1
+ [project]
2
+ name = "hdmimatrix"
3
+ version = "0.0.1"
4
+ authors = [
5
+ { name="Mark Lynch", email="marklynch@gmail.com" },
6
+ ]
7
+ description = "Python HDMI Matrix control library for AVGear and possibly other HDMI matrix controllers"
8
+ readme = "README.md"
9
+ requires-python = ">=3.9"
10
+ classifiers = [
11
+ "Programming Language :: Python :: 3",
12
+ "Operating System :: OS Independent",
13
+ ]
14
+ license = "MIT"
15
+ license-files = ["LICEN[CS]E*"]
16
+
17
+ [project.urls]
18
+ Homepage = "https://github.com/marklynch/hdmimatrix"
19
+ Issues = "https://github.com/marklynch/hdmimatrix/issues"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+