4 Commits

Author SHA1 Message Date
49197c8ca4 Merge pull request 'feat: Move tagmode setting to config.json, remove playlistdb settings' (#57) from unify-config into main
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m38s
Check code formatting / Check-C-Format (push) Successful in 6s
Check code formatting / Check-Python-Flake8 (push) Successful in 9s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 5s
Run unit tests on host / Run-Unit-Tests (push) Successful in 8s
Run pytests / Check-Pytest (push) Successful in 11s
Reviewed-on: #57
Reviewed-by: Stefan Kratochwil <kratochwil-la@gmx.de>
2025-12-16 21:55:28 +00:00
e447902001 feat: Move tagmode setting to config.json, remove playlistdb settings
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m35s
Check code formatting / Check-C-Format (push) Successful in 7s
Check code formatting / Check-Python-Flake8 (push) Successful in 9s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 6s
Run unit tests on host / Run-Unit-Tests (push) Successful in 8s
Run pytests / Check-Pytest (push) Successful in 10s
Signed-off-by: Matthias Blankertz <matthias@blankertz.org>
2025-12-16 20:41:40 +01:00
768b630722 Merge pull request 'misc-stability-improvements' (#56) from misc-stability-improvements into main
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m36s
Check code formatting / Check-C-Format (push) Successful in 7s
Check code formatting / Check-Python-Flake8 (push) Successful in 10s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 5s
Run unit tests on host / Run-Unit-Tests (push) Successful in 7s
Run pytests / Check-Pytest (push) Successful in 10s
Reviewed-on: #56
Reviewed-by: Stefan Kratochwil <kratochwil-la@gmx.de>
2025-12-16 19:25:44 +00:00
e0ff9c54bc refactor: timer: split out two helpers from _timer_worker
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m35s
Check code formatting / Check-C-Format (push) Successful in 8s
Check code formatting / Check-Python-Flake8 (push) Successful in 10s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 5s
Run unit tests on host / Run-Unit-Tests (push) Successful in 8s
Run pytests / Check-Pytest (push) Successful in 11s
Signed-off-by: Matthias Blankertz <matthias@blankertz.org>
2025-12-16 20:19:30 +01:00
5 changed files with 48 additions and 52 deletions

View File

@@ -51,7 +51,7 @@ class PlayerApp:
self.playlist_db = deps.playlistdb(self)
self.hwconfig = deps.hwconfig(self)
self.leds = deps.leds(self)
self.tag_mode = self.playlist_db.getSetting('tagmode')
self.tag_mode = self.config.get_tagmode()
self.playing_tag = None
self.playlist = None
self.buttons = deps.buttons(self) if deps.buttons is not None else None

View File

@@ -21,7 +21,8 @@ class Configuration:
'VOLDOWN': 2,
'PREV': None,
'NEXT': 1,
}
},
'TAGMODE': 'tagremains'
}
def __init__(self, config_path='/config.json'):
@@ -72,6 +73,9 @@ class Configuration:
def get_button_map(self) -> Mapping[str, int | None]:
return self._get('BUTTON_MAP')
def get_tagmode(self) -> str:
return self._get('TAGMODE')
# For the web API
def get_config(self) -> Mapping[str, Any]:
return self.config
@@ -87,5 +91,7 @@ class Configuration:
def set_config(self, config):
self._validate(self.DEFAULT_CONFIG, config)
if 'TAGMODE' in config and config['TAGMODE'] not in ['tagremains', 'tagstartstop']:
raise ValueError("Invalid TAGMODE: Must be 'tagremains' or 'tagstartstop'")
self.config = config
self._save()

View File

@@ -31,9 +31,6 @@ class BTreeDB(IPlaylistDB):
PERSIST_NO = b'no'
PERSIST_TRACK = b'track'
PERSIST_OFFSET = b'offset'
DEFAULT_SETTINGS = {
b'tagmode': b'tagremains'
}
class Playlist(IPlaylist):
def __init__(self, parent: "BTreeDB", tag: bytes, pos: int, persist, shuffle):
@@ -282,11 +279,6 @@ class BTreeDB(IPlaylistDB):
self._savePlaylist(tag, entries, persist, shuffle)
return self.getPlaylistForTag(tag)
def getSetting(self, key: bytes | str) -> str:
if type(key) is str:
key = key.encode()
return self.db.get(b'settings/' + key, self.DEFAULT_SETTINGS[key]).decode()
def validate(self, dump=False):
"""
Validate the structure of the playlist database.
@@ -306,8 +298,7 @@ class BTreeDB(IPlaylistDB):
fail(f'Malformed key {k!r}')
continue
if fields[0] == b'settings':
val = self.db[k].decode()
print(f'Setting {fields[1].decode()} = {val}')
# Legacy, not used any more
continue
if last_tag != fields[0]:
last_tag = fields[0]

View File

@@ -50,29 +50,36 @@ class TimerManager(object):
heapq.heapify(self.timers)
return i
def _next_timeout(self):
if len(self.timers) == 0:
if self.timer_debug:
print("timer: worker: queue empty")
return None
cur_nearest = self.timers[0][0]
next_timeout = cur_nearest - time.ticks_ms()
if self.timer_debug:
if next_timeout > 0:
print(f"timer: worker: next is {self.timers[0]}, sleep {next_timeout} ms")
else:
print(f"timer: worker: {self.timers[0]} elapsed @{cur_nearest}, delay {-next_timeout} ms")
return next_timeout
async def _wait(self, timeout):
try:
await asyncio.wait_for_ms(self.worker_event.wait(), timeout)
if self.timer_debug:
print("timer: worker: event")
# got woken up due to event
self.worker_event.clear()
return True
except asyncio.TimeoutError:
return False
async def _timer_worker(self):
while True:
if len(self.timers) == 0:
# Nothing to do
await self.worker_event.wait()
if self.timer_debug:
print("_timer_worker: event 0")
self.worker_event.clear()
continue
cur_nearest = self.timers[0][0]
wait_time = cur_nearest - time.ticks_ms()
if wait_time > 0:
if self.timer_debug:
print(f"_timer_worker: next is {self.timers[0]}, sleep {wait_time} ms")
try:
await asyncio.wait_for_ms(self.worker_event.wait(), wait_time)
if self.timer_debug:
print("_timer_worker: event 1")
# got woken up due to event
self.worker_event.clear()
continue
except asyncio.TimeoutError:
pass
next_timeout = self._next_timeout()
if next_timeout is None or next_timeout > 0:
await self._wait(next_timeout)
else:
_, callback = heapq.heappop(self.timers)
safe_callback(callback, "timer callback")

View File

@@ -136,6 +136,9 @@ class FakeConfig:
def get_tag_timeout(self):
return 5
def get_tagmode(self):
return 'tagremains'
def fake_open(filename, mode):
return FakeFile(filename, mode)
@@ -226,18 +229,16 @@ def test_playlist_unknown_tag(micropythonify, faketimermanager, monkeypatch):
def test_tagmode_startstop(micropythonify, faketimermanager, monkeypatch):
class MyFakePlaylistDb(FakePlaylistDb):
def __init__(self, tracklist=[b'test/path.mp3']):
super().__init__(tracklist)
class FakeStartStopConfig(FakeConfig):
def __init__(self):
super().__init__()
def getSetting(self, key: bytes | str):
if key == 'tagmode':
return 'tagstartstop'
return None
def get_tagmode(self):
return 'tagstartstop'
fake_db = MyFakePlaylistDb()
fake_db = FakePlaylistDb([b'test/path.mp3'])
fake_mp3 = FakeMp3Player()
deps = _makedeps(mp3player=fake_mp3, playlistdb=fake_db)
deps = _makedeps(mp3player=fake_mp3, playlistdb=fake_db, config=FakeStartStopConfig)
app.PlayerApp(deps)
with monkeypatch.context() as m:
m.setattr(builtins, 'open', fake_open)
@@ -264,16 +265,7 @@ def test_tagmode_startstop(micropythonify, faketimermanager, monkeypatch):
def test_tagmode_remains(micropythonify, faketimermanager, monkeypatch):
class MyFakePlaylistDb(FakePlaylistDb):
def __init__(self, tracklist=[b'test/path.mp3']):
super().__init__(tracklist)
def getSetting(self, key: bytes | str):
if key == 'tagmode':
return 'tagremains'
return None
fake_db = MyFakePlaylistDb()
fake_db = FakePlaylistDb([b'test/path.mp3'])
fake_mp3 = FakeMp3Player()
deps = _makedeps(mp3player=fake_mp3, playlistdb=fake_db)
app.PlayerApp(deps)