Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 57 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 <path_to_eq_file>
poetry run python -m rew2streammagic.main <path_to_eq_file> <ip_address>
```

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]
Expand Down
75 changes: 75 additions & 0 deletions examples/ipv6_demonstration.py
Original file line number Diff line number Diff line change
@@ -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()
47 changes: 44 additions & 3 deletions rew2streammagic/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -47,13 +69,31 @@ def parse_eq_file(file_path):


async def main():
if len(sys.argv) < 2:
print("Usage: python -m rew2streammagic.main <path_to_eq_file>")
if len(sys.argv) < 3:
print("Usage: python -m rew2streammagic.main <path_to_eq_file> <ip_address>")
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.")
Expand All @@ -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()

Expand Down