From 603fb41daa81b98cab7dfab81e3350576e104320 Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Wed, 21 Aug 2024 21:00:54 +0200 Subject: [PATCH 01/17] Extend server to return ZoneB attributes in BASIC --- ynca/server.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ynca/server.py b/ynca/server.py index 8e9a84a..51150c7 100644 --- a/ynca/server.py +++ b/ynca/server.py @@ -96,9 +96,13 @@ def put_data(self, subunit, function, new_value): multiresponse_functions_table = { "BASIC": [ "PWR", + "PWRB", "SLEEP", "VOL", "MUTE", + "ZONEBAVAIL", + "ZONEBVOL", + "ZONEBMUTE", "INP", "STRAIGHT", "ENHANCER", @@ -113,6 +117,8 @@ def put_data(self, subunit, function, new_value): "ADAPTIVEDRC", "DIALOGUELVL", "DTSDIALOGUECONTROL", + "SPEAKERA", + "SPEAKERB", ], "METAINFO": ["ARTIST", "ALBUM", "SONG", "TRACK", "CHNAME"], "RDSINFO": ["RDSPRGTYPE", "RDSPRGSERVICE", "RDSTXTA", "RDSTXTB", "RDSCLOCK"], From 903025d5c2f3bb9632f10670016fc9c4b7f8f5c8 Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Wed, 21 Aug 2024 21:01:50 +0200 Subject: [PATCH 02/17] Add RX-V583 logs which has ZoneB data --- logs/RX-V583.txt | 547 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 547 insertions(+) create mode 100644 logs/RX-V583.txt diff --git a/logs/RX-V583.txt b/logs/RX-V583.txt new file mode 100644 index 0000000..12f2563 --- /dev/null +++ b/logs/RX-V583.txt @@ -0,0 +1,547 @@ +{ + "sys": { + "modelname": "RX-V583", + "version": "2.87/1.81" + }, + "communication": { + "initialization": [ + "Send: @SYS:MODELNAME=?", + "Received: @SYS:MODELNAME=RX-V583", + "Send: @SYS:MODELNAME=?", + "Received: @SYS:MODELNAME=RX-V583", + "Send: @SYS:AVAIL=?", + "Received: @UNDEFINED", + "Send: @MAIN:AVAIL=?", + "Received: @MAIN:AVAIL=Ready", + "Send: @ZONE2:AVAIL=?", + "Received: @RESTRICTED", + "Send: @ZONE3:AVAIL=?", + "Received: @RESTRICTED", + "Send: @ZONE4:AVAIL=?", + "Received: @RESTRICTED", + "Send: @AIRPLAY:AVAIL=?", + "Received: @AIRPLAY:AVAIL=Not Ready", + "Send: @BT:AVAIL=?", + "Received: @BT:AVAIL=Not Ready", + "Send: @DAB:AVAIL=?", + "Received: @RESTRICTED", + "Send: @IPOD:AVAIL=?", + "Received: @UNDEFINED", + "Send: @IPODUSB:AVAIL=?", + "Received: @UNDEFINED", + "Send: @NAPSTER:AVAIL=?", + "Received: @RESTRICTED", + "Send: @NETRADIO:AVAIL=?", + "Received: @NETRADIO:AVAIL=Not Ready", + "Send: @PANDORA:AVAIL=?", + "Received: @PANDORA:AVAIL=Not Ready", + "Send: @PC:AVAIL=?", + "Received: @UNDEFINED", + "Send: @RHAP:AVAIL=?", + "Received: @UNDEFINED", + "Send: @SIRIUS:AVAIL=?", + "Received: @UNDEFINED", + "Send: @SIRIUSIR:AVAIL=?", + "Received: @UNDEFINED", + "Send: @SIRIUSXM:AVAIL=?", + "Received: @RESTRICTED", + "Send: @SERVER:AVAIL=?", + "Received: @SERVER:AVAIL=Not Ready", + "Send: @SPOTIFY:AVAIL=?", + "Received: @SPOTIFY:AVAIL=Not Ready", + "Send: @TUN:AVAIL=?", + "Received: @TUN:AVAIL=Not Ready", + "Send: @UAW:AVAIL=?", + "Received: @UNDEFINED", + "Send: @USB:AVAIL=?", + "Received: @USB:AVAIL=Not Ready", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @SYS:AVAIL=?", + "Received: @UNDEFINED", + "Send: @SYS:HDMIOUT1=?", + "Received: @SYS:HDMIOUT1=On", + "Send: @SYS:HDMIOUT2=?", + "Received: @RESTRICTED", + "Send: @SYS:HDMIOUT3=?", + "Received: @UNDEFINED", + "Send: @SYS:INPNAME=?", + "Received: @SYS:INPNAMETUNER=TUNER", + "Received: @SYS:INPNAMEHDMI1=4K", + "Received: @SYS:INPNAMEHDMI2=PlayStation 4", + "Received: @SYS:INPNAMEHDMI3=HDMI3", + "Received: @SYS:INPNAMEHDMI4=HDMI4", + "Received: @SYS:INPNAMEAV1=AV1", + "Received: @SYS:INPNAMEAV2=AV2", + "Received: @SYS:INPNAMEAV3=AV3", + "Received: @SYS:INPNAMEAUX=AUX", + "Received: @SYS:INPNAMEAUDIO1=TV", + "Received: @SYS:INPNAMEAUDIO2=AUDIO2", + "Received: @SYS:INPNAMEAUDIO3=AUDIO3", + "Received: @SYS:INPNAMEMCLINK=MusicCast Link", + "Received: @SYS:INPNAMESERVER=SERVER", + "Received: @SYS:INPNAMENETRADIO=NET RADIO", + "Received: @SYS:INPNAMEBT=Bluetooth", + "Received: @SYS:INPNAMEUSB=USB", + "Send: @SYS:MODELNAME=?", + "Received: @SYS:MODELNAME=RX-V583", + "Send: @SYS:PARTY=?", + "Received: @RESTRICTED", + "Send: @SYS:PARTYMUTE=?", + "Received: @RESTRICTED", + "Send: @SYS:PWR=?", + "Received: @SYS:PWR=On", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @AIRPLAY:METAINFO=?", + "Received: @AIRPLAY:ARTIST=", + "Received: @AIRPLAY:ALBUM=", + "Received: @AIRPLAY:SONG=", + "Send: @AIRPLAY:AVAIL=?", + "Received: @AIRPLAY:AVAIL=Not Ready", + "Send: @AIRPLAY:PLAYBACKINFO=?", + "Received: @AIRPLAY:PLAYBACKINFO=Stop", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @BT:AVAIL=?", + "Received: @BT:AVAIL=Not Ready", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @MAIN:ADAPTIVEDRC=?", + "Received: @MAIN:ADAPTIVEDRC=Off", + "Send: @MAIN:AVAIL=?", + "Received: @MAIN:AVAIL=Ready", + "Send: @MAIN:ENHANCER=?", + "Received: @MAIN:ENHANCER=Off", + "Send: @MAIN:HDMIOUT=?", + "Received: @UNDEFINED", + "Send: @MAIN:HPBASS=?", + "Received: @UNDEFINED", + "Send: @MAIN:HPTREBLE=?", + "Received: @UNDEFINED", + "Send: @MAIN:INITVOLLVL=?", + "Received: @MAIN:INITVOLLVL=Off", + "Send: @MAIN:INITVOLMODE=?", + "Received: @RESTRICTED", + "Send: @MAIN:BASIC=?", + "Received: @MAIN:PWR=On", + "Received: @MAIN:PWRB=On", + "Received: @MAIN:SLEEP=Off", + "Received: @MAIN:VOL=-22.0", + "Received: @MAIN:MUTE=Off", + "Received: @MAIN:SWFRTRIM=0.0", + "Received: @MAIN:ZONEBAVAIL=Ready", + "Received: @MAIN:ZONEBVOL=-42.0", + "Received: @MAIN:ZONEBMUTE=Off", + "Received: @MAIN:INP=HDMI1", + "Received: @MAIN:STRAIGHT=Off", + "Received: @MAIN:ENHANCER=Off", + "Received: @MAIN:SOUNDPRG=7ch Stereo", + "Received: @MAIN:3DCINEMA=Auto", + "Received: @MAIN:TONEBASS=0.0", + "Received: @MAIN:TONETREBLE=0.0", + "Received: @MAIN:DIRMODE=Off", + "Received: @SYS:HDMIOUT1=On", + "Received: @MAIN:EXBASS=Auto", + "Received: @MAIN:ADAPTIVEDRC=Off", + "Received: @MAIN:DIALOGUELVL=0", + "Received: @MAIN:DTSDIALOGUECONTROL=0", + "Received: @MAIN:SPEAKERA=On", + "Received: @MAIN:SPEAKERB=On", + "Send: @MAIN:LIPSYNCHDMIOUT1OFFSET=?", + "Received: @UNDEFINED", + "Send: @MAIN:LIPSYNCHDMIOUT2OFFSET=?", + "Received: @UNDEFINED", + "Send: @MAIN:MAXVOL=?", + "Received: @MAIN:MAXVOL=16.5", + "Send: @MAIN:SCENENAME=?", + "Received: @MAIN:SCENE1NAME=BD/DVD Movie Viewing", + "Received: @MAIN:SCENE2NAME=TV Viewing", + "Received: @MAIN:SCENE3NAME=NET Audio Listening", + "Received: @MAIN:SCENE4NAME=Radio Listening", + "Send: @MAIN:SLEEP=?", + "Received: @MAIN:SLEEP=Off", + "Send: @MAIN:SPBASS=?", + "Received: @UNDEFINED", + "Send: @MAIN:SPTREBLE=?", + "Received: @UNDEFINED", + "Send: @MAIN:3DCINEMA=?", + "Received: @MAIN:3DCINEMA=Auto", + "Send: @MAIN:2CHDECODER=?", + "Received: @MAIN:2CHDECODER=Auto", + "Send: @MAIN:ZONENAME=?", + "Received: @MAIN:ZONENAME=Main", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @NETRADIO:METAINFO=?", + "Received: @NETRADIO:STATION=", + "Received: @NETRADIO:ALBUM=", + "Received: @NETRADIO:SONG=", + "Send: @NETRADIO:AVAIL=?", + "Received: @NETRADIO:AVAIL=Not Ready", + "Send: @NETRADIO:PLAYBACKINFO=?", + "Received: @NETRADIO:PLAYBACKINFO=Stop", + "Send: @NETRADIO:PRESET=?", + "Send: @NETRADIO:STATION=?", + "Received: @NETRADIO:STATION=", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @PANDORA:METAINFO=?", + "Received: @PANDORA:TRACK=", + "Received: @PANDORA:ALBUM=", + "Received: @PANDORA:STATION=", + "Send: @PANDORA:AVAIL=?", + "Received: @PANDORA:AVAIL=Not Ready", + "Send: @PANDORA:PLAYBACKINFO=?", + "Received: @PANDORA:PLAYBACKINFO=Stop", + "Send: @PANDORA:PRESET=?", + "Send: @PANDORA:STATION=?", + "Received: @PANDORA:STATION=", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @SERVER:METAINFO=?", + "Received: @SERVER:ARTIST=", + "Received: @SERVER:ALBUM=", + "Received: @SERVER:SONG=", + "Send: @SERVER:AVAIL=?", + "Received: @SERVER:AVAIL=Not Ready", + "Send: @SERVER:PLAYBACKINFO=?", + "Received: @SERVER:PLAYBACKINFO=Stop", + "Send: @SERVER:REPEAT=?", + "Received: @SERVER:REPEAT=Off", + "Send: @SERVER:SHUFFLE=?", + "Received: @SERVER:SHUFFLE=Off", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @SPOTIFY:METAINFO=?", + "Received: @SPOTIFY:ARTIST=", + "Received: @SPOTIFY:ALBUM=", + "Received: @SPOTIFY:TRACK=", + "Send: @SPOTIFY:AVAIL=?", + "Received: @SPOTIFY:AVAIL=Not Ready", + "Send: @SPOTIFY:PLAYBACKINFO=?", + "Received: @SPOTIFY:PLAYBACKINFO=Stop", + "Send: @SPOTIFY:REPEAT=?", + "Received: @SPOTIFY:REPEAT=All", + "Send: @SPOTIFY:SHUFFLE=?", + "Received: @SPOTIFY:SHUFFLE=Off", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @TUN:AMFREQ=?", + "Received: @TUN:AMFREQ=1080", + "Send: @TUN:AVAIL=?", + "Received: @TUN:AVAIL=Not Ready", + "Send: @TUN:BAND=?", + "Received: @TUN:BAND=FM", + "Send: @TUN:FMFREQ=?", + "Received: @TUN:FMFREQ=99.90", + "Send: @TUN:PRESET=?", + "Received: @TUN:PRESET=No Preset", + "Send: @TUN:RDSINFO=?", + "Received: @RESTRICTED", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @USB:METAINFO=?", + "Received: @USB:ARTIST=", + "Received: @USB:ALBUM=", + "Received: @USB:SONG=", + "Send: @USB:AVAIL=?", + "Received: @USB:AVAIL=Not Ready", + "Send: @USB:PLAYBACKINFO=?", + "Received: @USB:PLAYBACKINFO=Stop", + "Send: @USB:PRESET=?", + "Send: @USB:REPEAT=?", + "Received: @USB:REPEAT=Off", + "Send: @USB:SHUFFLE=?", + "Received: @USB:SHUFFLE=Off", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81" + ], + "history": [ + "Send: @SYS:MODELNAME=?", + "Received: @SYS:MODELNAME=RX-V583", + "Send: @SYS:MODELNAME=?", + "Received: @SYS:MODELNAME=RX-V583", + "Send: @SYS:AVAIL=?", + "Received: @UNDEFINED", + "Send: @MAIN:AVAIL=?", + "Received: @MAIN:AVAIL=Ready", + "Send: @ZONE2:AVAIL=?", + "Received: @RESTRICTED", + "Send: @ZONE3:AVAIL=?", + "Received: @RESTRICTED", + "Send: @ZONE4:AVAIL=?", + "Received: @RESTRICTED", + "Send: @AIRPLAY:AVAIL=?", + "Received: @AIRPLAY:AVAIL=Not Ready", + "Send: @BT:AVAIL=?", + "Received: @BT:AVAIL=Not Ready", + "Send: @DAB:AVAIL=?", + "Received: @RESTRICTED", + "Send: @IPOD:AVAIL=?", + "Received: @UNDEFINED", + "Send: @IPODUSB:AVAIL=?", + "Received: @UNDEFINED", + "Send: @NAPSTER:AVAIL=?", + "Received: @RESTRICTED", + "Send: @NETRADIO:AVAIL=?", + "Received: @NETRADIO:AVAIL=Not Ready", + "Send: @PANDORA:AVAIL=?", + "Received: @PANDORA:AVAIL=Not Ready", + "Send: @PC:AVAIL=?", + "Received: @UNDEFINED", + "Send: @RHAP:AVAIL=?", + "Received: @UNDEFINED", + "Send: @SIRIUS:AVAIL=?", + "Received: @UNDEFINED", + "Send: @SIRIUSIR:AVAIL=?", + "Received: @UNDEFINED", + "Send: @SIRIUSXM:AVAIL=?", + "Received: @RESTRICTED", + "Send: @SERVER:AVAIL=?", + "Received: @SERVER:AVAIL=Not Ready", + "Send: @SPOTIFY:AVAIL=?", + "Received: @SPOTIFY:AVAIL=Not Ready", + "Send: @TUN:AVAIL=?", + "Received: @TUN:AVAIL=Not Ready", + "Send: @UAW:AVAIL=?", + "Received: @UNDEFINED", + "Send: @USB:AVAIL=?", + "Received: @USB:AVAIL=Not Ready", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @SYS:AVAIL=?", + "Received: @UNDEFINED", + "Send: @SYS:HDMIOUT1=?", + "Received: @SYS:HDMIOUT1=On", + "Send: @SYS:HDMIOUT2=?", + "Received: @RESTRICTED", + "Send: @SYS:HDMIOUT3=?", + "Received: @UNDEFINED", + "Send: @SYS:INPNAME=?", + "Received: @SYS:INPNAMETUNER=TUNER", + "Received: @SYS:INPNAMEHDMI1=4K", + "Received: @SYS:INPNAMEHDMI2=PlayStation 4", + "Received: @SYS:INPNAMEHDMI3=HDMI3", + "Received: @SYS:INPNAMEHDMI4=HDMI4", + "Received: @SYS:INPNAMEAV1=AV1", + "Received: @SYS:INPNAMEAV2=AV2", + "Received: @SYS:INPNAMEAV3=AV3", + "Received: @SYS:INPNAMEAUX=AUX", + "Received: @SYS:INPNAMEAUDIO1=TV", + "Received: @SYS:INPNAMEAUDIO2=AUDIO2", + "Received: @SYS:INPNAMEAUDIO3=AUDIO3", + "Received: @SYS:INPNAMEMCLINK=MusicCast Link", + "Received: @SYS:INPNAMESERVER=SERVER", + "Received: @SYS:INPNAMENETRADIO=NET RADIO", + "Received: @SYS:INPNAMEBT=Bluetooth", + "Received: @SYS:INPNAMEUSB=USB", + "Send: @SYS:MODELNAME=?", + "Received: @SYS:MODELNAME=RX-V583", + "Send: @SYS:PARTY=?", + "Received: @RESTRICTED", + "Send: @SYS:PARTYMUTE=?", + "Received: @RESTRICTED", + "Send: @SYS:PWR=?", + "Received: @SYS:PWR=On", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @AIRPLAY:METAINFO=?", + "Received: @AIRPLAY:ARTIST=", + "Received: @AIRPLAY:ALBUM=", + "Received: @AIRPLAY:SONG=", + "Send: @AIRPLAY:AVAIL=?", + "Received: @AIRPLAY:AVAIL=Not Ready", + "Send: @AIRPLAY:PLAYBACKINFO=?", + "Received: @AIRPLAY:PLAYBACKINFO=Stop", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @BT:AVAIL=?", + "Received: @BT:AVAIL=Not Ready", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @MAIN:ADAPTIVEDRC=?", + "Received: @MAIN:ADAPTIVEDRC=Off", + "Send: @MAIN:AVAIL=?", + "Received: @MAIN:AVAIL=Ready", + "Send: @MAIN:ENHANCER=?", + "Received: @MAIN:ENHANCER=Off", + "Send: @MAIN:HDMIOUT=?", + "Received: @UNDEFINED", + "Send: @MAIN:HPBASS=?", + "Received: @UNDEFINED", + "Send: @MAIN:HPTREBLE=?", + "Received: @UNDEFINED", + "Send: @MAIN:INITVOLLVL=?", + "Received: @MAIN:INITVOLLVL=Off", + "Send: @MAIN:INITVOLMODE=?", + "Received: @RESTRICTED", + "Send: @MAIN:BASIC=?", + "Received: @MAIN:PWR=On", + "Received: @MAIN:PWRB=On", + "Received: @MAIN:SLEEP=Off", + "Received: @MAIN:VOL=-22.0", + "Received: @MAIN:MUTE=Off", + "Received: @MAIN:SWFRTRIM=0.0", + "Received: @MAIN:ZONEBAVAIL=Ready", + "Received: @MAIN:ZONEBVOL=-42.0", + "Received: @MAIN:ZONEBMUTE=Off", + "Received: @MAIN:INP=HDMI1", + "Received: @MAIN:STRAIGHT=Off", + "Received: @MAIN:ENHANCER=Off", + "Received: @MAIN:SOUNDPRG=7ch Stereo", + "Received: @MAIN:3DCINEMA=Auto", + "Received: @MAIN:TONEBASS=0.0", + "Received: @MAIN:TONETREBLE=0.0", + "Received: @MAIN:DIRMODE=Off", + "Received: @SYS:HDMIOUT1=On", + "Received: @MAIN:EXBASS=Auto", + "Received: @MAIN:ADAPTIVEDRC=Off", + "Received: @MAIN:DIALOGUELVL=0", + "Received: @MAIN:DTSDIALOGUECONTROL=0", + "Received: @MAIN:SPEAKERA=On", + "Received: @MAIN:SPEAKERB=On", + "Send: @MAIN:LIPSYNCHDMIOUT1OFFSET=?", + "Received: @UNDEFINED", + "Send: @MAIN:LIPSYNCHDMIOUT2OFFSET=?", + "Received: @UNDEFINED", + "Send: @MAIN:MAXVOL=?", + "Received: @MAIN:MAXVOL=16.5", + "Send: @MAIN:SCENENAME=?", + "Received: @MAIN:SCENE1NAME=BD/DVD Movie Viewing", + "Received: @MAIN:SCENE2NAME=TV Viewing", + "Received: @MAIN:SCENE3NAME=NET Audio Listening", + "Received: @MAIN:SCENE4NAME=Radio Listening", + "Send: @MAIN:SLEEP=?", + "Received: @MAIN:SLEEP=Off", + "Send: @MAIN:SPBASS=?", + "Received: @UNDEFINED", + "Send: @MAIN:SPTREBLE=?", + "Received: @UNDEFINED", + "Send: @MAIN:3DCINEMA=?", + "Received: @MAIN:3DCINEMA=Auto", + "Send: @MAIN:2CHDECODER=?", + "Received: @MAIN:2CHDECODER=Auto", + "Send: @MAIN:ZONENAME=?", + "Received: @MAIN:ZONENAME=Main", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @NETRADIO:METAINFO=?", + "Received: @NETRADIO:STATION=", + "Received: @NETRADIO:ALBUM=", + "Received: @NETRADIO:SONG=", + "Send: @NETRADIO:AVAIL=?", + "Received: @NETRADIO:AVAIL=Not Ready", + "Send: @NETRADIO:PLAYBACKINFO=?", + "Received: @NETRADIO:PLAYBACKINFO=Stop", + "Send: @NETRADIO:PRESET=?", + "Send: @NETRADIO:STATION=?", + "Received: @NETRADIO:STATION=", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @PANDORA:METAINFO=?", + "Received: @PANDORA:TRACK=", + "Received: @PANDORA:ALBUM=", + "Received: @PANDORA:STATION=", + "Send: @PANDORA:AVAIL=?", + "Received: @PANDORA:AVAIL=Not Ready", + "Send: @PANDORA:PLAYBACKINFO=?", + "Received: @PANDORA:PLAYBACKINFO=Stop", + "Send: @PANDORA:PRESET=?", + "Send: @PANDORA:STATION=?", + "Received: @PANDORA:STATION=", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @SERVER:METAINFO=?", + "Received: @SERVER:ARTIST=", + "Received: @SERVER:ALBUM=", + "Received: @SERVER:SONG=", + "Send: @SERVER:AVAIL=?", + "Received: @SERVER:AVAIL=Not Ready", + "Send: @SERVER:PLAYBACKINFO=?", + "Received: @SERVER:PLAYBACKINFO=Stop", + "Send: @SERVER:REPEAT=?", + "Received: @SERVER:REPEAT=Off", + "Send: @SERVER:SHUFFLE=?", + "Received: @SERVER:SHUFFLE=Off", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @SPOTIFY:METAINFO=?", + "Received: @SPOTIFY:ARTIST=", + "Received: @SPOTIFY:ALBUM=", + "Received: @SPOTIFY:TRACK=", + "Send: @SPOTIFY:AVAIL=?", + "Received: @SPOTIFY:AVAIL=Not Ready", + "Send: @SPOTIFY:PLAYBACKINFO=?", + "Received: @SPOTIFY:PLAYBACKINFO=Stop", + "Send: @SPOTIFY:REPEAT=?", + "Received: @SPOTIFY:REPEAT=All", + "Send: @SPOTIFY:SHUFFLE=?", + "Received: @SPOTIFY:SHUFFLE=Off", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @TUN:AMFREQ=?", + "Received: @TUN:AMFREQ=1080", + "Send: @TUN:AVAIL=?", + "Received: @TUN:AVAIL=Not Ready", + "Send: @TUN:BAND=?", + "Received: @TUN:BAND=FM", + "Send: @TUN:FMFREQ=?", + "Received: @TUN:FMFREQ=99.90", + "Send: @TUN:PRESET=?", + "Received: @TUN:PRESET=No Preset", + "Send: @TUN:RDSINFO=?", + "Received: @RESTRICTED", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @USB:METAINFO=?", + "Received: @USB:ARTIST=", + "Received: @USB:ALBUM=", + "Received: @USB:SONG=", + "Send: @USB:AVAIL=?", + "Received: @USB:AVAIL=Not Ready", + "Send: @USB:PLAYBACKINFO=?", + "Received: @USB:PLAYBACKINFO=Stop", + "Send: @USB:PRESET=?", + "Send: @USB:REPEAT=?", + "Received: @USB:REPEAT=Off", + "Send: @USB:SHUFFLE=?", + "Received: @USB:SHUFFLE=Off", + "Send: @SYS:VERSION=?", + "Received: @SYS:VERSION=2.87/1.81", + "Send: @SYS:MODELNAME=?", + "Received: @SYS:MODELNAME=RX-V583", + "Send: @SYS:MODELNAME=?", + "Received: @SYS:MODELNAME=RX-V583", + "Send: @SYS:MODELNAME=?", + "Received: @SYS:MODELNAME=RX-V583", + "Received: @MAIN:PWR=Standby", + "Received: @MAIN:SPEAKERA=Off", + "Received: @MAIN:PWR=On", + "Received: @MAIN:AVAIL=Ready", + "Received: @MAIN:DIRMODE=Off", + "Received: @MAIN:INP=HDMI1", + "Received: @MAIN:MUTE=Off", + "Received: @MAIN:PWRB=On", + "Received: @MAIN:SOUNDPRG=7ch Stereo", + "Received: @MAIN:STRAIGHT=Off", + "Received: @MAIN:VOL=-22.0", + "Received: @MAIN:ZONEBAVAIL=Ready", + "Received: @MAIN:ZONEBMUTE=Off", + "Received: @MAIN:ZONEBVOL=-42.0", + "Received: @MAIN:SPEAKERA=On", + "Received: @MAIN:PWRB=Standby", + "Received: @MAIN:SPEAKERB=Off", + "Received: @MAIN:PWRB=On", + "Received: @MAIN:ZONEBAVAIL=Ready", + "Received: @MAIN:ZONEBVOL=-42.0", + "Received: @MAIN:ZONEBMUTE=Off", + "Received: @MAIN:SPEAKERB=On", + "Send: @SYS:MODELNAME=?", + "Received: @SYS:MODELNAME=RX-V583", + "Send: @SYS:MODELNAME=?", + "Received: @SYS:MODELNAME=RX-V583" + ] + } + } +} \ No newline at end of file From 1d61b902c6b45116314936abef780512dbeb034f Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Wed, 21 Aug 2024 21:10:43 +0200 Subject: [PATCH 03/17] Add support for ZoneB atttributes on Main zone --- ynca/enums.py | 74 +++++++++++++++++++++++++++++++++++++++++++ ynca/subunits/zone.py | 21 ++++++++++++ 2 files changed, 95 insertions(+) diff --git a/ynca/enums.py b/ynca/enums.py index 0ca90c3..a9089a5 100644 --- a/ynca/enums.py +++ b/ynca/enums.py @@ -325,6 +325,19 @@ def _missing_(cls, value): UNKNOWN = UNKNOWN_STRING """Unknown values in the enum are mapped to UNKNOWN""" +@unique +class PwrB(Enum): + ON = "On" + STANDBY = "Standby" + + @classmethod + def _missing_(cls, value): + logger.warning("Unknown value '%s' in %s", value, cls.__name__) + return cls.UNKNOWN + + UNKNOWN = UNKNOWN_STRING + """Unknown values in the enum are mapped to UNKNOWN""" + @unique class Repeat(str, Enum): @@ -413,6 +426,34 @@ def _missing_(cls, value): """Unknown values in the enum are mapped to UNKNOWN""" +@unique +class SpeakerA(Enum): + ON = "On" + OFF = "Off" + + @classmethod + def _missing_(cls, value): + logger.warning("Unknown value '%s' in %s", value, cls.__name__) + return cls.UNKNOWN + + UNKNOWN = UNKNOWN_STRING + """Unknown values in the enum are mapped to UNKNOWN""" + + +@unique +class SpeakerB(Enum): + ON = "On" + OFF = "Off" + + @classmethod + def _missing_(cls, value): + logger.warning("Unknown value '%s' in %s", value, cls.__name__) + return cls.UNKNOWN + + UNKNOWN = UNKNOWN_STRING + """Unknown values in the enum are mapped to UNKNOWN""" + + @unique class Straight(Enum): ON = "On" @@ -468,3 +509,36 @@ def _missing_(cls, value): UNKNOWN = UNKNOWN_STRING """Unknown values in the enum are mapped to UNKNOWN""" + + +@unique +class ZoneBAvail(str, Enum): + NOT_CONNECTED = "Not Connected" + NOT_READY = "Not Ready" + READY = "Ready" + + @classmethod + def _missing_(cls, value): + logger.warning("Unknown value '%s' in %s", value, cls.__name__) + return cls.UNKNOWN + + UNKNOWN = UNKNOWN_STRING + """Unknown values in the enum are mapped to UNKNOWN""" + + +@unique +class ZoneBMute(str, Enum): + # TODO: Check if all are supported on ZoneB + ON = "On" + ATT_MINUS_20 = "Att -20 dB" + ATT_MINUS_40 = "Att -40 dB" + OFF = "Off" + + @classmethod + def _missing_(cls, value): + logger.warning("Unknown value '%s' in %s", value, cls.__name__) + return cls.UNKNOWN + + UNKNOWN = UNKNOWN_STRING + """Unknown values in the enum are mapped to UNKNOWN""" + diff --git a/ynca/subunits/zone.py b/ynca/subunits/zone.py index 5e01e38..614119e 100644 --- a/ynca/subunits/zone.py +++ b/ynca/subunits/zone.py @@ -23,11 +23,16 @@ Enhancer, PureDirMode, Pwr, + PwrB, Sleep, SoundPrg, + SpeakerA, + SpeakerB, Straight, ThreeDeeCinema, TwoChDecoder, + ZoneBAvail, + ZoneBMute, ) from ..helpers import number_to_string_with_stepsize from ..subunit import SubunitBase @@ -178,6 +183,22 @@ def vol_down(self, step_size: float = 0.5): class Main(ZoneBase): id = Subunit.MAIN + # ZoneA/B only exists as "subzones" on the main subunit + + speakera = EnumFunctionMixin[SpeakerA](SpeakerA, init="BASIC") + speakerb = EnumFunctionMixin[SpeakerB](SpeakerB, init="BASIC") + + pwrb = EnumFunctionMixin[PwrB](PwrB, init="BASIC") + + zonebavail = EnumFunctionMixin[ZoneBAvail](ZoneBAvail, init="BASIC") + zonebmute = EnumFunctionMixin[ZoneBMute](ZoneBMute, init="BASIC") + zonebvol = FloatFunctionMixin( + converter=FloatConverter( + to_str=lambda v: number_to_string_with_stepsize(v, 1, 0.5) + ), + init="BASIC", + ) + class Zone2(ZoneBase): id = Subunit.ZONE2 From b822164750a6d1844c0beae8fdba29b349db01db Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Wed, 21 Aug 2024 21:13:40 +0200 Subject: [PATCH 04/17] Add ZONEBVOL and PWRB handling to server --- ynca/server.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ynca/server.py b/ynca/server.py index 51150c7..b9acdef 100644 --- a/ynca/server.py +++ b/ynca/server.py @@ -228,7 +228,8 @@ def handle_put(self, subunit, function, value): if function == "MEM": return - if function == "VOL" and value.startswith("Up") or value.startswith("Down"): + # Assume ZONEBVOL is independant of VOL + if (function == "VOL" or function == "ZONEBVOL") and value.startswith("Up") or value.startswith("Down"): # Need to handle Up/Down as it would otherwise overwrite the VOL value wtih text Up/Down up = value.startswith("Up") @@ -269,6 +270,9 @@ def handle_put(self, subunit, function, value): result = self.store.put_data(zone, function, value) if result[1]: self.write_line(f"@{zone}:{function}={value}") + result = self.store.put_data(zone, "PWRB", value) + if result[1]: + self.write_line(f"@{zone}:PWRB={value}") elif subunit in ZONES: # Setting PWR on a ZONE can influence SYS overall PWR sys_is_on = False @@ -276,6 +280,11 @@ def handle_put(self, subunit, function, value): zone_is_on = self.store.get_data(zone, function) if zone_is_on != UNDEFINED: sys_is_on |= zone_is_on == "On" + + zoneb_is_on = self.store.get_data(zone, function) + if zoneb_is_on != UNDEFINED: + sys_is_on |= zoneb_is_on == "On" + sys_on_value = "On" if sys_is_on else "Standby" result = self.store.put_data("SYS", function, sys_on_value) if result[1]: From 5702bf47e4a4233b7d179be70bdfdbf5086ee1db Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Wed, 21 Aug 2024 21:39:46 +0200 Subject: [PATCH 05/17] Export ZoneB enums from package --- ynca/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ynca/__init__.py b/ynca/__init__.py index 0bf8ab1..0c03c46 100644 --- a/ynca/__init__.py +++ b/ynca/__init__.py @@ -30,13 +30,18 @@ PlaybackInfo, PureDirMode, Pwr, + PwrB, Repeat, Shuffle, Sleep, SoundPrg, + SpeakerA, + SpeakerB, Straight, ThreeDeeCinema, TwoChDecoder, + ZoneBAvail, + ZoneBMute, ) from .modelinfo import YncaModelInfo @@ -69,13 +74,18 @@ "PlaybackInfo", "PureDirMode", "Pwr", + "PwrB", "Repeat", "Shuffle", "Sleep", "SoundPrg", + "SpeakerA", + "SpeakerB", "Straight", "ThreeDeeCinema", "TwoChDecoder", + "ZoneBAvail", + "ZoneBMute", ] logging.getLogger(__name__).addHandler(logging.NullHandler()) From 8f2a37324b6845aef3ec454e48cb13050eaeb5da Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Wed, 21 Aug 2024 21:40:01 +0200 Subject: [PATCH 06/17] Implement zonebvol_up/down --- ynca/subunits/zone.py | 45 ++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/ynca/subunits/zone.py b/ynca/subunits/zone.py index 614119e..9f09f3f 100644 --- a/ynca/subunits/zone.py +++ b/ynca/subunits/zone.py @@ -44,6 +44,27 @@ def raiser(ex: Type[Exception]): raise ex +def do_vol_up(self, step_size: float, function: str): + """ + Increase the volume with given stepsize. + Supported stepsizes are: 0.5, 1, 2 and 5 + """ + value = "Up" + if step_size in [1, 2, 5]: + value = "Up {} dB".format(step_size) + self._put(function, value) + +def do_vol_down(self, step_size: float, function: str): + """ + Decrease the volume with given stepsize. + Supported stepsizes are: 0.5, 1, 2 and 5 + """ + value = "Down" + if step_size in [1, 2, 5]: + value = "Down {} dB".format(step_size) + self._put(function, value) + + class ZoneBase(PlaybackFunctionMixin, SubunitBase): @@ -160,24 +181,10 @@ def scene(self, scene_id: int | str): self._put("SCENE", f"Scene {scene_id}") def vol_up(self, step_size: float = 0.5): - """ - Increase the volume with given stepsize. - Supported stepsizes are: 0.5, 1, 2 and 5 - """ - value = "Up" - if step_size in [1, 2, 5]: - value = "Up {} dB".format(step_size) - self._put("VOL", value) + do_vol_up(self, step_size = 0.5, function="VOL") def vol_down(self, step_size: float = 0.5): - """ - Decrease the volume with given stepsize. - Supported stepsizes are: 0.5, 1, 2 and 5 - """ - value = "Down" - if step_size in [1, 2, 5]: - value = "Down {} dB".format(step_size) - self._put("VOL", value) + do_vol_down(self, step_size, function="VOL") class Main(ZoneBase): @@ -199,6 +206,12 @@ class Main(ZoneBase): init="BASIC", ) + def zonebvol_up(self, step_size: float = 0.5): + do_vol_up(self, step_size, function="ZONEBVOL") + + def zonebvol_down(self, step_size: float = 0.5): + do_vol_down(self, step_size, function="ZONEBVOL") + class Zone2(ZoneBase): id = Subunit.ZONE2 From 5959897c02e18cf1311199331cccf250c2461ab0 Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Sat, 24 Aug 2024 14:27:26 +0200 Subject: [PATCH 07/17] Improve comment in debug server --- ynca/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ynca/server.py b/ynca/server.py index b9acdef..9326eb1 100644 --- a/ynca/server.py +++ b/ynca/server.py @@ -224,7 +224,7 @@ def handle_put(self, subunit, function, value): return # MEM does not seem to generate a response - # assume it was supported for the subunit + # assume it was supported for the subunit, so no error message if function == "MEM": return From 8e203c4b770ba51375cee3fd7d23193a4e0a1fa4 Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Sun, 25 Aug 2024 15:40:59 +0200 Subject: [PATCH 08/17] Add ZONEB commands to all_commands_ever_seen --- docs/all_commands_ever_seen.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/all_commands_ever_seen.txt b/docs/all_commands_ever_seen.txt index ae080a7..c90193d 100644 --- a/docs/all_commands_ever_seen.txt +++ b/docs/all_commands_ever_seen.txt @@ -224,6 +224,7 @@ @MAIN:MUTE=? @MAIN:PUREDIRMODE=? @MAIN:PWR=? +@MAIN:PWRB=? @MAIN:SCENE10NAME=? @MAIN:SCENE11NAME=? @MAIN:SCENE12NAME=? @@ -240,6 +241,8 @@ @MAIN:SLEEP=? @MAIN:SOUNDPRG=? @MAIN:SPBASS=? +@MAIN:SPEAKERA=? +@MAIN:SPEAKERB=? @MAIN:SPTREBLE=? @MAIN:STRAIGHT=? @MAIN:SWFRTRIM=? @@ -251,6 +254,9 @@ @MAIN:TVAUDIN2=? @MAIN:VOL=? @MAIN:YPAOVOL=? +@MAIN:ZONEBAVAIL=? +@MAIN:ZONEBMUTE=? +@MAIN:ZONEBVOL=? @MAIN:ZONENAME=? ## ZONE2 From 23275507c3b2d9f491167b94f37d0640d0a4fe8f Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Sun, 25 Aug 2024 15:41:17 +0200 Subject: [PATCH 09/17] Add some note about Zone A/B receivers --- docs/PRACTICALITIES.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/PRACTICALITIES.md b/docs/PRACTICALITIES.md index 28a295b..3001044 100644 --- a/docs/PRACTICALITIES.md +++ b/docs/PRACTICALITIES.md @@ -123,3 +123,36 @@ Some receivers have a single audio input and that input is called AUDIO. Receive Seen on RX-V475 receiver in [issue #230](https://github.com/mvdwetering/yamaha_ynca/issues/230), but probably also applies to RX-V575/HTR-4066/HTR-5066 as they share the same firmware. For some reason this input is _not_ reported when requesting the input names with `@SYS:INPNAME=?` (unlike AUDIO1, AUDIO2 inputs). This makes it impossible to automatically detect if the input is supported by the receiver. The receiver does respond with `@RESTRICTED` when requesting `@SYS:INPNAMEAUDIO1=?` or `@SYS:TRIG1INPAUDIO1=?` instead of `@UNDEFINED`. However these responses are currently not really handled by the library and building support for that will be hard as there is not a guarenteed request/response mechanism due to the asynchronous nature of the protocol. + +## Zone A/B receivers + +There are receivers that have zones indicated as "A/B". These are different from the usual MAIN, ZONE2, ZONE3 and ZONE4 subunits. + +There seem to be 2 variations of it. + + +### Speakersets + +Zone A/B are just "speakersets" where A is the normal set of speakers and B is an additional set. These can be toggled on/off individually. These are part of the MAIN zone. + +On the API these are controlled with these functions `@MAIN:SPEAKERA` and `@MAIN:SPEAKERB`. + +e.g. RX-V573 + + +### Subzone + +An other variation seen on RX-V583 is a "subzone" called Zone B. In the AV Controller app it is shown similar to Zone 2. +This zone can be powered individually from the the MAIN zone (TO BE VERIFIED), but wil always have the same input as the MAIN zone. + +Reason for calling it a subzone is that it's functions are exposed on the MAIN subunit. + +On the API this subzone is controlled by the following functions. It is assumed they support same values as the MAIN zone. +``` +@MAIN:PWRB +@MAIN:ZONEBAVAIL +@MAIN:ZONEBMUTE +@MAIN:ZONEBVOL +``` + +Note that this variant also has the `@MAIN:SPEAKERA/B` functions. It is unclear why. From a76fac7716edddaa6937814388af785ee7de5e1e Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Mon, 26 Aug 2024 13:14:45 +0200 Subject: [PATCH 10/17] Remove Att values from ZoneB mute since not supported --- ynca/enums.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ynca/enums.py b/ynca/enums.py index a9089a5..6250c24 100644 --- a/ynca/enums.py +++ b/ynca/enums.py @@ -530,8 +530,6 @@ def _missing_(cls, value): class ZoneBMute(str, Enum): # TODO: Check if all are supported on ZoneB ON = "On" - ATT_MINUS_20 = "Att -20 dB" - ATT_MINUS_40 = "Att -40 dB" OFF = "Off" @classmethod From 4d0bce42141d9a942416738b8d988a6f4acbcd46 Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Mon, 26 Aug 2024 13:14:59 +0200 Subject: [PATCH 11/17] Add ZoneBName function --- ynca/subunits/zone.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ynca/subunits/zone.py b/ynca/subunits/zone.py index 9f09f3f..af4b48c 100644 --- a/ynca/subunits/zone.py +++ b/ynca/subunits/zone.py @@ -199,6 +199,7 @@ class Main(ZoneBase): zonebavail = EnumFunctionMixin[ZoneBAvail](ZoneBAvail, init="BASIC") zonebmute = EnumFunctionMixin[ZoneBMute](ZoneBMute, init="BASIC") + zonebname = StrFunctionMixin(converter=StrConverter(min_len=0, max_len=9)) zonebvol = FloatFunctionMixin( converter=FloatConverter( to_str=lambda v: number_to_string_with_stepsize(v, 1, 0.5) From 10ab79c6e04b68ef965b27408662e23ddabdb312 Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Mon, 26 Aug 2024 13:15:27 +0200 Subject: [PATCH 12/17] Add more findings to practicalities doc --- docs/PRACTICALITIES.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/PRACTICALITIES.md b/docs/PRACTICALITIES.md index 3001044..a8d66da 100644 --- a/docs/PRACTICALITIES.md +++ b/docs/PRACTICALITIES.md @@ -126,10 +126,11 @@ For some reason this input is _not_ reported when requesting the input names wit ## Zone A/B receivers -There are receivers that have zones indicated as "A/B". These are different from the usual MAIN, ZONE2, ZONE3 and ZONE4 subunits. +(this section is compiled from findings in https://github.com/mvdwetering/yamaha_ynca/issues/320) -There seem to be 2 variations of it. +There are receivers that have zones indicated as "A/B". These are different from the usual MAIN, ZONE2, ZONE3 and ZONE4 subunits. +There seem to be 2 variations. ### Speakersets @@ -143,16 +144,17 @@ e.g. RX-V573 ### Subzone An other variation seen on RX-V583 is a "subzone" called Zone B. In the AV Controller app it is shown similar to Zone 2. -This zone can be powered individually from the the MAIN zone (TO BE VERIFIED), but wil always have the same input as the MAIN zone. +This zone can be powered individually from the the MAIN zone, but wil always have the same input as the MAIN zone. Reason for calling it a subzone is that it's functions are exposed on the MAIN subunit. -On the API this subzone is controlled by the following functions. It is assumed they support same values as the MAIN zone. +On the API this subzone is controlled by the following functions. Note that Mute only supports On/Off ``` @MAIN:PWRB @MAIN:ZONEBAVAIL +@MAIN:ZONEBNAME # Unknown if this can actually be set on the AVR @MAIN:ZONEBMUTE @MAIN:ZONEBVOL ``` -Note that this variant also has the `@MAIN:SPEAKERA/B` functions. It is unclear why. +Note that this variant also has the `@MAIN:SPEAKERA/B` functions. But when controlling the SPEAKERB it will power on/off ZoneB (assumption is that it works the same for SPEAKERA/MAIN zone PWR). So when implementing a client these should probably be hidden/ignored. From 7a73e85e9b7deff95f1dc26c838c555c40c44188 Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Mon, 26 Aug 2024 13:17:19 +0200 Subject: [PATCH 13/17] Remove resolved comment --- ynca/enums.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ynca/enums.py b/ynca/enums.py index 6250c24..489f62d 100644 --- a/ynca/enums.py +++ b/ynca/enums.py @@ -528,7 +528,6 @@ def _missing_(cls, value): @unique class ZoneBMute(str, Enum): - # TODO: Check if all are supported on ZoneB ON = "On" OFF = "Off" From 08fcac1d1e1f89d7b6170c7f7c30566e4e1e17d3 Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Mon, 26 Aug 2024 13:48:29 +0200 Subject: [PATCH 14/17] Add tests for ZoneB and fix issues --- tests/test_enums.py | 11 ++++ tests/test_zone.py | 130 +++++++++++++++++++++++++++++++++++++++++- ynca/subunits/zone.py | 2 +- 3 files changed, 140 insertions(+), 3 deletions(-) diff --git a/tests/test_enums.py b/tests/test_enums.py index b1fb534..067c67e 100644 --- a/tests/test_enums.py +++ b/tests/test_enums.py @@ -21,13 +21,18 @@ PlaybackInfo, PureDirMode, Pwr, + PwrB, Repeat, Shuffle, Sleep, SoundPrg, + SpeakerA, + SpeakerB, Straight, ThreeDeeCinema, TwoChDecoder, + ZoneBAvail, + ZoneBMute, ) @@ -51,10 +56,16 @@ def test_invalid_values_on_enums(): assert PlaybackInfo("x") is PlaybackInfo.UNKNOWN assert PureDirMode("x") is PureDirMode.UNKNOWN assert Pwr("x") is Pwr.UNKNOWN + assert PwrB("x") is PwrB.UNKNOWN assert Repeat("x") is Repeat.UNKNOWN assert Shuffle("x") is Shuffle.UNKNOWN assert Sleep("x") is Sleep.UNKNOWN assert SoundPrg("x") is SoundPrg.UNKNOWN + assert SpeakerA("x") is SpeakerA.UNKNOWN + assert SpeakerB("x") is SpeakerB.UNKNOWN assert Straight("x") is Straight.UNKNOWN assert ThreeDeeCinema("x") is ThreeDeeCinema.UNKNOWN assert TwoChDecoder("x") is TwoChDecoder.UNKNOWN + assert ZoneBAvail("x") is ZoneBAvail.UNKNOWN + assert ZoneBMute("x") is ZoneBMute.UNKNOWN + diff --git a/tests/test_zone.py b/tests/test_zone.py index 5dbb5ba..7fe3465 100644 --- a/tests/test_zone.py +++ b/tests/test_zone.py @@ -10,14 +10,16 @@ Mute, PureDirMode, Pwr, + PwrB, SoundPrg, + SpeakerA, + SpeakerB, Straight, TwoChDecoder, + ZoneBMute, ) from ynca.subunits.zone import Main, ZoneBase -from .mock_yncaconnection import YncaConnectionMock - SYS = "SYS" SUBUNIT = "MAIN" NUM_SCENES = 12 @@ -33,9 +35,13 @@ (SUBUNIT, "BASIC"), [ (SUBUNIT, "PWR", "Standby"), + (SUBUNIT, "PWRB", "On"), (SUBUNIT, "SLEEP", "Off"), (SUBUNIT, "VOL", "-30.0"), (SUBUNIT, "MUTE", "Off"), + (SUBUNIT, "ZONEBAVAIL", "Ready"), + (SUBUNIT, "ZONEBVOL", "-20.5"), + (SUBUNIT, "ZONEBMUTE", "On"), (SUBUNIT, "INP", "HDMI1"), (SUBUNIT, "STRAIGHT", "Off"), (SUBUNIT, "ENHANCER", "On"), @@ -45,6 +51,8 @@ (SUBUNIT, "SPBASS", "4"), (SUBUNIT, "SPTREBLE", "-4"), (SUBUNIT, "ADAPTIVEDRC", "Off"), + (SUBUNIT, "SPEAKERA", "Off"), + (SUBUNIT, "SPEAKERB", "On"), ], ), ( @@ -76,6 +84,12 @@ (SUBUNIT, "2CHDECODER", "Dolby PLIIx Movie"), ], ), + ( + (SUBUNIT, "ZONEBNAME"), + [ + (SUBUNIT, "ZONEBNAME", "ZoneBName"), + ], + ), ( (SUBUNIT, "ZONENAME"), [ @@ -172,6 +186,13 @@ def test_initialize_full(connection, update_callback): for scene_id in range(1, NUM_SCENES + 1): assert getattr(z, f"scene{scene_id}name") == f"Scene name {scene_id}" + assert z.pwrb is PwrB.ON + assert z.zonebmute is ZoneBMute.ON + assert z.zonebname == "ZoneBName" + assert z.zonebvol == -20.5 + assert z.speakera is SpeakerA.OFF + assert z.speakerb is SpeakerB.ON + def test_mute(connection, initialized_zone: ZoneBase): # Writing to device @@ -195,6 +216,20 @@ def test_mute(connection, initialized_zone: ZoneBase): assert initialized_zone.mute is Mute.OFF +def test_zoneb_mute(connection, initialized_zone: Main): + # Writing to device + initialized_zone.zonebmute = ZoneBMute.ON + connection.put.assert_called_with(SUBUNIT, "ZONEBMUTE", "On") + initialized_zone.zonebmute = ZoneBMute.OFF + connection.put.assert_called_with(SUBUNIT, "ZONEBMUTE", "Off") + + # Updates from device + connection.send_protocol_message(SUBUNIT, "ZONEBMUTE", "On") + assert initialized_zone.zonebmute is ZoneBMute.ON + connection.send_protocol_message(SUBUNIT, "ZONEBMUTE", "Off") + assert initialized_zone.zonebmute is ZoneBMute.OFF + + def test_volume(connection, initialized_zone: ZoneBase): # Writing to device @@ -249,6 +284,60 @@ def test_volume(connection, initialized_zone: ZoneBase): assert initialized_zone.vol == -10 +def test_zoneb_volume(connection, initialized_zone: Main): + # Writing to device + + # Positive with step rounding + initialized_zone.zonebvol = 0 + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "0.0") + initialized_zone.zonebvol = 0.1 + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "0.0") + initialized_zone.zonebvol = 0.4 + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "0.5") + + # Negative with step rounding + initialized_zone.zonebvol = -5 + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "-5.0") + initialized_zone.zonebvol = -0.5 + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "-0.5") + initialized_zone.zonebvol = -0.4 + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "-0.5") + initialized_zone.zonebvol = -0.1 + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "0.0") + + # Up + initialized_zone.zonebvol_up() + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "Up") + initialized_zone.zonebvol_up(1) + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "Up 1 dB") + initialized_zone.zonebvol_up(2) + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "Up 2 dB") + initialized_zone.zonebvol_up(5) + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "Up 5 dB") + initialized_zone.zonebvol_up(50) + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "Up") + + # Down + initialized_zone.zonebvol_down() + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "Down") + initialized_zone.zonebvol_down(1) + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "Down 1 dB") + initialized_zone.zonebvol_down(2) + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "Down 2 dB") + initialized_zone.zonebvol_down(5) + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "Down 5 dB") + initialized_zone.zonebvol_down(50) + connection.put.assert_called_with(SUBUNIT, "ZONEBVOL", "Down") + + # Updates from device + connection.send_protocol_message(SUBUNIT, "ZONEBVOL", "0.0") + assert initialized_zone.zonebvol == 0 + connection.send_protocol_message(SUBUNIT, "ZONEBVOL", "10.0") + assert initialized_zone.zonebvol == 10 + connection.send_protocol_message(SUBUNIT, "ZONEBVOL", "-10.0") + assert initialized_zone.zonebvol == -10 + + def test_maxvol(connection, initialized_zone: ZoneBase): # Writing to device @@ -346,6 +435,18 @@ def test_zonename(connection, initialized_zone: ZoneBase): assert initialized_zone.zonename == "updated" +def test_zonebname(connection, initialized_zone: Main): + # Writing to device + initialized_zone.zonebname = "new name" + connection.put.assert_called_with(SUBUNIT, "ZONEBNAME", "new name") + with pytest.raises(ValueError): + initialized_zone.zonebname = "new name is too long" + + # Updates from device + connection.send_protocol_message(SUBUNIT, "ZONEBNAME", "updated") + assert initialized_zone.zonebname == "updated" + + def test_twochdecoder(connection, initialized_zone: ZoneBase): # Writing to device initialized_zone.twochdecoder = TwoChDecoder.DolbyPl @@ -447,3 +548,28 @@ def test_lipsynchdmioutoffset(connection, initialized_zone: ZoneBase): assert initialized_zone.lipsynchdmiout2offset == 250 connection.send_protocol_message(SUBUNIT, "LIPSYNCHDMIOUT2OFFSET", "-250") assert initialized_zone.lipsynchdmiout2offset == -250 + +def test_speaker_ab(connection, initialized_zone: Main): + # Writing to device + initialized_zone.speakera = SpeakerA.ON + connection.put.assert_called_with(SUBUNIT, "SPEAKERA", "On") + initialized_zone.speakera = SpeakerA.OFF + connection.put.assert_called_with(SUBUNIT, "SPEAKERA", "Off") + + # Updates from device + connection.send_protocol_message(SUBUNIT, "SPEAKERA", "On") + assert initialized_zone.speakera is SpeakerA.ON + connection.send_protocol_message(SUBUNIT, "SPEAKERA", "Off") + assert initialized_zone.speakera is SpeakerA.OFF + + # Writing to device + initialized_zone.speakerb = SpeakerB.ON + connection.put.assert_called_with(SUBUNIT, "SPEAKERB", "On") + initialized_zone.speakerb = SpeakerB.OFF + connection.put.assert_called_with(SUBUNIT, "SPEAKERB", "Off") + + # Updates from device + connection.send_protocol_message(SUBUNIT, "SPEAKERB", "On") + assert initialized_zone.speakerb is SpeakerB.ON + connection.send_protocol_message(SUBUNIT, "SPEAKERB", "Off") + assert initialized_zone.speakerb is SpeakerB.OFF diff --git a/ynca/subunits/zone.py b/ynca/subunits/zone.py index af4b48c..ac79f31 100644 --- a/ynca/subunits/zone.py +++ b/ynca/subunits/zone.py @@ -181,7 +181,7 @@ def scene(self, scene_id: int | str): self._put("SCENE", f"Scene {scene_id}") def vol_up(self, step_size: float = 0.5): - do_vol_up(self, step_size = 0.5, function="VOL") + do_vol_up(self, step_size = step_size, function="VOL") def vol_down(self, step_size: float = 0.5): do_vol_down(self, step_size, function="VOL") From add78713620ac25af1288b084d3c8c24d7ed650b Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Mon, 26 Aug 2024 15:14:15 +0200 Subject: [PATCH 15/17] Remove Speaker A/B from BASIC initialization --- ynca/subunits/zone.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ynca/subunits/zone.py b/ynca/subunits/zone.py index ac79f31..bca2230 100644 --- a/ynca/subunits/zone.py +++ b/ynca/subunits/zone.py @@ -192,8 +192,9 @@ class Main(ZoneBase): # ZoneA/B only exists as "subzones" on the main subunit - speakera = EnumFunctionMixin[SpeakerA](SpeakerA, init="BASIC") - speakerb = EnumFunctionMixin[SpeakerB](SpeakerB, init="BASIC") + # Speaker A/B are in BASIC on RX-V583, but are not on RX-V573 it seems + speakera = EnumFunctionMixin[SpeakerA](SpeakerA) #, init="BASIC") + speakerb = EnumFunctionMixin[SpeakerB](SpeakerB) #, init="BASIC") pwrb = EnumFunctionMixin[PwrB](PwrB, init="BASIC") From 931b824afcf62004733da4d07b08cbf393995d13 Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Mon, 26 Aug 2024 15:44:54 +0200 Subject: [PATCH 16/17] Fix typos --- docs/PRACTICALITIES.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/PRACTICALITIES.md b/docs/PRACTICALITIES.md index a8d66da..46952b2 100644 --- a/docs/PRACTICALITIES.md +++ b/docs/PRACTICALITIES.md @@ -136,19 +136,19 @@ There seem to be 2 variations. Zone A/B are just "speakersets" where A is the normal set of speakers and B is an additional set. These can be toggled on/off individually. These are part of the MAIN zone. -On the API these are controlled with these functions `@MAIN:SPEAKERA` and `@MAIN:SPEAKERB`. +On the API, these are controlled with these functions `@MAIN:SPEAKERA` and `@MAIN:SPEAKERB`. e.g. RX-V573 ### Subzone -An other variation seen on RX-V583 is a "subzone" called Zone B. In the AV Controller app it is shown similar to Zone 2. -This zone can be powered individually from the the MAIN zone, but wil always have the same input as the MAIN zone. +Another variation seen on RX-V583 is a "subzone" called Zone B. In the AV Controller app it is shown similar to Zone 2. +This zone can be powered individually from the MAIN zone, but wil always have the same input as the MAIN zone. -Reason for calling it a subzone is that it's functions are exposed on the MAIN subunit. +Reason for calling it a subzone is that its functions are exposed on the MAIN subunit. -On the API this subzone is controlled by the following functions. Note that Mute only supports On/Off +On the API, this subzone is controlled by the following functions. Note that Mute only supports On/Off ``` @MAIN:PWRB @MAIN:ZONEBAVAIL From 251ec2166d3406b04568c64e90110e061b33640a Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Mon, 26 Aug 2024 15:48:53 +0200 Subject: [PATCH 17/17] More typos --- docs/PRACTICALITIES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/PRACTICALITIES.md b/docs/PRACTICALITIES.md index 46952b2..e3e672a 100644 --- a/docs/PRACTICALITIES.md +++ b/docs/PRACTICALITIES.md @@ -144,7 +144,7 @@ e.g. RX-V573 ### Subzone Another variation seen on RX-V583 is a "subzone" called Zone B. In the AV Controller app it is shown similar to Zone 2. -This zone can be powered individually from the MAIN zone, but wil always have the same input as the MAIN zone. +This zone can be powered individually from the MAIN zone, but will always have the same input as the MAIN zone. Reason for calling it a subzone is that its functions are exposed on the MAIN subunit.