diff --git a/docs/all_commands_ever_seen.txt b/docs/all_commands_ever_seen.txt index 401953f..ae080a7 100644 --- a/docs/all_commands_ever_seen.txt +++ b/docs/all_commands_ever_seen.txt @@ -763,6 +763,7 @@ @PC:METAINFO=? @PC:PLAYBACK=? @PC:PLAYBACKINFO=? +@PC:PRESET=? @PC:REPEAT=? @PC:SHUFFLE=? @PC:STATION=? diff --git a/tests/test_tun.py b/tests/test_tun.py index 0197f57..553d36b 100644 --- a/tests/test_tun.py +++ b/tests/test_tun.py @@ -31,6 +31,12 @@ (SUBUNIT, "FMFREQ", "101.60"), ], ), + ( + (SUBUNIT, "PRESET"), + [ + (SUBUNIT, "PRESET", "12"), + ], + ), ( (SUBUNIT, "RDSINFO"), [ @@ -70,6 +76,7 @@ def test_initialize(connection, update_callback): assert tun.band is BandTun.FM assert tun.amfreq == 1080 assert tun.fmfreq == 101.60 + assert tun.preset == 12 def test_am(connection, initialized_tun: Tun): @@ -106,3 +113,32 @@ def test_rds(connection, initialized_tun: Tun): connection.send_protocol_message(SUBUNIT, "RDSTXTB", "radiotext b") assert initialized_tun.rdstxtb == "radiotext b" + +def test_preset(connection, initialized_tun: Tun): + + # Updates from device + connection.send_protocol_message(SUBUNIT, "PRESET", "11") + assert initialized_tun.preset == 11 + + connection.send_protocol_message(SUBUNIT, "PRESET", "No Preset") + assert initialized_tun.preset == None + + # Set preset + initialized_tun.preset = 10 + connection.put.assert_called_with(SUBUNIT, "PRESET", "10") + + # Preset Up Down + initialized_tun.preset_up() + connection.put.assert_called_with(SUBUNIT, "PRESET", "Up") + + initialized_tun.preset_down() + connection.put.assert_called_with(SUBUNIT, "PRESET", "Down") + +def test_mem(connection, initialized_tun: Tun): + + # Store + initialized_tun.mem(10) + connection.put.assert_called_with(SUBUNIT, "MEM", "10") + + initialized_tun.mem() + connection.put.assert_called_with(SUBUNIT, "MEM", "Auto") \ No newline at end of file diff --git a/ynca/converters.py b/ynca/converters.py index 4e96f37..61207c4 100644 --- a/ynca/converters.py +++ b/ynca/converters.py @@ -45,6 +45,23 @@ def to_str(self, value: int) -> str: return self._to_str(value) +class IntOrNoneConverter(ConverterBase): + + def __init__(self, to_str: Callable[[int], str] | None = None) -> None: + self._to_str = to_str or str + + def to_value(self, value_string: str) -> int | None: + try: + return int(value_string) + except ValueError: + return None + + def to_str(self, value: int) -> str: + # Make sure it is an int compatible types to be usable with MultiConverter + int(value) + return self._to_str(value) + + class FloatConverter(ConverterBase): def __init__(self, to_str: Callable[[float], str] | None = None) -> None: self._to_str = to_str or str diff --git a/ynca/subunits/__init__.py b/ynca/subunits/__init__.py index 892549a..051be96 100644 --- a/ynca/subunits/__init__.py +++ b/ynca/subunits/__init__.py @@ -1,7 +1,13 @@ from __future__ import annotations -from ..converters import FloatConverter -from ..function import Cmd, EnumFunctionMixin, FloatFunctionMixin, StrFunctionMixin +from ..converters import FloatConverter, IntOrNoneConverter +from ..function import ( + Cmd, + EnumFunctionMixin, + FloatFunctionMixin, + IntFunctionMixin, + StrFunctionMixin, +) from ..enums import Playback, PlaybackInfo, Repeat, Shuffle from ..helpers import number_to_string_with_stepsize @@ -27,6 +33,12 @@ class FmFreqFunctionMixin: """Read/write FM frequency. Values will be aligned to a valid stepsize.""" +class MemFunctionMixin: + def mem(self, parameter: int | None = None): + """Store preset in memory slot, parameter is a slot number 1-40 or None to select a slot automatically.""" + self._put("MEM", "Auto" if parameter is None else str(parameter)) # type: ignore + + class PlaybackFunctionMixin: def playback(self, parameter: Playback): """Change playback state""" @@ -37,6 +49,21 @@ class PlaybackInfoFunctionMixin: playbackinfo = EnumFunctionMixin[PlaybackInfo](PlaybackInfo, Cmd.GET) +class PresetFunctionMixin: + preset = IntFunctionMixin(converter=IntOrNoneConverter()) + """Activate or read preset. Note that only TUN and SIRIUS seem to support GET.""" + + +class PresetUpDownFunctionMixin: + def preset_up(self): + """Select next available preset""" + self._put("PRESET", "Up") # type: ignore + + def preset_down(self): + """Select previous available preset""" + self._put("PRESET", "Down") # type: ignore + + class RepeatFunctionMixin: repeat = EnumFunctionMixin[Repeat](Repeat) diff --git a/ynca/subunits/napster.py b/ynca/subunits/napster.py index 67beec9..ff344bc 100644 --- a/ynca/subunits/napster.py +++ b/ynca/subunits/napster.py @@ -5,8 +5,10 @@ from . import ( AlbumFunctionMixin, ArtistFunctionMixin, + MemFunctionMixin, PlaybackFunctionMixin, PlaybackInfoFunctionMixin, + PresetFunctionMixin, RepeatFunctionMixin, ShuffleFunctionMixin, SongFunctionMixin, @@ -16,8 +18,10 @@ class Napster( AlbumFunctionMixin, ArtistFunctionMixin, + MemFunctionMixin, PlaybackFunctionMixin, PlaybackInfoFunctionMixin, + PresetFunctionMixin, RepeatFunctionMixin, ShuffleFunctionMixin, SongFunctionMixin, diff --git a/ynca/subunits/netradio.py b/ynca/subunits/netradio.py index 2eb9225..ee9fb85 100644 --- a/ynca/subunits/netradio.py +++ b/ynca/subunits/netradio.py @@ -2,13 +2,23 @@ from ..constants import Subunit from ..subunit import SubunitBase -from . import AlbumFunctionMixin, PlaybackFunctionMixin, PlaybackInfoFunctionMixin, SongFunctionMixin, StationFunctionMixin +from . import ( + AlbumFunctionMixin, + MemFunctionMixin, + PlaybackFunctionMixin, + PlaybackInfoFunctionMixin, + PresetFunctionMixin, + SongFunctionMixin, + StationFunctionMixin, +) class NetRadio( AlbumFunctionMixin, + MemFunctionMixin, PlaybackFunctionMixin, PlaybackInfoFunctionMixin, + PresetFunctionMixin, SongFunctionMixin, StationFunctionMixin, SubunitBase, diff --git a/ynca/subunits/pandora.py b/ynca/subunits/pandora.py index fa5adf7..3958c00 100644 --- a/ynca/subunits/pandora.py +++ b/ynca/subunits/pandora.py @@ -5,8 +5,10 @@ from . import ( AlbumFunctionMixin, ArtistFunctionMixin, + MemFunctionMixin, PlaybackFunctionMixin, PlaybackInfoFunctionMixin, + PresetFunctionMixin, SongFunctionMixin, StationFunctionMixin, TrackFunctionMixin, @@ -16,8 +18,10 @@ class Pandora( AlbumFunctionMixin, ArtistFunctionMixin, + MemFunctionMixin, PlaybackFunctionMixin, PlaybackInfoFunctionMixin, + PresetFunctionMixin, SongFunctionMixin, StationFunctionMixin, TrackFunctionMixin, # Pandora seems to use TRACK or SONG for title based on logs. Maybe depends on firmware version? diff --git a/ynca/subunits/pc.py b/ynca/subunits/pc.py index 1cb6698..d760a41 100644 --- a/ynca/subunits/pc.py +++ b/ynca/subunits/pc.py @@ -5,8 +5,10 @@ from . import ( AlbumFunctionMixin, ArtistFunctionMixin, + MemFunctionMixin, PlaybackFunctionMixin, PlaybackInfoFunctionMixin, + PresetFunctionMixin, RepeatFunctionMixin, ShuffleFunctionMixin, SongFunctionMixin, @@ -16,8 +18,10 @@ class Pc( AlbumFunctionMixin, ArtistFunctionMixin, + MemFunctionMixin, PlaybackFunctionMixin, PlaybackInfoFunctionMixin, + PresetFunctionMixin, RepeatFunctionMixin, ShuffleFunctionMixin, SongFunctionMixin, diff --git a/ynca/subunits/rhap.py b/ynca/subunits/rhap.py index fdc93f4..5f7f24c 100644 --- a/ynca/subunits/rhap.py +++ b/ynca/subunits/rhap.py @@ -5,8 +5,10 @@ from . import ( AlbumFunctionMixin, ArtistFunctionMixin, + MemFunctionMixin, PlaybackFunctionMixin, PlaybackInfoFunctionMixin, + PresetFunctionMixin, RepeatFunctionMixin, ShuffleFunctionMixin, SongFunctionMixin, @@ -16,8 +18,10 @@ class Rhap( AlbumFunctionMixin, ArtistFunctionMixin, + MemFunctionMixin, PlaybackFunctionMixin, PlaybackInfoFunctionMixin, + PresetFunctionMixin, RepeatFunctionMixin, ShuffleFunctionMixin, SongFunctionMixin, diff --git a/ynca/subunits/sirius.py b/ynca/subunits/sirius.py index 4dd8d03..c4ef37c 100644 --- a/ynca/subunits/sirius.py +++ b/ynca/subunits/sirius.py @@ -5,8 +5,11 @@ from . import ( ArtistFunctionMixin, ChNameFunctionMixin, + MemFunctionMixin, PlaybackFunctionMixin, PlaybackInfoFunctionMixin, + PresetFunctionMixin, + PresetUpDownFunctionMixin, SongFunctionMixin, ) @@ -14,6 +17,9 @@ class Sirius( ArtistFunctionMixin, ChNameFunctionMixin, + MemFunctionMixin, + PresetFunctionMixin, + PresetUpDownFunctionMixin, SongFunctionMixin, SubunitBase, ): @@ -23,8 +29,10 @@ class Sirius( class SiriusIr( ArtistFunctionMixin, ChNameFunctionMixin, + MemFunctionMixin, PlaybackFunctionMixin, PlaybackInfoFunctionMixin, + PresetFunctionMixin, SongFunctionMixin, SubunitBase, ): diff --git a/ynca/subunits/tun.py b/ynca/subunits/tun.py index 4d53806..1a9d47a 100644 --- a/ynca/subunits/tun.py +++ b/ynca/subunits/tun.py @@ -6,14 +6,23 @@ from ..enums import BandTun from ..helpers import number_to_string_with_stepsize from ..subunit import SubunitBase -from . import FmFreqFunctionMixin +from . import ( + FmFreqFunctionMixin, + MemFunctionMixin, + PresetFunctionMixin, + PresetUpDownFunctionMixin, +) -class Tun(FmFreqFunctionMixin, SubunitBase): +class Tun( + FmFreqFunctionMixin, + MemFunctionMixin, + PresetFunctionMixin, + PresetUpDownFunctionMixin, + SubunitBase, +): id = Subunit.TUN - band = EnumFunctionMixin[BandTun](BandTun) - amfreq = IntFunctionMixin( converter=IntConverter( to_str=lambda v: number_to_string_with_stepsize(v, 0, 10) @@ -21,6 +30,8 @@ class Tun(FmFreqFunctionMixin, SubunitBase): ) """Read/write AM frequency. Values will be aligned to a valid stepsize.""" + band = EnumFunctionMixin[BandTun](BandTun) + rdsprgservice = StrFunctionMixin(Cmd.GET, init="RDSINFO") rdsprgtype = StrFunctionMixin(Cmd.GET, init="RDSINFO") rdstxta = StrFunctionMixin(Cmd.GET, init="RDSINFO") diff --git a/ynca/subunits/usb.py b/ynca/subunits/usb.py index 606196c..233af06 100644 --- a/ynca/subunits/usb.py +++ b/ynca/subunits/usb.py @@ -5,8 +5,10 @@ from . import ( AlbumFunctionMixin, ArtistFunctionMixin, + MemFunctionMixin, PlaybackFunctionMixin, PlaybackInfoFunctionMixin, + PresetFunctionMixin, RepeatFunctionMixin, ShuffleFunctionMixin, SongFunctionMixin, @@ -16,8 +18,10 @@ class Usb( AlbumFunctionMixin, ArtistFunctionMixin, + MemFunctionMixin, PlaybackFunctionMixin, PlaybackInfoFunctionMixin, + PresetFunctionMixin, RepeatFunctionMixin, ShuffleFunctionMixin, SongFunctionMixin,