From 197c8fcced88198957eb8bc1dcaef0af0f928d04 Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Wed, 6 Nov 2024 11:02:13 +0100 Subject: [PATCH 1/4] Add support for DIRMODE --- docs/PRACTICALITIES.md | 14 +++++++++++++ tests/test_enums.py | 2 ++ tests/test_zone.py | 17 ++++++++++++++++ ynca/__init__.py | 2 ++ ynca/enums.py | 12 +++++++++++ ynca/server.py | 1 + ynca/subunits/zone.py | 46 +++++++++++++++++++++++++----------------- 7 files changed, 76 insertions(+), 18 deletions(-) diff --git a/docs/PRACTICALITIES.md b/docs/PRACTICALITIES.md index 9a134af..83e8cbb 100644 --- a/docs/PRACTICALITIES.md +++ b/docs/PRACTICALITIES.md @@ -165,3 +165,17 @@ However the set of values that comes back is not stable between receivers. Initially it was assumed that it would always respond with all supported features. But at least for PUREDIRMODE this is not the case. It is supported on RX-V1067, but not part of BASIC response. + +## Pure Direct mode + +There seem to be 2 ways to control PureDirect mode. +Both ways seem to support `On` and `Off`. Unclear why there are 2 ways or to which models it applies. + +``` +@MAIN:PUREDIRMODE=? e.g. RX-A810 +@MAIN:DIRMODE=? e.g. RX-V473 +``` + +Both seem to be part of BASIC + +This was brought up in https://github.com/mvdwetering/yamaha_ynca/discussions/340 diff --git a/tests/test_enums.py b/tests/test_enums.py index 067c67e..43cc80e 100644 --- a/tests/test_enums.py +++ b/tests/test_enums.py @@ -7,6 +7,7 @@ BandDab, BandTun, DabPreset, + DirMode, Enhancer, FmPreset, HdmiOut, @@ -43,6 +44,7 @@ def test_invalid_values_on_enums(): assert BandTun("x") is BandTun.UNKNOWN assert DabPreset("x") is DabPreset.UNKNOWN assert Enhancer("x") is Enhancer.UNKNOWN + assert DirMode("x") is DirMode.UNKNOWN assert FmPreset("x") is FmPreset.UNKNOWN assert HdmiOut("x") is HdmiOut.UNKNOWN assert HdmiOutOnOff("x") is HdmiOutOnOff.UNKNOWN diff --git a/tests/test_zone.py b/tests/test_zone.py index 7fe3465..b6dba83 100644 --- a/tests/test_zone.py +++ b/tests/test_zone.py @@ -8,6 +8,7 @@ InitVolMode, Input, Mute, + DirMode, PureDirMode, Pwr, PwrB, @@ -53,6 +54,7 @@ (SUBUNIT, "ADAPTIVEDRC", "Off"), (SUBUNIT, "SPEAKERA", "Off"), (SUBUNIT, "SPEAKERB", "On"), + (SUBUNIT, "DIRMODE", "On"), ], ), ( @@ -159,6 +161,7 @@ def test_initialize_minimal(connection, update_callback): assert z.soundprg is None assert z.twochdecoder is None assert z.puredirmode is None + assert z.dirmode is None for scene_id in range(1, NUM_SCENES + 1): assert getattr(z, f"scene{scene_id}name") is None @@ -182,6 +185,7 @@ def test_initialize_full(connection, update_callback): assert z.zonename == "ZoneName" assert z.twochdecoder is TwoChDecoder.DolbyPl2xMovie assert z.puredirmode is PureDirMode.OFF + assert z.dirmode is DirMode.ON for scene_id in range(1, NUM_SCENES + 1): assert getattr(z, f"scene{scene_id}name") == f"Scene name {scene_id}" @@ -474,6 +478,19 @@ def test_puredirmode(connection, initialized_zone: ZoneBase): connection.send_protocol_message(SUBUNIT, "PUREDIRMODE", "Off") assert initialized_zone.puredirmode == PureDirMode.OFF +def test_dirmode(connection, initialized_zone: ZoneBase): + # Writing to device + initialized_zone.dirmode = DirMode.ON + connection.put.assert_called_with(SUBUNIT, "DIRMODE", "On") + initialized_zone.dirmode = DirMode.OFF + connection.put.assert_called_with(SUBUNIT, "DIRMODE", "Off") + + # Updates from device + connection.send_protocol_message(SUBUNIT, "DIRMODE", "On") + assert initialized_zone.dirmode == DirMode.ON + connection.send_protocol_message(SUBUNIT, "DIRMODE", "Off") + assert initialized_zone.dirmode == DirMode.OFF + def test_initvolmode(connection, initialized_zone: ZoneBase): # Writing to device diff --git a/ynca/__init__.py b/ynca/__init__.py index 0c03c46..73f85de 100644 --- a/ynca/__init__.py +++ b/ynca/__init__.py @@ -16,6 +16,7 @@ BandDab, BandTun, DabPreset, + DirMode, Enhancer, FmPreset, HdmiOut, @@ -60,6 +61,7 @@ "BandDab", "BandTun", "DabPreset", + "DirMode", "Enhancer", "FmPreset", "HdmiOut", diff --git a/ynca/enums.py b/ynca/enums.py index 12f497c..75b6cac 100644 --- a/ynca/enums.py +++ b/ynca/enums.py @@ -77,6 +77,18 @@ def _missing_(cls, value): UNKNOWN = UNKNOWN_STRING """Unknown values in the enum are mapped to UNKNOWN""" +@unique +class DirMode(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 Enhancer(str, Enum): diff --git a/ynca/server.py b/ynca/server.py index 9326eb1..7cebfa2 100644 --- a/ynca/server.py +++ b/ynca/server.py @@ -119,6 +119,7 @@ def put_data(self, subunit, function, new_value): "DTSDIALOGUECONTROL", "SPEAKERA", "SPEAKERB", + "DIRMODE", ], "METAINFO": ["ARTIST", "ALBUM", "SONG", "TRACK", "CHNAME"], "RDSINFO": ["RDSPRGTYPE", "RDSPRGSERVICE", "RDSTXTA", "RDSTXTB", "RDSCLOCK"], diff --git a/ynca/subunits/zone.py b/ynca/subunits/zone.py index b92747b..9ae70ca 100644 --- a/ynca/subunits/zone.py +++ b/ynca/subunits/zone.py @@ -5,22 +5,15 @@ from ..constants import Subunit from ..converters import EnumConverter, FloatConverter, MultiConverter, StrConverter -from ..function import ( - Cmd, - EnumFunctionMixin, - EnumOrFloatFunctionMixin, - FloatFunctionMixin, - IntFunctionMixin, - StrFunctionMixin, -) from ..enums import ( + AdaptiveDrc, + DirMode, + Enhancer, HdmiOut, InitVolLvl, InitVolMode, Input, Mute, - AdaptiveDrc, - Enhancer, PureDirMode, Pwr, PwrB, @@ -34,6 +27,14 @@ ZoneBAvail, ZoneBMute, ) +from ..function import ( + Cmd, + EnumFunctionMixin, + EnumOrFloatFunctionMixin, + FloatFunctionMixin, + IntFunctionMixin, + StrFunctionMixin, +) from ..helpers import number_to_string_with_stepsize from ..subunit import SubunitBase from . import PlaybackFunctionMixin @@ -44,6 +45,7 @@ def raiser(ex: Type[Exception]): raise ex + def do_vol_up(self, step_size: float, function: str): """ Increase the volume with given stepsize. @@ -54,6 +56,7 @@ def do_vol_up(self, step_size: float, function: str): 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. @@ -65,13 +68,13 @@ def do_vol_down(self, step_size: float, function: str): self._put(function, value) - class ZoneBase(PlaybackFunctionMixin, SubunitBase): # BASIC gets a lot of attribute like PWR, SLEEP, VOL, MUTE, INP, STRAIGHT, ENHANCER, SOUNDPRG and more # Use it to significantly reduce the amount of commands to send adaptivedrc = EnumFunctionMixin[AdaptiveDrc](AdaptiveDrc) + dirmode = EnumFunctionMixin[DirMode](DirMode, init="BASIC") enhancer = EnumFunctionMixin[Enhancer](Enhancer) hdmiout = EnumFunctionMixin[HdmiOut](HdmiOut) hpbass = FloatFunctionMixin( @@ -99,29 +102,32 @@ class ZoneBase(PlaybackFunctionMixin, SubunitBase): inp = EnumFunctionMixin[Input](Input, init="BASIC") lipsynchdmiout1offset = IntFunctionMixin() + def lipsynchdmiout1offset_down(self): """ Increase by 1 step (=1 ms). """ self._put("LIPSYNCHDMIOUT1OFFSET", "Down") + def lipsynchdmiout1offset_up(self): """ Decrease by 1 step (=1 ms). """ self._put("LIPSYNCHDMIOUT1OFFSET", "Up") - + lipsynchdmiout2offset = IntFunctionMixin() + def lipsynchdmiout2offset_down(self): """ Increase by 1 step (=1 ms). """ self._put("LIPSYNCHDMIOUT2OFFSET", "Down") + def lipsynchdmiout2offset_up(self): """ Decrease by 1 step (=1 ms). """ self._put("LIPSYNCHDMIOUT2OFFSET", "Up") - maxvol = FloatFunctionMixin( converter=MultiConverter( @@ -137,7 +143,9 @@ def lipsynchdmiout2offset_up(self): ), ) mute = EnumFunctionMixin[Mute](Mute, init="BASIC") - puredirmode = EnumFunctionMixin[PureDirMode](PureDirMode) # Not part of BASIC on RX-V1067 + puredirmode = EnumFunctionMixin[PureDirMode]( + PureDirMode + ) # , init="BASIC") # Not in BASIC on RX-V1067 pwr = EnumFunctionMixin[Pwr](Pwr, init="BASIC") scene1name = StrFunctionMixin(Cmd.GET, init="SCENENAME") scene2name = StrFunctionMixin(Cmd.GET, init="SCENENAME") @@ -167,7 +175,9 @@ def lipsynchdmiout2offset_up(self): threedcinema = EnumFunctionMixin[ThreeDeeCinema]( ThreeDeeCinema, name_override="3DCINEMA" ) - twochdecoder = EnumFunctionMixin[TwoChDecoder](TwoChDecoder, name_override="2CHDECODER") + twochdecoder = EnumFunctionMixin[TwoChDecoder]( + TwoChDecoder, name_override="2CHDECODER" + ) vol = FloatFunctionMixin( converter=FloatConverter( to_str=lambda v: number_to_string_with_stepsize(v, 1, 0.5) @@ -181,7 +191,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 = step_size, 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") @@ -193,8 +203,8 @@ class Main(ZoneBase): # ZoneA/B only exists as "subzones" on the main subunit # 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") + speakera = EnumFunctionMixin[SpeakerA](SpeakerA) # , init="BASIC") + speakerb = EnumFunctionMixin[SpeakerB](SpeakerB) # , init="BASIC") pwrb = EnumFunctionMixin[PwrB](PwrB, init="BASIC") From bcfd8b5a7660cfdfd18c1fcb2a61fff15ec922ba Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Wed, 6 Nov 2024 16:19:25 +0100 Subject: [PATCH 2/4] Clarify BASIC for pure direct --- docs/PRACTICALITIES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/PRACTICALITIES.md b/docs/PRACTICALITIES.md index 83e8cbb..3f86cbb 100644 --- a/docs/PRACTICALITIES.md +++ b/docs/PRACTICALITIES.md @@ -176,6 +176,6 @@ Both ways seem to support `On` and `Off`. Unclear why there are 2 ways or to whi @MAIN:DIRMODE=? e.g. RX-V473 ``` -Both seem to be part of BASIC +Both seem to be part of BASIC. Except for RX-V1067 wher PUREDIRMODE is supportec, but not in BASIC. This was brought up in https://github.com/mvdwetering/yamaha_ynca/discussions/340 From d6e7e9c41b83f116fb7a3a9608230f35b7bdcce3 Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Wed, 6 Nov 2024 16:31:50 +0100 Subject: [PATCH 3/4] Review tweaks --- docs/PRACTICALITIES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/PRACTICALITIES.md b/docs/PRACTICALITIES.md index 3f86cbb..6cad3f2 100644 --- a/docs/PRACTICALITIES.md +++ b/docs/PRACTICALITIES.md @@ -169,7 +169,7 @@ But at least for PUREDIRMODE this is not the case. It is supported on RX-V1067, ## Pure Direct mode There seem to be 2 ways to control PureDirect mode. -Both ways seem to support `On` and `Off`. Unclear why there are 2 ways or to which models it applies. +Both ways seem to support `On` and `Off`. Unclear why there are 2 ways, or to which models it applies. ``` @MAIN:PUREDIRMODE=? e.g. RX-A810 From 441628b2d57f8832fd8b9c4d2da03b2a31bed967 Mon Sep 17 00:00:00 2001 From: Michel van de Wetering Date: Wed, 6 Nov 2024 16:44:14 +0100 Subject: [PATCH 4/4] More tweaks due to review --- docs/PRACTICALITIES.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/PRACTICALITIES.md b/docs/PRACTICALITIES.md index 6cad3f2..446c97e 100644 --- a/docs/PRACTICALITIES.md +++ b/docs/PRACTICALITIES.md @@ -166,16 +166,18 @@ However the set of values that comes back is not stable between receivers. Initially it was assumed that it would always respond with all supported features. But at least for PUREDIRMODE this is not the case. It is supported on RX-V1067, but not part of BASIC response. -## Pure Direct mode +## (Pure) Direct mode -There seem to be 2 ways to control PureDirect mode. -Both ways seem to support `On` and `Off`. Unclear why there are 2 ways, or to which models it applies. +Turns out there are 2 direct modes; Direct and Pure Direct. + +These are slightly different. Pure Direct bypasses more signal processing than Direct does. +It seems Pure Direct is an evolution of Direct because Direct seems to apply to older/lower end models. + +Both commands support `On` and `Off`. ``` @MAIN:PUREDIRMODE=? e.g. RX-A810 @MAIN:DIRMODE=? e.g. RX-V473 ``` -Both seem to be part of BASIC. Except for RX-V1067 wher PUREDIRMODE is supportec, but not in BASIC. - -This was brought up in https://github.com/mvdwetering/yamaha_ynca/discussions/340 +Both seem to be part of BASIC. Except for RX-V1067 where PUREDIRMODE is supported, but not in BASIC.