Hey that is a neat idea! here is a proof of concept in PYTHON (tested quickly) with a concept of a volume offset between the two systems.
If you save that as something like syncvol.py
then fix up the IP addresses
and set Volume_offset to zero or some plus or minus value.
and run it with Python <pathtoscript>
Using Verbose = 0 you should see something like this
INFO:__main__:Volume changed on System 1: 18
INFO:__main__:Volume changed on System 1: 14
INFO:__main__:Syncing volume: System 1 (14) -> System 2 (9) with offset -5
INFO:__main__:Volume set to 9 on 192.168.0.32
or (depending on the verbose flag the default it will print more info)
the remote is paired with system 1 also the GROUP leader.
<new python script with 'dynamic polling frequency' and using 'logging'>
------------------------------------------------------------------------
import requests
import time
import logging
# Define Phantom system IPs and settings
SYSTEM_1_IP = "192.168.0.31" # Leader system
SYSTEM_2_IP = "192.168.0.32" # Follower system
VOLUME_OFFSET = 5 # System 2 will be 5 units louder than System 1
VOLUME_ENDPOINT = "/ipcontrol/v1/systems/current/sources/current/soundControl/volume"
VERBOSE = 1 # 0 for less output, 1 for more output
# Set up logging
logging.basicConfig(level=logging.DEBUG if VERBOSE else logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
def get_volume(ip):
"""Retrieve current volume from a Phantom system."""
try:
response = requests.get(f"http://{ip}{VOLUME_ENDPOINT}", timeout=5)
response.raise_for_status()
return response.json().get("volume")
logging.debug(f"get_volume - {ip}: {volume}")
except requests.exceptions.RequestException as e:
logging.error(f"Error retrieving volume from {ip}: {e}")
return None
def set_volume(ip, volume):
"""Set the volume on a Phantom system."""
try:
response = requests.post(f"http://{ip}{VOLUME_ENDPOINT}", json={"volume": volume}, timeout=5)
response.raise_for_status()
logging.debug(f"set_volume to {volume} on {ip}")
except requests.exceptions.RequestException as e:
logging.error(f"Error setting volume on {ip}: {e}")
def clamp_volume(vol1, vol2, offset, min_vol=0, max_vol=100):
"""Clamp System 2's volume based on System 1's volume and the offset."""
if vol1 == 0:
logging.debug(f"clamp_volume found vol1 is zero return zero")
return 0
return max(min_vol, min(max_vol, vol1 + offset))
def sync_volume():
"""Sync volume from System 1 to System 2 with an offset."""
last_volume = None
polling_interval = 30
while True:
vol1, vol2 = get_volume(SYSTEM_1_IP), get_volume(SYSTEM_2_IP)
if vol1 is None:
time.sleep(polling_interval)
continue
if vol1 != last_volume:
logging.debug(f"Volume changed on System 1: {vol1}")
last_volume = vol1
polling_interval = 10
else:
polling_interval = 30
new_vol2 = clamp_volume(vol1, vol2, VOLUME_OFFSET)
if new_vol2 != vol2:
logging.info(f"Syncing volume: System 1 ({vol1}) -> System 2 ({new_vol2})")
set_volume(SYSTEM_2_IP, new_vol2)
time.sleep(polling_interval)
if __name__ == "__main__":
sync_volume()
If you save that as something like syncvol.py
then fix up the IP addresses
and set Volume_offset to zero or some plus or minus value.
and run it with Python <pathtoscript>
Using Verbose = 0 you should see something like this
INFO:__main__:Volume changed on System 1: 18
INFO:__main__:Volume changed on System 1: 14
INFO:__main__:Syncing volume: System 1 (14) -> System 2 (9) with offset -5
INFO:__main__:Volume set to 9 on 192.168.0.32
or (depending on the verbose flag the default it will print more info)
the remote is paired with system 1 also the GROUP leader.
<new python script with 'dynamic polling frequency' and using 'logging'>
------------------------------------------------------------------------
import requests
import time
import logging
# Define Phantom system IPs and settings
SYSTEM_1_IP = "192.168.0.31" # Leader system
SYSTEM_2_IP = "192.168.0.32" # Follower system
VOLUME_OFFSET = 5 # System 2 will be 5 units louder than System 1
VOLUME_ENDPOINT = "/ipcontrol/v1/systems/current/sources/current/soundControl/volume"
VERBOSE = 1 # 0 for less output, 1 for more output
# Set up logging
logging.basicConfig(level=logging.DEBUG if VERBOSE else logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
def get_volume(ip):
"""Retrieve current volume from a Phantom system."""
try:
response = requests.get(f"http://{ip}{VOLUME_ENDPOINT}", timeout=5)
response.raise_for_status()
return response.json().get("volume")
logging.debug(f"get_volume - {ip}: {volume}")
except requests.exceptions.RequestException as e:
logging.error(f"Error retrieving volume from {ip}: {e}")
return None
def set_volume(ip, volume):
"""Set the volume on a Phantom system."""
try:
response = requests.post(f"http://{ip}{VOLUME_ENDPOINT}", json={"volume": volume}, timeout=5)
response.raise_for_status()
logging.debug(f"set_volume to {volume} on {ip}")
except requests.exceptions.RequestException as e:
logging.error(f"Error setting volume on {ip}: {e}")
def clamp_volume(vol1, vol2, offset, min_vol=0, max_vol=100):
"""Clamp System 2's volume based on System 1's volume and the offset."""
if vol1 == 0:
logging.debug(f"clamp_volume found vol1 is zero return zero")
return 0
return max(min_vol, min(max_vol, vol1 + offset))
def sync_volume():
"""Sync volume from System 1 to System 2 with an offset."""
last_volume = None
polling_interval = 30
while True:
vol1, vol2 = get_volume(SYSTEM_1_IP), get_volume(SYSTEM_2_IP)
if vol1 is None:
time.sleep(polling_interval)
continue
if vol1 != last_volume:
logging.debug(f"Volume changed on System 1: {vol1}")
last_volume = vol1
polling_interval = 10
else:
polling_interval = 30
new_vol2 = clamp_volume(vol1, vol2, VOLUME_OFFSET)
if new_vol2 != vol2:
logging.info(f"Syncing volume: System 1 ({vol1}) -> System 2 ({new_vol2})")
set_volume(SYSTEM_2_IP, new_vol2)
time.sleep(polling_interval)
if __name__ == "__main__":
sync_volume()

