diff --git a/openlp/core/api/poll.py b/openlp/core/api/poll.py
index b244d86bd..7d405fbea 100644
--- a/openlp/core/api/poll.py
+++ b/openlp/core/api/poll.py
@@ -24,16 +24,19 @@ from openlp.core.common.mixins import RegistryProperties
class Poller(RegistryProperties):
"""
Accessed by the web layer to get status type information from the application
+
+ WARNING:
+ This class is DEPRECATED, if you need the state of the program, use the registry to access variables.
+ Used only for the deprecated V1 HTTP API.
"""
def __init__(self):
"""
Constructor for the poll builder class.
"""
super(Poller, self).__init__()
- self.previous = {}
- def raw_poll(self):
- return {
+ def poll(self):
+ return {'results': {
'counter': self.live_controller.slide_count if self.live_controller.slide_count else 0,
'service': self.service_manager.service_id,
'slide': self.live_controller.selected_row or 0,
@@ -45,21 +48,4 @@ class Poller(RegistryProperties):
'version': 3,
'isSecure': self.settings.value('api/authentication enabled'),
'chordNotation': self.settings.value('songs/chord notation')
- }
-
- def poll(self):
- """
- Poll OpenLP to determine current state if it has changed.
- """
- current = self.raw_poll()
- if self.previous != current:
- self.previous = current
- return {'results': current}
- else:
- return None
-
- def poll_first_time(self):
- """
- Poll OpenLP to determine the current state.
- """
- return {'results': self.raw_poll()}
+ }}
diff --git a/openlp/core/api/versions/v2/core.py b/openlp/core/api/versions/v2/core.py
index 59475b38a..479df2f5f 100644
--- a/openlp/core/api/versions/v2/core.py
+++ b/openlp/core/api/versions/v2/core.py
@@ -30,11 +30,6 @@ core = Blueprint('core', __name__)
log = logging.getLogger(__name__)
-@core.route('/poll')
-def poll():
- return jsonify(Registry().get('poller').poll())
-
-
@core.route('/display', methods=['POST'])
@login_required
def toggle_display():
diff --git a/openlp/core/api/websockets.py b/openlp/core/api/websockets.py
index 8a482563c..da23b2029 100644
--- a/openlp/core/api/websockets.py
+++ b/openlp/core/api/websockets.py
@@ -19,8 +19,8 @@
# along with this program. If not, see . #
##########################################################################
"""
-The :mod:`http` module contains the API web server. This is a lightweight web server used by remotes to interact
-with OpenLP. It uses JSON to communicate with the remotes.
+The :mod:`websockets` module contains the websockets server. This is a server used by remotes to listen for stage
+changes from within OpenLP. It uses JSON to communicate with the remotes.
"""
import asyncio
import json
@@ -32,8 +32,10 @@ from websockets import serve
from openlp.core.common.mixins import LogMixin, RegistryProperties
from openlp.core.common.registry import Registry
from openlp.core.threading import ThreadWorker, run_thread
+from openlp.core.api.websocketspoll import WebSocketPoller
USERS = set()
+poller = WebSocketPoller()
log = logging.getLogger(__name__)
@@ -52,7 +54,7 @@ async def handle_websocket(websocket, path):
"""
log.debug('WebSocket handle_websocket connection')
await register(websocket)
- reply = Registry().get('poller').poll_first_time()
+ reply = poller.get_state()
if reply:
json_reply = json.dumps(reply).encode()
await websocket.send(json_reply)
@@ -93,7 +95,7 @@ async def notify_users():
:return:
"""
if USERS: # asyncio.wait doesn't accept an empty list
- reply = Registry().get('poller').poll()
+ reply = poller.get_state_if_changed()
if reply:
json_reply = json.dumps(reply).encode()
await asyncio.wait([user.send(json_reply) for user in USERS])
diff --git a/openlp/core/api/websocketspoll.py b/openlp/core/api/websocketspoll.py
new file mode 100644
index 000000000..b31bbb6fc
--- /dev/null
+++ b/openlp/core/api/websocketspoll.py
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+
+##########################################################################
+# OpenLP - Open Source Lyrics Projection #
+# ---------------------------------------------------------------------- #
+# Copyright (c) 2008-2020 OpenLP Developers #
+# ---------------------------------------------------------------------- #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see . #
+##########################################################################
+from openlp.core.common.mixins import RegistryProperties
+
+
+class WebSocketPoller(RegistryProperties):
+ """
+ Accessed by web sockets to get status type information from the application
+ """
+ def __init__(self):
+ """
+ Constructor for the web sockets poll builder class.
+ """
+ super(WebSocketPoller, self).__init__()
+ self._previous = {}
+
+ def get_state(self):
+ return {'results': {
+ 'counter': self.live_controller.slide_count if self.live_controller.slide_count else 0,
+ 'service': self.service_manager.service_id,
+ 'slide': self.live_controller.selected_row or 0,
+ 'item': self.live_controller.service_item.unique_identifier if self.live_controller.service_item else '',
+ 'twelve': self.settings.value('api/twelve hour'),
+ 'blank': self.live_controller.blank_screen.isChecked(),
+ 'theme': self.live_controller.theme_screen.isChecked(),
+ 'display': self.live_controller.desktop_screen.isChecked(),
+ 'version': 3,
+ 'isSecure': self.settings.value('api/authentication enabled'),
+ 'chordNotation': self.settings.value('songs/chord notation')
+ }}
+
+ def get_state_if_changed(self):
+ """
+ Poll OpenLP to determine current state if it has changed.
+
+ This must only be used by web sockets or else we could miss a state change.
+
+ :return: The current application state or None if unchanged since last call
+ """
+ current = self.get_state()
+ if self._previous != current:
+ self._previous = current
+ return current
+ else:
+ return None
diff --git a/tests/functional/openlp_core/api/test_websockets.py b/tests/functional/openlp_core/api/test_websockets.py
index 46a30294f..86f43c2a3 100644
--- a/tests/functional/openlp_core/api/test_websockets.py
+++ b/tests/functional/openlp_core/api/test_websockets.py
@@ -24,14 +24,14 @@ Functional tests to test the Http Server Class.
import pytest
from unittest.mock import MagicMock, patch
-from openlp.core.api.poll import Poller
+from openlp.core.api.websocketspoll import WebSocketPoller
from openlp.core.api.websockets import WebSocketServer
from openlp.core.common.registry import Registry
@pytest.fixture
def poller(settings):
- poll = Poller()
+ poll = WebSocketPoller()
yield poll
@@ -67,9 +67,9 @@ def test_serverstart_not_required(mocked_run_thread, MockWebSocketWorker, regist
assert MockWebSocketWorker.call_count == 0, 'The http thread should not have been called'
-def test_poll(poller, settings):
+def test_poller_get_state(poller, settings):
"""
- Test the poll function returns the correct JSON
+ Test the get_state function returns the correct JSON
"""
# GIVEN: the system is configured with a set of data
mocked_service_manager = MagicMock()
@@ -84,7 +84,7 @@ def test_poll(poller, settings):
Registry().register('live_controller', mocked_live_controller)
Registry().register('service_manager', mocked_service_manager)
# WHEN: The poller polls
- poll_json = poller.poll()
+ poll_json = poller.get_state()
# THEN: the live json should be generated and match expected results
assert poll_json['results']['blank'] is True, 'The blank return value should be True'
assert poll_json['results']['theme'] is False, 'The theme return value should be False'
@@ -95,3 +95,28 @@ def test_poll(poller, settings):
assert poll_json['results']['slide'] == 5, 'The slide return value should be 5'
assert poll_json['results']['service'] == 21, 'The version return value should be 21'
assert poll_json['results']['item'] == '23-34-45', 'The item return value should match 23-34-45'
+
+
+def test_poller_get_state_if_changed(poller, settings):
+ """
+ Test the get_state_if_changed function returns None if the state has not changed
+ """
+ # GIVEN: the system is configured with a set of data
+ poller._previous = {}
+ mocked_service_manager = MagicMock()
+ mocked_service_manager.service_id = 21
+ mocked_live_controller = MagicMock()
+ mocked_live_controller.selected_row = 5
+ mocked_live_controller.service_item = MagicMock()
+ mocked_live_controller.service_item.unique_identifier = '23-34-45'
+ mocked_live_controller.blank_screen.isChecked.return_value = True
+ mocked_live_controller.theme_screen.isChecked.return_value = False
+ mocked_live_controller.desktop_screen.isChecked.return_value = False
+ Registry().register('live_controller', mocked_live_controller)
+ Registry().register('service_manager', mocked_service_manager)
+ # WHEN: The poller polls twice
+ poll_json = poller.get_state_if_changed()
+ poll_json2 = poller.get_state_if_changed()
+ # THEN: The get_state_if_changed function should return None on the second call because the state has not changed
+ assert poll_json is not None, 'The first get_state_if_changed function call should have not returned None'
+ assert poll_json2 is None, 'The second get_state_if_changed function should return None'
diff --git a/tests/functional/openlp_core/api/v2/test_core.py b/tests/functional/openlp_core/api/v2/test_core.py
index 4e65fbb12..1b85fb839 100644
--- a/tests/functional/openlp_core/api/v2/test_core.py
+++ b/tests/functional/openlp_core/api/v2/test_core.py
@@ -18,7 +18,10 @@
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see . #
##########################################################################
+from unittest.mock import MagicMock
+
from openlp.core.common.registry import Registry
+from openlp.core.api.poll import Poller
from openlp.core.state import State
from openlp.core.lib.plugin import PluginStatus, StringContent
@@ -53,13 +56,35 @@ def test_system_information(flask_client, settings):
assert not res['login_required']
-def test_poll(flask_client):
- class FakePoller:
- def poll(self):
- return {'foo': 'bar'}
- Registry.create().register('poller', FakePoller())
- res = flask_client.get('/api/v2/core/poll').get_json()
- assert res['foo'] == 'bar'
+def test_poll_backend(settings):
+ """
+ Test the raw poll function returns the correct JSON
+ """
+ # GIVEN: the system is configured with a set of data
+ poller = Poller()
+ mocked_service_manager = MagicMock()
+ mocked_service_manager.service_id = 21
+ mocked_live_controller = MagicMock()
+ mocked_live_controller.selected_row = 5
+ mocked_live_controller.service_item = MagicMock()
+ mocked_live_controller.service_item.unique_identifier = '23-34-45'
+ mocked_live_controller.blank_screen.isChecked.return_value = True
+ mocked_live_controller.theme_screen.isChecked.return_value = False
+ mocked_live_controller.desktop_screen.isChecked.return_value = False
+ Registry().register('live_controller', mocked_live_controller)
+ Registry().register('service_manager', mocked_service_manager)
+ # WHEN: The poller polls
+ poll_json = poller.poll()
+ # THEN: the live json should be generated and match expected results
+ assert poll_json['results']['blank'] is True, 'The blank return value should be True'
+ assert poll_json['results']['theme'] is False, 'The theme return value should be False'
+ assert poll_json['results']['display'] is False, 'The display return value should be False'
+ assert poll_json['results']['isSecure'] is False, 'The isSecure return value should be False'
+ assert poll_json['results']['twelve'] is True, 'The twelve return value should be True'
+ assert poll_json['results']['version'] == 3, 'The version return value should be 3'
+ assert poll_json['results']['slide'] == 5, 'The slide return value should be 5'
+ assert poll_json['results']['service'] == 21, 'The version return value should be 21'
+ assert poll_json['results']['item'] == '23-34-45', 'The item return value should match 23-34-45'
def test_login_get_is_refused(flask_client):