diff --git a/software/src/app.py b/software/src/app.py index 3b42619..a8f046d 100644 --- a/software/src/app.py +++ b/software/src/app.py @@ -13,12 +13,36 @@ VOLUME_CURVE = [1, 2, 4, 8, 16, 32, 63, 126, 251] class PlayerApp: + class TagStateMachine: + def __init__(self, parent, timer_manager): + self.parent = parent + self.timer_manager = timer_manager + self.current_tag = None + self.current_tag_time = time.ticks_ms() + + def onTagChange(self, new_tag): + if new_tag is not None: + self.timer_manager.cancel(self.onTagRemoveDelay) + if new_tag == self.current_tag: + return + # Change playlist on new tag + if new_tag is not None: + self.current_tag_time = time.ticks_ms() + self.current_tag = new_tag + self.parent.onNewTag(new_tag) + else: + self.timer_manager.schedule(time.ticks_ms() + 5000, self.onTagRemoveDelay) + + def onTagRemoveDelay(self): + if self.current_tag is not None: + self.current_tag = None + self.parent.onTagRemoved() + def __init__(self, deps: Dependencies): - self.current_tag = None - self.current_tag_time = time.ticks_ms() self.timer_manager = TimerManager() + self.tag_state_machine = self.TagStateMachine(self, self.timer_manager) self.player = deps.mp3player(self) - self.nfc = deps.nfcreader(self) + self.nfc = deps.nfcreader(self.tag_state_machine) self.playlist_db = deps.playlistdb(self) self.buttons = deps.buttons(self) if deps.buttons is not None else None self.mp3file = None @@ -30,28 +54,22 @@ class PlayerApp: self.mp3file.close() self.mp3file = None - def onTagChange(self, new_tag): - if new_tag is not None: - self.timer_manager.cancel(self.onTagRemoveDelay) - if new_tag == self.current_tag: - return - # Change playlist on new tag - if new_tag is not None: - self.current_tag_time = time.ticks_ms() - self.current_tag = new_tag - uid_str = b''.join('{:02x}'.format(x).encode() for x in new_tag) - self._set_playlist(uid_str) - else: - self.timer_manager.schedule(time.ticks_ms() + 5000, self.onTagRemoveDelay) + def onNewTag(self, new_tag): + """ + Callback (typically called by TagStateMachine) to signal that a new tag has been presented. + """ + uid_str = b''.join('{:02x}'.format(x).encode() for x in new_tag) + self._set_playlist(uid_str) - def onTagRemoveDelay(self): - if self.current_tag is not None: - print('Tag gone, stopping playback') - self.current_tag = None - if self.playlist is not None: - pos = self.player.stop() - if pos is not None: - self.playlist.setPlaybackOffset(pos) + def onTagRemoved(self): + """ + Callback (typically called by TagStateMachine) to signal that a tag has been removed. + """ + print('Tag gone, stopping playback') + if self.playlist is not None: + pos = self.player.stop() + if pos is not None: + self.playlist.setPlaybackOffset(pos) def onButtonPressed(self, what): assert self.buttons is not None diff --git a/software/tests/test_playerapp.py b/software/tests/test_playerapp.py index ecacf35..b71590a 100644 --- a/software/tests/test_playerapp.py +++ b/software/tests/test_playerapp.py @@ -45,7 +45,10 @@ class FakeTimerManager: class FakeNfcReader: - def __init__(self): pass + tag_callback = None + + def __init__(self, tag_callback=None): + FakeNfcReader.tag_callback = tag_callback class FakeButtons: @@ -100,13 +103,13 @@ def test_load_playlist_on_tag(micropythonify, faketimermanager, monkeypatch): fake_db = FakePlaylistDb() fake_mp3 = FakeMp3Player() deps = app.Dependencies(mp3player=lambda _: fake_mp3, - nfcreader=lambda _: FakeNfcReader(), + nfcreader=lambda x: FakeNfcReader(x), buttons=lambda _: FakeButtons(), playlistdb=lambda _: fake_db) dut = app.PlayerApp(deps) with monkeypatch.context() as m: m.setattr(builtins, 'open', fake_open) - dut.onTagChange([23, 42, 1, 2, 3]) + FakeNfcReader.tag_callback.onTagChange([23, 42, 1, 2, 3]) assert fake_mp3.track is not None assert fake_mp3.track.filename == b'test/path.mp3' assert "r" in fake_mp3.track.mode @@ -117,13 +120,13 @@ def test_playlist_seq(micropythonify, faketimermanager, monkeypatch): fake_db = FakePlaylistDb([b'track1.mp3', b'track2.mp3', b'track3.mp3']) fake_mp3 = FakeMp3Player() deps = app.Dependencies(mp3player=lambda _: fake_mp3, - nfcreader=lambda _: FakeNfcReader(), + nfcreader=lambda x: FakeNfcReader(x), buttons=lambda _: FakeButtons(), playlistdb=lambda _: fake_db) dut = app.PlayerApp(deps) with monkeypatch.context() as m: m.setattr(builtins, 'open', fake_open) - dut.onTagChange([23, 42, 1, 2, 3]) + FakeNfcReader.tag_callback.onTagChange([23, 42, 1, 2, 3]) assert fake_mp3.track is not None assert fake_mp3.track.filename == b'track1.mp3' @@ -150,11 +153,11 @@ def test_playlist_unknown_tag(micropythonify, faketimermanager, monkeypatch): fake_db = FakeNoPlaylistDb() fake_mp3 = FakeMp3Player() deps = app.Dependencies(mp3player=lambda _: fake_mp3, - nfcreader=lambda _: FakeNfcReader(), + nfcreader=lambda x: FakeNfcReader(x), buttons=lambda _: FakeButtons(), playlistdb=lambda _: fake_db) dut = app.PlayerApp(deps) with monkeypatch.context() as m: m.setattr(builtins, 'open', fake_open) - dut.onTagChange([23, 42, 1, 2, 3]) + FakeNfcReader.tag_callback.onTagChange([23, 42, 1, 2, 3]) assert fake_mp3.track is None