From 020d5ce1d2a7de6bc133e243d92ead610d5a7ae9 Mon Sep 17 00:00:00 2001 From: Solmath <33658856+Solmath@users.noreply.github.com> Date: Thu, 21 Aug 2025 20:48:21 +0200 Subject: [PATCH 01/10] =?UTF-8?q?feat(logging):=20=F0=9F=93=9C=20Enhance?= =?UTF-8?q?=20logging=20and=20error=20handling=20in=20EQ=20file=20parsing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added logging configuration to capture detailed information. * Improved error handling for file operations and EQ band parsing. * Updated main application logic to log parsing steps and results. * Enhanced user feedback for EQ settings application process. --- rew2streammagic/main.py | 210 +++++++++++++++++++++++++++++++--------- 1 file changed, 164 insertions(+), 46 deletions(-) diff --git a/rew2streammagic/main.py b/rew2streammagic/main.py index af6aacd..dc05eff 100644 --- a/rew2streammagic/main.py +++ b/rew2streammagic/main.py @@ -6,13 +6,27 @@ import sys import re import asyncio +import logging from pathlib import Path -from aiohttp import ClientSession +from aiohttp import ( + ClientSession, + ClientConnectorError, + ClientError, + ClientTimeout, + ServerTimeoutError, +) from aiostreammagic import StreamMagicClient, EQBand, UserEQ, EQFilterType, Info from packaging.version import Version +# Configure logging +logging.basicConfig( + level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" +) +logger = logging.getLogger(__name__) + def parse_eq_file(file_path): + """Parse EQ file and return UserEQ object with band settings.""" bands = [] filter_map = { "LS": "LOWSHELF", @@ -24,66 +38,170 @@ def parse_eq_file(file_path): band_pattern = re.compile( r"^Filter\s+(\d+):\s+ON\s+([A-Z]+)\s+Fc\s+([\d.]+)\s*Hz(?:\s+Gain\s+([\-\d.]+)\s*dB)?(?:\s+Q\s+([\d.]+))?" ) - with open(file_path, "r", encoding="utf-8") as f: - for line in f: - match = band_pattern.match(line.strip()) - if match: - band_num = int(match.group(1)) - 1 # zero-based index - filter_type = match.group(2) - freq = float(match.group(3)) - gain = float(match.group(4)) if match.group(4) is not None else None - q = float(match.group(5)) if match.group(5) is not None else None - mapped_filter = filter_map.get(filter_type, filter_type) - band = EQBand( - index=band_num, - filter=EQFilterType[mapped_filter], - freq=int(freq), - gain=gain, - q=q, - ) - bands.append(band) - if len(bands) == 7: - break + + try: + with open(file_path, "r", encoding="utf-8") as f: + for line in f: + match = band_pattern.match(line.strip()) + if match: + band_num = int(match.group(1)) - 1 # zero-based index + filter_type = match.group(2) + freq = float(match.group(3)) + gain = float(match.group(4)) if match.group(4) is not None else None + q = float(match.group(5)) if match.group(5) is not None else None + mapped_filter = filter_map.get(filter_type, filter_type) + + try: + band = EQBand( + index=band_num, + filter=EQFilterType[mapped_filter], + freq=int(freq), + gain=gain, + q=q, + ) + bands.append(band) + if len(bands) == 7: + break + except (KeyError, ValueError) as e: + logger.warning(f"Skipping invalid band {band_num}: {e}") + continue + except FileNotFoundError: + logger.error(f"File not found: {file_path}") + raise + except IOError as e: + logger.error(f"Error reading file {file_path}: {e}") + raise + except Exception as e: + logger.error(f"Unexpected error parsing file {file_path}: {e}") + raise + return UserEQ(bands=bands) +async def connect_and_apply_eq(host, user_eq, timeout=10): + """Connect to StreamMagic device and apply EQ settings.""" + try: + # Create session with timeout + timeout_config = ClientTimeout(total=timeout) + async with ClientSession(timeout=timeout_config) as session: + logger.info(f"Attempting to connect to {host}") + client = StreamMagicClient(host, session=session) + + try: + await client.connect() + logger.info(f"Successfully connected to {host}") + + # Get device info + try: + info: Info = await client.get_info() + logger.info(f"Device API version: {info.api_version}") + + if Version(info.api_version) >= Version("1.9"): + logger.info("Applying EQ settings...") + # Example of setting equalizer band gain and frequency + # await client.set_equalizer_band_gain(0, 3.0) + # await client.set_equalizer_band_frequency(0, 100) + await client.set_equalizer_params(user_eq) + logger.info("EQ settings applied successfully") + else: + logger.warning( + f"API version {info.api_version} is too old. Minimum required: 1.9" + ) + return False + + except Exception as e: + logger.error(f"Error applying EQ settings: {e}") + return False + + finally: + try: + await client.disconnect() + logger.info("Disconnected from device") + except Exception as e: + logger.warning(f"Error during disconnect: {e}") + + return True + + except ClientConnectorError as e: + logger.error(f"Connection failed - device not reachable at {host}: {e}") + return False + except ServerTimeoutError as e: + logger.error(f"Connection timed out to {host}: {e}") + return False + except ClientError as e: + logger.error(f"HTTP client error connecting to {host}: {e}") + return False + except asyncio.TimeoutError: + logger.error(f"Operation timed out connecting to {host}") + return False + except OSError as e: + logger.error(f"Network error connecting to {host}: {e}") + return False + except Exception as e: + logger.error(f"Unexpected error connecting to {host}: {e}") + return False + + async def main(): + """Main application logic.""" if len(sys.argv) < 2: - print("Usage: python -m rew2streammagic.main ") + print("Usage: python -m rew2streammagic.main [host_ip]") + print("Default host: 192.168.1.29") sys.exit(1) + eq_file = Path(sys.argv[1]) + host = sys.argv[2] if len(sys.argv) > 2 else "192.168.1.29" + if not eq_file.exists(): - print(f"File not found: {eq_file}") - sys.exit(1) - user_eq = parse_eq_file(eq_file) - if not user_eq.bands: - print("No equalizer bands found in the file.") + logger.error(f"File not found: {eq_file}") sys.exit(1) - print("First 7 Equalizer Bands:") - for band in user_eq.bands: - print(f"Band {band.index}: Freq={band.freq}Hz, Gain={band.gain}dB, Q={band.q}") - async with ClientSession() as session: - client = StreamMagicClient("192.168.1.29", session=session) - await client.connect() - info: Info = await client.get_info() + try: + # Parse EQ file + logger.info(f"Parsing EQ file: {eq_file}") + user_eq = parse_eq_file(eq_file) - print(f"API: {info.api_version}") - if Version(info.api_version) >= Version("1.9"): - # Example of setting equalizer band gain and frequency - # await client.set_equalizer_band_gain(0, 3.0) - # await client.set_equalizer_band_frequency(0, 100) - await client.set_equalizer_params(user_eq) + if not user_eq.bands: + logger.error("No equalizer bands found in the file.") + sys.exit(1) - await client.disconnect() + # Display parsed bands + print("First 7 Equalizer Bands:") + for band in user_eq.bands: + print( + f"Band {band.index + 1}: Freq={band.freq}Hz, Gain={band.gain}dB, Q={band.q}" + ) + + # Connect and apply EQ + success = await connect_and_apply_eq(host, user_eq) + + if success: + print(f"✅ EQ settings successfully applied to device at {host}") + return 0 + else: + print(f"❌ Failed to apply EQ settings to device at {host}") + return 1 + + except KeyboardInterrupt: + logger.info("Operation cancelled by user") + return 1 + except Exception as e: + logger.error(f"Unexpected error in main: {e}") + return 1 def cli(): - """ - Command line interface for the script. - """ - asyncio.run(main()) + """Command line interface for the script.""" + try: + exit_code = asyncio.run(main()) + sys.exit(exit_code) + except KeyboardInterrupt: + print("\nOperation cancelled by user") + sys.exit(1) + except Exception as e: + print(f"Fatal error: {e}") + sys.exit(1) if __name__ == "__main__": - asyncio.run(main()) + cli() From 828a26e145392857e42b8d5585d581f0dfdb8d62 Mon Sep 17 00:00:00 2001 From: Solmath <33658856+Solmath@users.noreply.github.com> Date: Thu, 21 Aug 2025 20:58:39 +0200 Subject: [PATCH 02/10] =?UTF-8?q?fix(logging):=20=F0=9F=94=A7=20Improve=20?= =?UTF-8?q?error=20handling=20and=20logging=20in=20`connect=5Fand=5Fapply?= =?UTF-8?q?=5Feq`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added specific error logging for connection timeouts and client connection errors. * Adjusted timeout parameter from 10 to 5 seconds for better responsiveness. * Enhanced logging suppression for `aiohttp` and `aiostreammagic` to reduce noise. --- rew2streammagic/main.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/rew2streammagic/main.py b/rew2streammagic/main.py index dc05eff..ef324a2 100644 --- a/rew2streammagic/main.py +++ b/rew2streammagic/main.py @@ -13,7 +13,6 @@ ClientConnectorError, ClientError, ClientTimeout, - ServerTimeoutError, ) from aiostreammagic import StreamMagicClient, EQBand, UserEQ, EQFilterType, Info from packaging.version import Version @@ -24,6 +23,10 @@ ) logger = logging.getLogger(__name__) +# Suppress aiohttp and aiostreammagic debug logs to reduce noise +logging.getLogger("aiohttp").setLevel(logging.WARNING) +logging.getLogger("aiostreammagic").setLevel(logging.WARNING) + def parse_eq_file(file_path): """Parse EQ file and return UserEQ object with band settings.""" @@ -78,7 +81,7 @@ def parse_eq_file(file_path): return UserEQ(bands=bands) -async def connect_and_apply_eq(host, user_eq, timeout=10): +async def connect_and_apply_eq(host, user_eq, timeout=5): """Connect to StreamMagic device and apply EQ settings.""" try: # Create session with timeout @@ -113,6 +116,16 @@ async def connect_and_apply_eq(host, user_eq, timeout=10): logger.error(f"Error applying EQ settings: {e}") return False + except (TimeoutError, asyncio.TimeoutError): + logger.error(f"Connection timed out to {host}") + return False + except ClientConnectorError: + logger.error(f"Connection failed - device not reachable at {host}") + return False + except Exception as e: + logger.error(f"Error connecting to device: {e}") + return False + finally: try: await client.disconnect() @@ -122,18 +135,18 @@ async def connect_and_apply_eq(host, user_eq, timeout=10): return True - except ClientConnectorError as e: - logger.error(f"Connection failed - device not reachable at {host}: {e}") + except (TimeoutError, asyncio.TimeoutError): + logger.error(f"Connection timed out to {host}") return False - except ServerTimeoutError as e: - logger.error(f"Connection timed out to {host}: {e}") + except ClientConnectorError: + logger.error(f"Connection failed - device not reachable at {host}") return False + # except ServerTimeoutError: + # logger.error(f"Server timeout connecting to {host}") + # return False except ClientError as e: logger.error(f"HTTP client error connecting to {host}: {e}") return False - except asyncio.TimeoutError: - logger.error(f"Operation timed out connecting to {host}") - return False except OSError as e: logger.error(f"Network error connecting to {host}: {e}") return False From 7cee650973c790644db18d3934041a911d556424 Mon Sep 17 00:00:00 2001 From: Solmath <33658856+Solmath@users.noreply.github.com> Date: Thu, 21 Aug 2025 21:05:28 +0200 Subject: [PATCH 03/10] =?UTF-8?q?fix(logging):=20=F0=9F=94=A7=20Improve=20?= =?UTF-8?q?logging=20levels=20to=20reduce=20noise?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Changed logging levels for `aiohttp`, `aiostreammagic`, and `asyncio` to `CRITICAL` to suppress unnecessary debug logs. - Added `ServerTimeoutError` import for potential future error handling in `connect_and_apply_eq`. --- rew2streammagic/main.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/rew2streammagic/main.py b/rew2streammagic/main.py index ef324a2..18a6d27 100644 --- a/rew2streammagic/main.py +++ b/rew2streammagic/main.py @@ -23,9 +23,10 @@ ) logger = logging.getLogger(__name__) -# Suppress aiohttp and aiostreammagic debug logs to reduce noise -logging.getLogger("aiohttp").setLevel(logging.WARNING) -logging.getLogger("aiostreammagic").setLevel(logging.WARNING) +# Suppress all logs from libraries that spam tracebacks +logging.getLogger("aiohttp").setLevel(logging.CRITICAL) +logging.getLogger("aiostreammagic").setLevel(logging.CRITICAL) +logging.getLogger("asyncio").setLevel(logging.CRITICAL) def parse_eq_file(file_path): @@ -141,9 +142,6 @@ async def connect_and_apply_eq(host, user_eq, timeout=5): except ClientConnectorError: logger.error(f"Connection failed - device not reachable at {host}") return False - # except ServerTimeoutError: - # logger.error(f"Server timeout connecting to {host}") - # return False except ClientError as e: logger.error(f"HTTP client error connecting to {host}: {e}") return False From 5ef8b2b3fdcdd8054cad940821d2888f8f10307c Mon Sep 17 00:00:00 2001 From: Solmath <33658856+Solmath@users.noreply.github.com> Date: Thu, 21 Aug 2025 21:15:56 +0200 Subject: [PATCH 04/10] =?UTF-8?q?fix(connect=5Fand=5Fapply=5Feq):=20?= =?UTF-8?q?=F0=9F=94=A7=20Refactor=20connection=20logic=20for=20improved?= =?UTF-8?q?=20clarity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Simplified the connection and EQ application process in `connect_and_apply_eq`. * Removed nested try-except blocks for better readability. * Enhanced logging for connection status and device information. --- rew2streammagic/main.py | 59 +++++++++++++---------------------------- 1 file changed, 18 insertions(+), 41 deletions(-) diff --git a/rew2streammagic/main.py b/rew2streammagic/main.py index 18a6d27..e042698 100644 --- a/rew2streammagic/main.py +++ b/rew2streammagic/main.py @@ -91,48 +91,25 @@ async def connect_and_apply_eq(host, user_eq, timeout=5): logger.info(f"Attempting to connect to {host}") client = StreamMagicClient(host, session=session) - try: - await client.connect() - logger.info(f"Successfully connected to {host}") - - # Get device info - try: - info: Info = await client.get_info() - logger.info(f"Device API version: {info.api_version}") - - if Version(info.api_version) >= Version("1.9"): - logger.info("Applying EQ settings...") - # Example of setting equalizer band gain and frequency - # await client.set_equalizer_band_gain(0, 3.0) - # await client.set_equalizer_band_frequency(0, 100) - await client.set_equalizer_params(user_eq) - logger.info("EQ settings applied successfully") - else: - logger.warning( - f"API version {info.api_version} is too old. Minimum required: 1.9" - ) - return False - - except Exception as e: - logger.error(f"Error applying EQ settings: {e}") - return False - - except (TimeoutError, asyncio.TimeoutError): - logger.error(f"Connection timed out to {host}") + await client.connect() + logger.info(f"Successfully connected to {host}") + + # Get device info + info: Info = await client.get_info() + logger.info(f"Device API version: {info.api_version}") + + if Version(info.api_version) >= Version("1.9"): + logger.info("Applying EQ settings...") + # Example of setting equalizer band gain and frequency + # await client.set_equalizer_band_gain(0, 3.0) + # await client.set_equalizer_band_frequency(0, 100) + await client.set_equalizer_params(user_eq) + logger.info("EQ settings applied successfully") + else: + logger.warning( + f"API version {info.api_version} is too old. Minimum required: 1.9" + ) return False - except ClientConnectorError: - logger.error(f"Connection failed - device not reachable at {host}") - return False - except Exception as e: - logger.error(f"Error connecting to device: {e}") - return False - - finally: - try: - await client.disconnect() - logger.info("Disconnected from device") - except Exception as e: - logger.warning(f"Error during disconnect: {e}") return True From 0ce528b8cc86c1753d4ec61771729b158b90c926 Mon Sep 17 00:00:00 2001 From: Solmath <33658856+Solmath@users.noreply.github.com> Date: Thu, 21 Aug 2025 21:24:12 +0200 Subject: [PATCH 05/10] =?UTF-8?q?fix(connect=5Fand=5Fapply=5Feq):=20?= =?UTF-8?q?=F0=9F=94=A7=20Add=20disconnection=20logging=20after=20applying?= =?UTF-8?q?=20EQ=20settings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Ensure proper disconnection from the StreamMagic device. * Log the disconnection event for better traceability. --- rew2streammagic/main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rew2streammagic/main.py b/rew2streammagic/main.py index e042698..60e7f6e 100644 --- a/rew2streammagic/main.py +++ b/rew2streammagic/main.py @@ -111,6 +111,9 @@ async def connect_and_apply_eq(host, user_eq, timeout=5): ) return False + await client.disconnect() + logger.info(f"Disconnected from {host}") + return True except (TimeoutError, asyncio.TimeoutError): From affb3f907ee68b79f9bbb224571e763ad7e69f5a Mon Sep 17 00:00:00 2001 From: Solmath <33658856+Solmath@users.noreply.github.com> Date: Thu, 21 Aug 2025 21:25:04 +0200 Subject: [PATCH 06/10] =?UTF-8?q?fix(project):=20=F0=9F=94=A7=20Update=20v?= =?UTF-8?q?ersion=20to=200.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Incremented the version number in `pyproject.toml` to reflect the latest changes. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 776330e..18e0e07 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "rew2streammagic" -version = "0.1.0" +version = "0.2.0" description = "Apply REW equalizer profiles to StreamMagic device" authors = [ {name = "Solmath",email = "33658856+Solmath@users.noreply.github.com"} From 33898e88f18406145b11bda5e982bfdedc149d8d Mon Sep 17 00:00:00 2001 From: Solmath <33658856+Solmath@users.noreply.github.com> Date: Thu, 21 Aug 2025 21:40:58 +0200 Subject: [PATCH 07/10] =?UTF-8?q?fix(main):=20=F0=9F=94=A7=20Refactor=20`m?= =?UTF-8?q?ain`=20function=20for=20improved=20argument=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updated `main` function to accept parameters directly instead of relying on `sys.argv`. * Enhanced error handling by returning error codes instead of using `sys.exit`. * Improved clarity and maintainability of the code. --- rew2streammagic/main.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/rew2streammagic/main.py b/rew2streammagic/main.py index 60e7f6e..beaddde 100644 --- a/rew2streammagic/main.py +++ b/rew2streammagic/main.py @@ -133,19 +133,14 @@ async def connect_and_apply_eq(host, user_eq, timeout=5): return False -async def main(): +async def main(eq_file_path, host, timeout=5): """Main application logic.""" - if len(sys.argv) < 2: - print("Usage: python -m rew2streammagic.main [host_ip]") - print("Default host: 192.168.1.29") - sys.exit(1) - eq_file = Path(sys.argv[1]) - host = sys.argv[2] if len(sys.argv) > 2 else "192.168.1.29" + eq_file = Path(eq_file_path) if not eq_file.exists(): logger.error(f"File not found: {eq_file}") - sys.exit(1) + return 1 try: # Parse EQ file @@ -154,7 +149,7 @@ async def main(): if not user_eq.bands: logger.error("No equalizer bands found in the file.") - sys.exit(1) + return 1 # Display parsed bands print("First 7 Equalizer Bands:") @@ -183,8 +178,16 @@ async def main(): def cli(): """Command line interface for the script.""" + if len(sys.argv) < 2: + print("Usage: python -m rew2streammagic.main [host_ip]") + print("Default host: 192.168.1.29") + sys.exit(1) + + eq_file = sys.argv[1] + host = sys.argv[2] if len(sys.argv) > 2 else "192.168.1.29" + try: - exit_code = asyncio.run(main()) + exit_code = asyncio.run(main(eq_file, host)) sys.exit(exit_code) except KeyboardInterrupt: print("\nOperation cancelled by user") From 32c04c5fb748a591383e78738d1fe86bc9f581c2 Mon Sep 17 00:00:00 2001 From: Solmath <33658856+Solmath@users.noreply.github.com> Date: Thu, 21 Aug 2025 21:52:52 +0200 Subject: [PATCH 08/10] =?UTF-8?q?feat(cli):=20=E2=9C=A8=20Add=20command=20?= =?UTF-8?q?line=20arguments=20for=20EQ=20file=20processing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Introduced `argparse` for improved command line interface. - Added options for `--host`, `--timeout`, and `--dry-run`. - Enhanced user experience with usage examples and default values. --- rew2streammagic/main.py | 55 ++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/rew2streammagic/main.py b/rew2streammagic/main.py index beaddde..3db6460 100644 --- a/rew2streammagic/main.py +++ b/rew2streammagic/main.py @@ -7,6 +7,7 @@ import re import asyncio import logging +import argparse from pathlib import Path from aiohttp import ( ClientSession, @@ -133,9 +134,12 @@ async def connect_and_apply_eq(host, user_eq, timeout=5): return False -async def main(eq_file_path, host, timeout=5): +async def main(eq_file_path, host, timeout=5, dry_run=False): """Main application logic.""" + if dry_run: + print("🔍 DRY RUN: Device connection will be skipped.") + eq_file = Path(eq_file_path) if not eq_file.exists(): @@ -158,8 +162,12 @@ async def main(eq_file_path, host, timeout=5): f"Band {band.index + 1}: Freq={band.freq}Hz, Gain={band.gain}dB, Q={band.q}" ) + if dry_run: + print("🔍 DRY RUN: EQ file parsed successfully.") + return 0 + # Connect and apply EQ - success = await connect_and_apply_eq(host, user_eq) + success = await connect_and_apply_eq(host, user_eq, timeout) if success: print(f"✅ EQ settings successfully applied to device at {host}") @@ -178,16 +186,45 @@ async def main(eq_file_path, host, timeout=5): def cli(): """Command line interface for the script.""" - if len(sys.argv) < 2: - print("Usage: python -m rew2streammagic.main [host_ip]") - print("Default host: 192.168.1.29") - sys.exit(1) + parser = argparse.ArgumentParser( + description="Apply Room EQ Wizard settings to Cambridge CXN 100", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + python -m rew2streammagic.main eq_file.txt + python -m rew2streammagic.main eq_file.txt --host 192.168.1.50 + python -m rew2streammagic.main eq_file.txt --timeout 10 --dry-run + """, + ) + + parser.add_argument( + "eq_file", help="Path to Room EQ Wizard equalizer description file" + ) + + parser.add_argument( + "--host", + default="192.168.1.29", + help="IP address of the Cambridge CXN 100 device (default: 192.168.1.29)", + ) - eq_file = sys.argv[1] - host = sys.argv[2] if len(sys.argv) > 2 else "192.168.1.29" + parser.add_argument( + "--timeout", + type=int, + default=5, + help="Connection timeout in seconds (default: 5)", + ) + + parser.add_argument( + "--dry-run", + action="store_true", + help="Parse EQ file but don't connect to device or apply settings", + ) try: - exit_code = asyncio.run(main(eq_file, host)) + args = parser.parse_args() + exit_code = asyncio.run( + main(args.eq_file, args.host, args.timeout, args.dry_run) + ) sys.exit(exit_code) except KeyboardInterrupt: print("\nOperation cancelled by user") From 61fe8349a6d9ab379b85bfea366bc1170541c8a9 Mon Sep 17 00:00:00 2001 From: Solmath <33658856+Solmath@users.noreply.github.com> Date: Thu, 21 Aug 2025 22:26:04 +0200 Subject: [PATCH 09/10] =?UTF-8?q?fix(connect=5Fand=5Fapply=5Feq):=20?= =?UTF-8?q?=F0=9F=94=A7=20Add=20IP=20address=20validation=20for=20connecti?= =?UTF-8?q?on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Implemented validation for both IPv4 and IPv6 addresses in `connect_and_apply_eq`. * Added error logging for invalid IP addresses to improve debugging. --- rew2streammagic/main.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rew2streammagic/main.py b/rew2streammagic/main.py index 3db6460..c73c412 100644 --- a/rew2streammagic/main.py +++ b/rew2streammagic/main.py @@ -6,6 +6,7 @@ import sys import re import asyncio +import ipaddress import logging import argparse from pathlib import Path @@ -85,6 +86,15 @@ def parse_eq_file(file_path): async def connect_and_apply_eq(host, user_eq, timeout=5): """Connect to StreamMagic device and apply EQ settings.""" + + # Validate IP address + try: + # This will validate both IPv4 and IPv6 addresses + host = str(ipaddress.ip_address(host)) + except ValueError as e: + logger.error(f"Invalid IP address: {host} - {e}") + return False + try: # Create session with timeout timeout_config = ClientTimeout(total=timeout) From 98f68c8e8f57b1e9e55069be6067999dd1ffa853 Mon Sep 17 00:00:00 2001 From: Solmath <33658856+Solmath@users.noreply.github.com> Date: Thu, 21 Aug 2025 22:26:28 +0200 Subject: [PATCH 10/10] =?UTF-8?q?docs:=20=E2=9C=8F=EF=B8=8F=20Update=20REA?= =?UTF-8?q?DME=20with=20advanced=20options=20for=20EQ=20file=20processing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Clarified that the tool sends EQ settings to the StreamMagic device at the specified host IP address. - Added details on using the `--host` argument for setting the host IP address. - Included information about IP address validation before connection. - Documented the `--dry-run` option for parsing files without connecting to the device. - Explained how to set the connection timeout duration. --- README.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index eecd6ba..7384f42 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,29 @@ rew2streammagic is a Python tool to parse Room EQ Wizard (REW) equalizer descrip poetry run rew2streammagic ``` -1. The tool will parse the file and send the EQ settings to your StreamMagic device, if it is supported by the API version. +1. The tool will parse the file and send the EQ settings to your StreamMagic device at the specified host IP address, if it is supported by the API version. + +## Advanced options + +- IP adress of the host can be set with the ```--host``` argument + + ```sh + poetry run rew2streammagic --host 192.168.1.50 + ```` + + IP adresses will be validated before attempting connection to ensure proper format. + +- To check whether the file can be parsed without connecting to the device: + + ```sh + poetry run rew2streammagic --dry-run + ``` + +- Duration of the connection timeout in seconds can be set like this: + + ```sh + poetry run rew2streammagic --timeout 10 + ``` ## Example