From db82c1d8af27f9ab2b8867cc4c4b7bb1f7ce8c4d Mon Sep 17 00:00:00 2001 From: Davis Mosenkovs Date: Mon, 16 Jan 2017 22:58:31 +0200 Subject: [PATCH 01/14] Fix upload to coveralls.io Fix upload to coveralls.io by using older (2.2.1) requests library --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1890198..179d5f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,4 +14,4 @@ script: - "tox -e $TOX_ENV" after_success: - - "if [ $TOX_ENV == 'py27' ]; then pip install coveralls; coveralls; fi" \ No newline at end of file + - "if [ $TOX_ENV == 'py27' ]; then pip install coveralls requests==2.2.1; coveralls; fi" \ No newline at end of file From 86274ee2fe329597c13467ff8e62a356a7290bac Mon Sep 17 00:00:00 2001 From: Davis Mosenkovs Date: Mon, 16 Jan 2017 23:10:42 +0200 Subject: [PATCH 02/14] Cleanup build Remove legacy install_command used for installing pre-release Mopidy --- tox.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/tox.ini b/tox.ini index 3e260be..ac56c04 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,6 @@ deps = nose freezegun mopidy -install_command = pip install --allow-unverified=mopidy --pre {opts} {packages} commands = nosetests -v --with-xunit --xunit-file=xunit-{envname}.xml --with-coverage --cover-package=mopidy_alarmclock [testenv:flake8] From 3ff77426ddabcb2407221b5b40ac4c958439075e Mon Sep 17 00:00:00 2001 From: Davis Mosenkovs Date: Sat, 17 Mar 2018 01:45:37 +0200 Subject: [PATCH 03/14] Improved fallback and added logging Fallback to built-in backup alarm will happen if no playback is started within 30 seconds. Logging has been added for better troubleshooting. --- mopidy_alarmclock/alarm_manager.py | 31 ++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/mopidy_alarmclock/alarm_manager.py b/mopidy_alarmclock/alarm_manager.py index 9b9b9c8..859b7ec 100644 --- a/mopidy_alarmclock/alarm_manager.py +++ b/mopidy_alarmclock/alarm_manager.py @@ -4,6 +4,8 @@ import datetime import os import time +import logging +import mopidy from threading import Timer @@ -23,6 +25,7 @@ class AlarmManager(object): core = None state = states.DISABLED idle_timer = None + logger = logging.getLogger(__name__) def get_core(self, core): self.core = core @@ -80,15 +83,19 @@ def set_alarm(self, clock_datetime, playlist, random_mode, volume, volume_increa self.idle() - def play(self): + def play(self, fallback = False): + self.logger.info("AlarmClock alarm started (fallback %s)", fallback) self.core.playback.stop() self.core.tracklist.clear() try: + if fallback: + raise Exception('Fallback') self.core.tracklist.add(self.get_playlist().tracks) if self.core.tracklist.length.get() < 1: raise Exception('Tracklist empty') - except: + except Exception as e: + self.logger.info("AlarmClock using backup alarm, reason: %s", e) self.core.tracklist.add(None, 0, 'file://' + os.path.join(os.path.dirname(__file__), 'backup-alarm.mp3')) self.core.tracklist.consume = False @@ -100,11 +107,25 @@ def play(self): self.core.playback.next() self.core.playback.mute = False - - self.adjust_volume(self.volume, self.volume_increase_seconds, 0) + self.core.playback.volume = 0 self.core.playback.play() + if not fallback: # do fallback only once + self.logger.info("AlarmClock waiting for playback to start") + time.sleep(0.5) + waited = 0.5 + while waited <= 30 and (self.core.playback.state != mopidy.core.PlaybackState.PLAYING or self.core.playback.time_position < 100): + time.sleep(0.5) + waited += 0.5 + if self.core.playback.state != mopidy.core.PlaybackState.PLAYING or self.core.playback.time_position < 100: + self.logger.info("AlarmClock playback did NOT start after %.1f seconds", waited) + self.play(True) + return + self.logger.info("AlarmClock playback started within %.1f seconds", waited) + + self.adjust_volume(self.volume, self.volume_increase_seconds, 0) + self.reset() self.state = states.DISABLED @@ -126,8 +147,10 @@ def adjust_volume(self, target_volume, increase_duration, step_no): pass if step_no == 0 or not isinstance(current_volume, int) or current_volume == int(round(target_volume * (step_no) / (number_of_steps + 1))): if step_no >= number_of_steps: # this design should prevent floating-point edge-case bugs (in case such bugs could be possible here) + self.logger.info("AlarmClock increasing volume to target volume %d", target_volume) self.core.playback.volume = target_volume else: + self.logger.info("AlarmClock increasing volume to %d", int(round(target_volume * (step_no + 1) / (number_of_steps + 1)))) self.core.playback.volume = int(round(target_volume * (step_no + 1) / (number_of_steps + 1))) t = Timer(increase_duration / number_of_steps, self.adjust_volume, [target_volume, increase_duration, step_no + 1]) t.start() From 5f3d7f322104b01edd0a8d20d333fc38cdb4d550 Mon Sep 17 00:00:00 2001 From: Davis Mosenkovs Date: Sat, 17 Mar 2018 01:49:07 +0200 Subject: [PATCH 04/14] Removed PyPI downloads counter --- README.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.rst b/README.rst index a738d4c..e3b52b0 100644 --- a/README.rst +++ b/README.rst @@ -6,10 +6,6 @@ Mopidy-AlarmClock :target: https://pypi.python.org/pypi/Mopidy-AlarmClock/ :alt: Latest PyPI version -.. image:: https://img.shields.io/pypi/dm/Mopidy-AlarmClock.svg?style=flat - :target: https://pypi.python.org/pypi/Mopidy-AlarmClock/ - :alt: Number of PyPI downloads - .. image:: https://travis-ci.org/DavisNT/mopidy-alarmclock.svg?branch=develop :target: https://travis-ci.org/DavisNT/mopidy-alarmclock :alt: Travis-CI build status From a849f286e96a0275cf6d2534daecb7da67e50083 Mon Sep 17 00:00:00 2001 From: Davis Mosenkovs Date: Sat, 17 Mar 2018 03:09:08 +0200 Subject: [PATCH 05/14] Fix Travis CI build --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index ac56c04..1324e35 100644 --- a/tox.ini +++ b/tox.ini @@ -9,12 +9,14 @@ deps = nose freezegun mopidy + tornado<5.0 commands = nosetests -v --with-xunit --xunit-file=xunit-{envname}.xml --with-coverage --cover-package=mopidy_alarmclock [testenv:flake8] deps = flake8 flake8-import-order + tornado<5.0 commands = flake8 [flake8] From b470c2bd3374bc429228a7068da1fee935b17759 Mon Sep 17 00:00:00 2001 From: Davis Mosenkovs Date: Sat, 17 Mar 2018 03:18:37 +0200 Subject: [PATCH 06/14] Fix flake8 errors --- mopidy_alarmclock/__init__.py | 1 - mopidy_alarmclock/alarm_manager.py | 11 ++++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mopidy_alarmclock/__init__.py b/mopidy_alarmclock/__init__.py index b4242ca..bb4eecd 100644 --- a/mopidy_alarmclock/__init__.py +++ b/mopidy_alarmclock/__init__.py @@ -1,7 +1,6 @@ from __future__ import unicode_literals import os - from http import MessageStore, factory_decorator from alarm_manager import AlarmManager diff --git a/mopidy_alarmclock/alarm_manager.py b/mopidy_alarmclock/alarm_manager.py index 859b7ec..a07b113 100644 --- a/mopidy_alarmclock/alarm_manager.py +++ b/mopidy_alarmclock/alarm_manager.py @@ -2,12 +2,13 @@ from __future__ import unicode_literals import datetime +import logging import os import time -import logging -import mopidy from threading import Timer +import mopidy + # Enum of states class states: @@ -83,7 +84,7 @@ def set_alarm(self, clock_datetime, playlist, random_mode, volume, volume_increa self.idle() - def play(self, fallback = False): + def play(self, fallback=False): self.logger.info("AlarmClock alarm started (fallback %s)", fallback) self.core.playback.stop() self.core.tracklist.clear() @@ -111,7 +112,7 @@ def play(self, fallback = False): self.core.playback.play() - if not fallback: # do fallback only once + if not fallback: # do fallback only once self.logger.info("AlarmClock waiting for playback to start") time.sleep(0.5) waited = 0.5 @@ -143,7 +144,7 @@ def adjust_volume(self, target_volume, increase_duration, step_no): current_volume = None try: current_volume = self.core.playback.volume.get() - except: + except Exception: pass if step_no == 0 or not isinstance(current_volume, int) or current_volume == int(round(target_volume * (step_no) / (number_of_steps + 1))): if step_no >= number_of_steps: # this design should prevent floating-point edge-case bugs (in case such bugs could be possible here) From 588ea0c2b89b7ba0199f93f26dd3e240b65a9599 Mon Sep 17 00:00:00 2001 From: Davis Mosenkovs Date: Sat, 17 Mar 2018 20:00:10 +0200 Subject: [PATCH 07/14] Improved playback detection for fallback * Make playback detection exception-safe. * Count time spent detecting playback against timeout. * Use monotonic for time measurement. * Improve logging. --- mopidy_alarmclock/alarm_manager.py | 19 +++++++++++++------ setup.py | 1 + 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/mopidy_alarmclock/alarm_manager.py b/mopidy_alarmclock/alarm_manager.py index a07b113..25aee20 100644 --- a/mopidy_alarmclock/alarm_manager.py +++ b/mopidy_alarmclock/alarm_manager.py @@ -7,6 +7,7 @@ import time from threading import Timer +import monotonic import mopidy @@ -114,16 +115,22 @@ def play(self, fallback=False): if not fallback: # do fallback only once self.logger.info("AlarmClock waiting for playback to start") - time.sleep(0.5) waited = 0.5 - while waited <= 30 and (self.core.playback.state != mopidy.core.PlaybackState.PLAYING or self.core.playback.time_position < 100): + starttime = 0 + try: + starttime = monotonic.monotonic() time.sleep(0.5) - waited += 0.5 - if self.core.playback.state != mopidy.core.PlaybackState.PLAYING or self.core.playback.time_position < 100: - self.logger.info("AlarmClock playback did NOT start after %.1f seconds", waited) + while self.core.playback.state.get() != mopidy.core.PlaybackState.PLAYING or self.core.playback.time_position.get() < 100: # in some cases this check will cause a notable delay + self.logger.info("AlarmClock has been waiting for %.2f seconds (waited inside AlarmClock %.2f sec)", monotonic.monotonic()-starttime, waited) + if waited > 30 or (waited > 0.5 and monotonic.monotonic()-starttime > 30): # ensure EITHER delay is more than 30 seconds OR at least 2 times above line has been executed + raise Exception("Timeout") + time.sleep(1) + waited += 1 + self.logger.info("AlarmClock playback started within %.2f seconds (waited inside AlarmClock %.2f sec)", monotonic.monotonic()-starttime, waited) + except Exception as e: + self.logger.info("AlarmClock playback FAILED to start (waited inside AlarmClock %.2f sec), reason: %s", waited, e) self.play(True) return - self.logger.info("AlarmClock playback started within %.1f seconds", waited) self.adjust_volume(self.volume, self.volume_increase_seconds, 0) diff --git a/setup.py b/setup.py index c31fb19..a482414 100644 --- a/setup.py +++ b/setup.py @@ -29,6 +29,7 @@ def get_version(filename): 'setuptools', 'Mopidy >= 0.19', 'Pykka >= 1.1', + 'monotonic >= 1.4', ], test_suite='nose.collector', tests_require=[ From d8fb2ee679a08de6de60abc5812ec59319dabdba Mon Sep 17 00:00:00 2001 From: Davis Mosenkovs Date: Sat, 17 Mar 2018 20:04:17 +0200 Subject: [PATCH 08/14] Fix flake8 errors --- mopidy_alarmclock/alarm_manager.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mopidy_alarmclock/alarm_manager.py b/mopidy_alarmclock/alarm_manager.py index 25aee20..562e464 100644 --- a/mopidy_alarmclock/alarm_manager.py +++ b/mopidy_alarmclock/alarm_manager.py @@ -8,6 +8,7 @@ from threading import Timer import monotonic + import mopidy @@ -121,12 +122,12 @@ def play(self, fallback=False): starttime = monotonic.monotonic() time.sleep(0.5) while self.core.playback.state.get() != mopidy.core.PlaybackState.PLAYING or self.core.playback.time_position.get() < 100: # in some cases this check will cause a notable delay - self.logger.info("AlarmClock has been waiting for %.2f seconds (waited inside AlarmClock %.2f sec)", monotonic.monotonic()-starttime, waited) - if waited > 30 or (waited > 0.5 and monotonic.monotonic()-starttime > 30): # ensure EITHER delay is more than 30 seconds OR at least 2 times above line has been executed + self.logger.info("AlarmClock has been waiting for %.2f seconds (waited inside AlarmClock %.2f sec)", monotonic.monotonic() - starttime, waited) + if waited > 30 or (waited > 0.5 and monotonic.monotonic() - starttime > 30): # ensure EITHER delay is more than 30 seconds OR at least 2 times above line has been executed raise Exception("Timeout") time.sleep(1) waited += 1 - self.logger.info("AlarmClock playback started within %.2f seconds (waited inside AlarmClock %.2f sec)", monotonic.monotonic()-starttime, waited) + self.logger.info("AlarmClock playback started within %.2f seconds (waited inside AlarmClock %.2f sec)", monotonic.monotonic() - starttime, waited) except Exception as e: self.logger.info("AlarmClock playback FAILED to start (waited inside AlarmClock %.2f sec), reason: %s", waited, e) self.play(True) From da58049b6d4c3fe0da95354e94fb5ac7d9168e67 Mon Sep 17 00:00:00 2001 From: Davis Mosenkovs Date: Sat, 17 Mar 2018 20:29:43 +0200 Subject: [PATCH 09/14] Fix import PlaybackState --- mopidy_alarmclock/alarm_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mopidy_alarmclock/alarm_manager.py b/mopidy_alarmclock/alarm_manager.py index 562e464..d9d20ef 100644 --- a/mopidy_alarmclock/alarm_manager.py +++ b/mopidy_alarmclock/alarm_manager.py @@ -9,7 +9,7 @@ import monotonic -import mopidy +from mopidy.core import PlaybackState # Enum of states @@ -121,7 +121,7 @@ def play(self, fallback=False): try: starttime = monotonic.monotonic() time.sleep(0.5) - while self.core.playback.state.get() != mopidy.core.PlaybackState.PLAYING or self.core.playback.time_position.get() < 100: # in some cases this check will cause a notable delay + while self.core.playback.state.get() != PlaybackState.PLAYING or self.core.playback.time_position.get() < 100: # in some cases this check will cause a notable delay self.logger.info("AlarmClock has been waiting for %.2f seconds (waited inside AlarmClock %.2f sec)", monotonic.monotonic() - starttime, waited) if waited > 30 or (waited > 0.5 and monotonic.monotonic() - starttime > 30): # ensure EITHER delay is more than 30 seconds OR at least 2 times above line has been executed raise Exception("Timeout") From 44543a5a4e52454bd66abb0e5ec2c59ba17caff2 Mon Sep 17 00:00:00 2001 From: Davis Mosenkovs Date: Sat, 17 Mar 2018 21:31:08 +0200 Subject: [PATCH 10/14] Fix Travis CI environment and tests --- .travis.yml | 5 +++++ tests/test_alarm_manager.py | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 179d5f2..397fc0a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: python +sudo: true python: - "2.7_with_system_site_packages" @@ -7,6 +8,10 @@ env: - TOX_ENV=py27 - TOX_ENV=flake8 +before_install: + - sudo apt-get update + - sudo apt-get install -y python-gi python-gst-1.0 gir1.2-gstreamer-1.0 gir1.2-gst-plugins-base-1.0 gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly gstreamer1.0-tools + install: - "pip install tox" diff --git a/tests/test_alarm_manager.py b/tests/test_alarm_manager.py index a0f6c2f..2fb40a0 100644 --- a/tests/test_alarm_manager.py +++ b/tests/test_alarm_manager.py @@ -8,6 +8,8 @@ import mock +from mopidy.core import PlaybackState + from mopidy_alarmclock.alarm_manager import AlarmManager @@ -98,6 +100,8 @@ def test02_set_alarm__threading(self): def test02_set_alarm__empty_playlist(self): core = mock.Mock() playlist = 'Playlist URI' + core.playback.state.get.side_effect = lambda: PlaybackState.PLAYING + core.playback.time_position.get.side_effect = lambda: 234 core.playlists.lookup('Playlist URI').get().tracks = 'Tracks 811, 821, 823, 827, 829, 839' core.tracklist.length.get.side_effect = lambda: 4 self.assertEqual(core.playlists.lookup.call_count, 1) # First call when setting up the Mock @@ -121,7 +125,7 @@ def test02_set_alarm__empty_playlist(self): # Set alarm to PAST am.set_alarm(datetime.datetime(2000, 4, 28, 7, 59, 15, 324341), playlist, False, 83, 0) - # Ensure that tracks were added + # Ensure that tracks (including backup alarm) were added self.assertEqual(core.playlists.lookup.call_count, 3) self.assertEqual(core.tracklist.add.call_count, 2) core.tracklist.add.assert_any_call('Tracks 811, 821, 823, 827, 829, 839') @@ -142,6 +146,8 @@ def test02_get_ring_time(self): def test03_cancel(self): core = mock.Mock() playlist = 'Playlist URI' + core.playback.state.get.side_effect = lambda: PlaybackState.PLAYING + core.playback.time_position.get.side_effect = lambda: 234 threadcount = threading.active_count() am = AlarmManager() @@ -436,6 +442,8 @@ def test03_adjust_volume__100_30_intervened(self): def test04__integration_1(self): core = mock.Mock() playlist = 'Playlist URI' + core.playback.state.get.side_effect = lambda: PlaybackState.PLAYING + core.playback.time_position.get.side_effect = lambda: 234 core.playlists.lookup('Playlist URI').get().tracks = 'Tracks 811, 821, 823, 827, 829, 839' self.assertEqual(core.playlists.lookup.call_count, 1) # First call when setting up the Mock threadcount = threading.active_count() From be219e8caf6b3de1b50ee3a20ced05f60dce6a26 Mon Sep 17 00:00:00 2001 From: Davis Mosenkovs Date: Sat, 17 Mar 2018 22:28:59 +0200 Subject: [PATCH 11/14] Add unittest for broken playback Added test test02_set_alarm__broken_playback --- tests/test_alarm_manager.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/test_alarm_manager.py b/tests/test_alarm_manager.py index 2fb40a0..4918b5e 100644 --- a/tests/test_alarm_manager.py +++ b/tests/test_alarm_manager.py @@ -132,6 +132,41 @@ def test02_set_alarm__empty_playlist(self): core.tracklist.add.assert_called_with(None, 0, 'file://' + os.path.dirname(os.path.dirname(__file__)) + '/mopidy_alarmclock/backup-alarm.mp3') core.playback.play.assert_called_once_with() + def test02_set_alarm__broken_playback(self): + core = mock.Mock() + playlist = 'Playlist URI' + core.playback.state.get.side_effect = lambda: PlaybackState.PLAYING + core.playback.time_position.get.side_effect = lambda: 234 + core.playlists.lookup('Playlist URI').get().tracks = 'Tracks 811, 821, 823, 827, 829, 839' + core.tracklist.length.get.side_effect = lambda: 4 + self.assertEqual(core.playlists.lookup.call_count, 1) # First call when setting up the Mock + + am = AlarmManager() + am.get_core(core) + + # Set alarm to PAST + am.set_alarm(datetime.datetime(2000, 4, 28, 7, 59, 15, 324341), playlist, False, 83, 0) + + # Ensure that tracks were added + self.assertEqual(core.playlists.lookup.call_count, 2) + core.tracklist.add.assert_called_once_with('Tracks 811, 821, 823, 827, 829, 839') + core.playback.play.assert_called_once_with() + + # Cleanup and re-setup + core.tracklist.add.reset_mock() + core.playback.play.reset_mock() + core.playback.time_position.get.side_effect = lambda: 0 # simulate broken playback (stuck at 0 milliseconds) + + # Set alarm to PAST + am.set_alarm(datetime.datetime(2000, 4, 28, 7, 59, 15, 324341), playlist, False, 83, 0) + + # Ensure that tracks (including backup alarm) were added + self.assertEqual(core.playlists.lookup.call_count, 3) + self.assertEqual(core.tracklist.add.call_count, 2) + core.tracklist.add.assert_any_call('Tracks 811, 821, 823, 827, 829, 839') + core.tracklist.add.assert_called_with(None, 0, 'file://' + os.path.dirname(os.path.dirname(__file__)) + '/mopidy_alarmclock/backup-alarm.mp3') + self.assertEqual(core.playback.play.call_count, 2) + def test02_get_ring_time(self): playlist = 'Playlist URI' From f61cd2894a15abacac58c5e9b75dad920b749de7 Mon Sep 17 00:00:00 2001 From: Davis Mosenkovs Date: Sat, 17 Mar 2018 23:54:37 +0200 Subject: [PATCH 12/14] Improve unittest for broken playback detection Verify number of playback checks and simulate slowness (that should result in monotonic induced timeout after 2 attempts). --- tests/test_alarm_manager.py | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/tests/test_alarm_manager.py b/tests/test_alarm_manager.py index 4918b5e..7ee6bc4 100644 --- a/tests/test_alarm_manager.py +++ b/tests/test_alarm_manager.py @@ -152,21 +152,52 @@ def test02_set_alarm__broken_playback(self): core.tracklist.add.assert_called_once_with('Tracks 811, 821, 823, 827, 829, 839') core.playback.play.assert_called_once_with() - # Cleanup and re-setup + # Cleanup and re-setup (part 2 starts here) core.tracklist.add.reset_mock() core.playback.play.reset_mock() + core.playback.state.get.reset_mock() + core.playback.time_position.get.reset_mock() + core.playback.state.get.side_effect = lambda: PlaybackState.PLAYING core.playback.time_position.get.side_effect = lambda: 0 # simulate broken playback (stuck at 0 milliseconds) # Set alarm to PAST am.set_alarm(datetime.datetime(2000, 4, 28, 7, 59, 15, 324341), playlist, False, 83, 0) - # Ensure that tracks (including backup alarm) were added + # Ensure that tracks (including backup alarm) were added and playback started self.assertEqual(core.playlists.lookup.call_count, 3) self.assertEqual(core.tracklist.add.call_count, 2) core.tracklist.add.assert_any_call('Tracks 811, 821, 823, 827, 829, 839') core.tracklist.add.assert_called_with(None, 0, 'file://' + os.path.dirname(os.path.dirname(__file__)) + '/mopidy_alarmclock/backup-alarm.mp3') self.assertEqual(core.playback.play.call_count, 2) + # Ensure playback was checked around 31 times (tolerate 1 sec possible slowness of build env) + self.assertGreaterEqual(core.playback.state.get.call_count, 30) + self.assertLess(core.playback.state.get.call_count, 32) + self.assertGreaterEqual(core.playback.time_position.get.call_count, 30) + self.assertLess(core.playback.time_position.get.call_count, 32) + + # Cleanup and re-setup (part 3 starts here) + core.tracklist.add.reset_mock() + core.playback.play.reset_mock() + core.playback.state.get.reset_mock() + core.playback.time_position.get.reset_mock() + core.playback.state.get.side_effect = lambda: time.sleep(31) # simulate broken playback (return invalid state after 31 second delay) + core.playback.time_position.get.side_effect = lambda: 234 + + # Set alarm to PAST + am.set_alarm(datetime.datetime(2000, 4, 28, 7, 59, 15, 324341), playlist, False, 83, 0) + + # Ensure that tracks (including backup alarm) were added and playback started + self.assertEqual(core.playlists.lookup.call_count, 4) + self.assertEqual(core.tracklist.add.call_count, 2) + core.tracklist.add.assert_any_call('Tracks 811, 821, 823, 827, 829, 839') + core.tracklist.add.assert_called_with(None, 0, 'file://' + os.path.dirname(os.path.dirname(__file__)) + '/mopidy_alarmclock/backup-alarm.mp3') + self.assertEqual(core.playback.play.call_count, 2) + + # Ensure playback was checked exactly 2 times (due to delay during checking) + self.assertEqual(core.playback.state.get.call_count, 2) + self.assertEqual(core.playback.time_position.get.call_count, 0) # actually this does not get called (because it is 2 operand in or) + def test02_get_ring_time(self): playlist = 'Playlist URI' From 1f79a3e4a1be08a405a510991e52ce2c33b400dd Mon Sep 17 00:00:00 2001 From: Davis Mosenkovs Date: Sun, 18 Mar 2018 01:04:30 +0200 Subject: [PATCH 13/14] Updated readme Added warning about remote media (and license) to readme --- README.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.rst b/README.rst index e3b52b0..01c2462 100644 --- a/README.rst +++ b/README.rst @@ -53,6 +53,30 @@ Usage Make sure that the `HTTP extension `_ is enabled. Then browse to the app on the Mopidy server (for instance, http://localhost:6680/alarmclock/). +**WARNING! It is strongly recommended to use only local playlists with local media (files) for alarm clock.** + +Althrough Mopidy-AlarmClock contains some safety measures against playlist/track inaccessibility (e.g. upon network outage) it is still much safer to use local media. + +License +============= +:: + + Copyright 2014 Mathieu Xhonneux + Copyright 2015-2018 Davis Mosenkovs + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Project resources ================= From c1bba161a7ee377a36ce006ef8e265ccd2bff46a Mon Sep 17 00:00:00 2001 From: Davis Mosenkovs Date: Sun, 18 Mar 2018 01:32:46 +0200 Subject: [PATCH 14/14] Version 0.1.7 --- README.rst | 6 ++++++ mopidy_alarmclock/__init__.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 01c2462..e4aa933 100644 --- a/README.rst +++ b/README.rst @@ -88,6 +88,12 @@ Project resources Changelog ========= +v0.1.7 +---------------------------------------- + +- Play `backup alarm sound `_ when playback cannot be started (within 30 seconds or more). +- Added warning to readme that only local playlists/media should be used for alarm clock. + v0.1.6 ---------------------------------------- diff --git a/mopidy_alarmclock/__init__.py b/mopidy_alarmclock/__init__.py index bb4eecd..bd00fd7 100644 --- a/mopidy_alarmclock/__init__.py +++ b/mopidy_alarmclock/__init__.py @@ -8,7 +8,7 @@ from mopidy import config, ext -__version__ = '0.1.6' +__version__ = '0.1.7' class Extension(ext.Extension):