diff --git a/README.md b/README.md index 5d56ff1..05b5ac8 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,21 @@ rew2streammagic is a Python tool to parse Room EQ Wizard (REW) equalizer descrip - Supports various filter types (PEAKING, LOWSHELF, HIGHSHELF, LOWPASS, HIGHPASS) - Maps REW filter types to StreamMagic-compatible types - Communicates with StreamMagic devices to set user EQ parameters +- **Full IPv4 and IPv6 address support** for device connectivity + +## IP Address Support + +The tool supports both IPv4 and IPv6 addresses for connecting to StreamMagic devices: + +- **IPv4**: Standard dotted decimal notation (e.g., `192.168.1.29`, `10.0.0.100`) +- **IPv6**: All standard IPv6 formats including: + - Compressed notation (e.g., `::1`, `2001:db8::1`) + - Full notation (e.g., `2001:0db8:85a3:0000:0000:8a2e:0370:7334`) + - Link-local addresses (e.g., `fe80::1234:5678:abcd:ef01`) + - IPv4-mapped IPv6 addresses (e.g., `::ffff:192.168.1.1`) + - Zone identifiers for link-local addresses (e.g., `fe80::1%eth0`) + +All IP addresses are validated before attempting connection to ensure proper format. ## Usage @@ -22,15 +37,55 @@ rew2streammagic is a Python tool to parse Room EQ Wizard (REW) equalizer descrip 1. Run the tool: ```sh - poetry run python -m rew2streammagic.main + poetry run python -m rew2streammagic.main ``` -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 IP address, if it is supported by the API version. ## Example See the `example_data/` folder for sample input files. +For a comprehensive demonstration of IPv6 address support, run: +```sh +python3 examples/ipv6_demonstration.py +``` + +### Usage Examples + +#### IPv4 Examples +```sh +# Connect to a StreamMagic device at IP address 192.168.1.29 +poetry run python -m rew2streammagic.main example_data/default.txt 192.168.1.29 + +# Connect to a device at a different IP address +poetry run python -m rew2streammagic.main example_data/peaking.txt 10.0.0.100 + +# Connect to a device on localhost +poetry run python -m rew2streammagic.main example_data/filter_types.txt 127.0.0.1 +``` + +#### IPv6 Examples +```sh +# IPv6 localhost (compressed notation) +poetry run python -m rew2streammagic.main example_data/default.txt ::1 + +# IPv6 link-local address +poetry run python -m rew2streammagic.main example_data/peaking.txt fe80::1234:5678:abcd:ef01 + +# IPv6 global unicast address (compressed notation) +poetry run python -m rew2streammagic.main example_data/filter_types.txt 2001:db8::1 + +# IPv6 full notation +poetry run python -m rew2streammagic.main example_data/default.txt 2001:0db8:85a3:0000:0000:8a2e:0370:7334 + +# IPv4-mapped IPv6 address +poetry run python -m rew2streammagic.main example_data/peaking.txt ::ffff:192.168.1.1 + +# IPv6 with zone identifier (for link-local addresses) +poetry run python -m rew2streammagic.main example_data/filter_types.txt fe80::1%eth0 +``` + ## Requirements > [!WARNING] diff --git a/examples/ipv6_demonstration.py b/examples/ipv6_demonstration.py new file mode 100644 index 0000000..393ceab --- /dev/null +++ b/examples/ipv6_demonstration.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +""" +IPv6 Examples Demonstration + +This script demonstrates various IPv6 address formats that are supported +by the rew2streammagic tool for connecting to StreamMagic devices. +""" + +import ipaddress + + +def validate_ip_address(ip_str): + """ + Validate that the provided string is a valid IP address. + + Args: + ip_str (str): The IP address string to validate + + Returns: + str: The validated IP address + + Raises: + ValueError: If the IP address is invalid + """ + try: + # This will validate both IPv4 and IPv6 addresses + validated_ip = ipaddress.ip_address(ip_str) + return str(validated_ip) + except ValueError as e: + raise ValueError(f"Invalid IP address '{ip_str}': {e}") + + +def main(): + print("rew2streammagic IPv6 Address Support Demonstration") + print("=" * 60) + print() + + # IPv6 examples from the README + ipv6_examples = [ + ("::1", "IPv6 localhost (compressed notation)"), + ("fe80::1234:5678:abcd:ef01", "IPv6 link-local address"), + ("2001:db8::1", "IPv6 global unicast address (compressed notation)"), + ("2001:0db8:85a3:0000:0000:8a2e:0370:7334", "IPv6 full notation"), + ("::ffff:192.168.1.1", "IPv4-mapped IPv6 address"), + ("fe80::1%eth0", "IPv6 with zone identifier (for link-local addresses)"), + ] + + print("All of the following IPv6 address formats are supported:\n") + + for ip_addr, description in ipv6_examples: + try: + validated = validate_ip_address(ip_addr) + print(f"✓ {ip_addr:<35} -> {validated}") + print(f" {description}") + print() + except ValueError as e: + print(f"✗ {ip_addr:<35} -> ERROR: {e}") + print() + + print("Usage Example Commands:") + print("-" * 30) + for ip_addr, description in ipv6_examples: + print( + f"poetry run python -m rew2streammagic.main example_data/default.txt {ip_addr}" + ) + + print() + print("Note: These examples demonstrate IP address validation.") + print( + "Actual device connection requires a valid StreamMagic device at the specified address." + ) + + +if __name__ == "__main__": + main() diff --git a/rew2streammagic/main.py b/rew2streammagic/main.py index 9198d14..6b34796 100644 --- a/rew2streammagic/main.py +++ b/rew2streammagic/main.py @@ -6,11 +6,33 @@ import sys import re import asyncio +import ipaddress from pathlib import Path from aiostreammagic import StreamMagicClient, EQBand, UserEQ, EQFilterType, Info from packaging.version import Version +def validate_ip_address(ip_str): + """ + Validate that the provided string is a valid IP address. + + Args: + ip_str (str): The IP address string to validate + + Returns: + str: The validated IP address + + Raises: + ValueError: If the IP address is invalid + """ + try: + # This will validate both IPv4 and IPv6 addresses + validated_ip = ipaddress.ip_address(ip_str) + return str(validated_ip) + except ValueError as e: + raise ValueError(f"Invalid IP address '{ip_str}': {e}") + + def parse_eq_file(file_path): bands = [] filter_map = { @@ -47,13 +69,31 @@ def parse_eq_file(file_path): async def main(): - if len(sys.argv) < 2: - print("Usage: python -m rew2streammagic.main ") + if len(sys.argv) < 3: + print("Usage: python -m rew2streammagic.main ") + print("") + print("Arguments:") + print(" path_to_eq_file Path to the REW equalizer description file") + print(" ip_address IPv4 or IPv6 address of the StreamMagic device") + print("") + print("Examples:") + print(" python -m rew2streammagic.main eq_file.txt 192.168.1.29") + print(" python -m rew2streammagic.main eq_file.txt ::1") + print(" python -m rew2streammagic.main eq_file.txt 2001:db8::1") sys.exit(1) + eq_file = Path(sys.argv[1]) if not eq_file.exists(): print(f"File not found: {eq_file}") sys.exit(1) + + # Validate IP address + try: + ip_address = validate_ip_address(sys.argv[2]) + except ValueError as e: + print(f"Error: {e}") + sys.exit(1) + user_eq = parse_eq_file(eq_file) if not user_eq.bands: print("No equalizer bands found in the file.") @@ -62,7 +102,8 @@ async def main(): for band in user_eq.bands: print(f"Band {band.index}: Freq={band.freq}Hz, Gain={band.gain}dB, Q={band.q}") - async with StreamMagicClient("192.168.1.29") as client: + print(f"Connecting to StreamMagic device at {ip_address}...") + async with StreamMagicClient(ip_address) as client: await client.connect() info: Info = await client.get_info()